From cad810f21b803229eb11403f9209855525a25d57 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Fri, 6 May 2011 11:45:16 +0100 Subject: Merge WebKit at r75315: Initial merge by git. Change-Id: I570314b346ce101c935ed22a626b48c2af266b84 --- Source/WebCore/rendering/AutoTableLayout.cpp | 750 +++ Source/WebCore/rendering/AutoTableLayout.h | 78 + Source/WebCore/rendering/BidiRun.cpp | 74 + Source/WebCore/rendering/BidiRun.h | 67 + Source/WebCore/rendering/ColumnInfo.h | 102 + Source/WebCore/rendering/CounterNode.cpp | 272 + Source/WebCore/rendering/CounterNode.h | 100 + Source/WebCore/rendering/EllipsisBox.cpp | 126 + Source/WebCore/rendering/EllipsisBox.h | 60 + Source/WebCore/rendering/FixedTableLayout.cpp | 330 ++ Source/WebCore/rendering/FixedTableLayout.h | 47 + Source/WebCore/rendering/GapRects.h | 62 + Source/WebCore/rendering/HitTestRequest.h | 58 + Source/WebCore/rendering/HitTestResult.cpp | 579 ++ Source/WebCore/rendering/HitTestResult.h | 157 + Source/WebCore/rendering/InlineBox.cpp | 321 + Source/WebCore/rendering/InlineBox.h | 379 ++ Source/WebCore/rendering/InlineFlowBox.cpp | 1358 +++++ Source/WebCore/rendering/InlineFlowBox.h | 272 + Source/WebCore/rendering/InlineIterator.h | 266 + Source/WebCore/rendering/InlineTextBox.cpp | 1292 +++++ Source/WebCore/rendering/InlineTextBox.h | 178 + Source/WebCore/rendering/LayoutState.cpp | 177 + Source/WebCore/rendering/LayoutState.h | 104 + Source/WebCore/rendering/MediaControlElements.cpp | 876 +++ Source/WebCore/rendering/MediaControlElements.h | 357 ++ .../WebCore/rendering/OverlapTestRequestClient.h | 39 + Source/WebCore/rendering/PaintInfo.h | 111 + Source/WebCore/rendering/PaintPhase.h | 66 + Source/WebCore/rendering/PointerEventsHitRules.cpp | 111 + Source/WebCore/rendering/PointerEventsHitRules.h | 49 + Source/WebCore/rendering/RenderApplet.cpp | 110 + Source/WebCore/rendering/RenderApplet.h | 69 + Source/WebCore/rendering/RenderArena.cpp | 146 + Source/WebCore/rendering/RenderArena.h | 69 + Source/WebCore/rendering/RenderBR.cpp | 81 + Source/WebCore/rendering/RenderBR.h | 82 + Source/WebCore/rendering/RenderBlock.cpp | 6111 ++++++++++++++++++++ Source/WebCore/rendering/RenderBlock.h | 739 +++ Source/WebCore/rendering/RenderBlockLineLayout.cpp | 2244 +++++++ Source/WebCore/rendering/RenderBox.cpp | 3428 +++++++++++ Source/WebCore/rendering/RenderBox.h | 523 ++ Source/WebCore/rendering/RenderBoxModelObject.cpp | 1858 ++++++ Source/WebCore/rendering/RenderBoxModelObject.h | 167 + Source/WebCore/rendering/RenderButton.cpp | 197 + Source/WebCore/rendering/RenderButton.h | 95 + Source/WebCore/rendering/RenderCounter.cpp | 496 ++ Source/WebCore/rendering/RenderCounter.h | 70 + Source/WebCore/rendering/RenderDataGrid.cpp | 261 + Source/WebCore/rendering/RenderDataGrid.h | 88 + Source/WebCore/rendering/RenderDetails.cpp | 40 + Source/WebCore/rendering/RenderDetails.h | 49 + Source/WebCore/rendering/RenderDetailsMarker.cpp | 31 + Source/WebCore/rendering/RenderDetailsMarker.h | 49 + Source/WebCore/rendering/RenderEmbeddedObject.cpp | 309 + Source/WebCore/rendering/RenderEmbeddedObject.h | 91 + Source/WebCore/rendering/RenderFieldset.cpp | 223 + Source/WebCore/rendering/RenderFieldset.h | 64 + .../WebCore/rendering/RenderFileUploadControl.cpp | 321 + Source/WebCore/rendering/RenderFileUploadControl.h | 96 + Source/WebCore/rendering/RenderFlexibleBox.cpp | 1108 ++++ Source/WebCore/rendering/RenderFlexibleBox.h | 71 + Source/WebCore/rendering/RenderForeignObject.cpp | 174 + Source/WebCore/rendering/RenderForeignObject.h | 76 + Source/WebCore/rendering/RenderFrame.cpp | 91 + Source/WebCore/rendering/RenderFrame.h | 60 + Source/WebCore/rendering/RenderFrameBase.cpp | 93 + Source/WebCore/rendering/RenderFrameBase.h | 44 + Source/WebCore/rendering/RenderFrameSet.cpp | 890 +++ Source/WebCore/rendering/RenderFrameSet.h | 148 + Source/WebCore/rendering/RenderFullScreen.cpp | 70 + Source/WebCore/rendering/RenderFullScreen.h | 61 + Source/WebCore/rendering/RenderHTMLCanvas.cpp | 89 + Source/WebCore/rendering/RenderHTMLCanvas.h | 61 + Source/WebCore/rendering/RenderIFrame.cpp | 159 + Source/WebCore/rendering/RenderIFrame.h | 76 + Source/WebCore/rendering/RenderImage.cpp | 572 ++ Source/WebCore/rendering/RenderImage.h | 119 + Source/WebCore/rendering/RenderImageResource.cpp | 106 + Source/WebCore/rendering/RenderImageResource.h | 79 + .../rendering/RenderImageResourceStyleImage.cpp | 63 + .../rendering/RenderImageResourceStyleImage.h | 68 + Source/WebCore/rendering/RenderIndicator.cpp | 63 + Source/WebCore/rendering/RenderIndicator.h | 48 + Source/WebCore/rendering/RenderInline.cpp | 1155 ++++ Source/WebCore/rendering/RenderInline.h | 177 + Source/WebCore/rendering/RenderInputSpeech.cpp | 99 + Source/WebCore/rendering/RenderInputSpeech.h | 49 + Source/WebCore/rendering/RenderLayer.cpp | 4058 +++++++++++++ Source/WebCore/rendering/RenderLayer.h | 771 +++ Source/WebCore/rendering/RenderLayerBacking.cpp | 1366 +++++ Source/WebCore/rendering/RenderLayerBacking.h | 208 + Source/WebCore/rendering/RenderLayerCompositor.cpp | 1668 ++++++ Source/WebCore/rendering/RenderLayerCompositor.h | 284 + Source/WebCore/rendering/RenderLineBoxList.cpp | 389 ++ Source/WebCore/rendering/RenderLineBoxList.h | 90 + Source/WebCore/rendering/RenderListBox.cpp | 743 +++ Source/WebCore/rendering/RenderListBox.h | 146 + Source/WebCore/rendering/RenderListItem.cpp | 459 ++ Source/WebCore/rendering/RenderListItem.h | 98 + Source/WebCore/rendering/RenderListMarker.cpp | 1720 ++++++ Source/WebCore/rendering/RenderListMarker.h | 101 + Source/WebCore/rendering/RenderMarquee.cpp | 319 + Source/WebCore/rendering/RenderMarquee.h | 99 + Source/WebCore/rendering/RenderMedia.cpp | 650 +++ Source/WebCore/rendering/RenderMedia.h | 169 + Source/WebCore/rendering/RenderMediaControls.cpp | 187 + Source/WebCore/rendering/RenderMediaControls.h | 48 + .../rendering/RenderMediaControlsChromium.cpp | 324 ++ .../rendering/RenderMediaControlsChromium.h | 46 + Source/WebCore/rendering/RenderMenuList.cpp | 562 ++ Source/WebCore/rendering/RenderMenuList.h | 150 + Source/WebCore/rendering/RenderMeter.cpp | 261 + Source/WebCore/rendering/RenderMeter.h | 80 + Source/WebCore/rendering/RenderObject.cpp | 2776 +++++++++ Source/WebCore/rendering/RenderObject.h | 1050 ++++ Source/WebCore/rendering/RenderObjectChildList.cpp | 472 ++ Source/WebCore/rendering/RenderObjectChildList.h | 68 + Source/WebCore/rendering/RenderOverflow.h | 163 + Source/WebCore/rendering/RenderPart.cpp | 61 + Source/WebCore/rendering/RenderPart.h | 55 + Source/WebCore/rendering/RenderProgress.cpp | 149 + Source/WebCore/rendering/RenderProgress.h | 83 + Source/WebCore/rendering/RenderReplaced.cpp | 412 ++ Source/WebCore/rendering/RenderReplaced.h | 85 + Source/WebCore/rendering/RenderReplica.cpp | 82 + Source/WebCore/rendering/RenderReplica.h | 57 + Source/WebCore/rendering/RenderRuby.cpp | 204 + Source/WebCore/rendering/RenderRuby.h | 87 + Source/WebCore/rendering/RenderRubyBase.cpp | 185 + Source/WebCore/rendering/RenderRubyBase.h | 63 + Source/WebCore/rendering/RenderRubyRun.cpp | 284 + Source/WebCore/rendering/RenderRubyRun.h | 84 + Source/WebCore/rendering/RenderRubyText.cpp | 51 + Source/WebCore/rendering/RenderRubyText.h | 52 + Source/WebCore/rendering/RenderSVGAllInOne.cpp | 76 + Source/WebCore/rendering/RenderSVGBlock.cpp | 105 + Source/WebCore/rendering/RenderSVGBlock.h | 50 + Source/WebCore/rendering/RenderSVGContainer.cpp | 184 + Source/WebCore/rendering/RenderSVGContainer.h | 101 + Source/WebCore/rendering/RenderSVGGradientStop.cpp | 83 + Source/WebCore/rendering/RenderSVGGradientStop.h | 68 + .../WebCore/rendering/RenderSVGHiddenContainer.cpp | 62 + .../WebCore/rendering/RenderSVGHiddenContainer.h | 58 + Source/WebCore/rendering/RenderSVGImage.cpp | 195 + Source/WebCore/rendering/RenderSVGImage.h | 99 + Source/WebCore/rendering/RenderSVGModelObject.cpp | 117 + Source/WebCore/rendering/RenderSVGModelObject.h | 76 + Source/WebCore/rendering/RenderSVGResource.cpp | 162 + Source/WebCore/rendering/RenderSVGResource.h | 90 + .../WebCore/rendering/RenderSVGResourceClipper.cpp | 350 ++ .../WebCore/rendering/RenderSVGResourceClipper.h | 79 + .../rendering/RenderSVGResourceContainer.cpp | 193 + .../WebCore/rendering/RenderSVGResourceContainer.h | 95 + .../WebCore/rendering/RenderSVGResourceFilter.cpp | 327 ++ Source/WebCore/rendering/RenderSVGResourceFilter.h | 94 + .../rendering/RenderSVGResourceFilterPrimitive.cpp | 105 + .../rendering/RenderSVGResourceFilterPrimitive.h | 58 + .../rendering/RenderSVGResourceGradient.cpp | 282 + .../WebCore/rendering/RenderSVGResourceGradient.h | 78 + .../rendering/RenderSVGResourceLinearGradient.cpp | 67 + .../rendering/RenderSVGResourceLinearGradient.h | 55 + .../WebCore/rendering/RenderSVGResourceMarker.cpp | 177 + Source/WebCore/rendering/RenderSVGResourceMarker.h | 83 + .../WebCore/rendering/RenderSVGResourceMasker.cpp | 224 + Source/WebCore/rendering/RenderSVGResourceMasker.h | 71 + .../WebCore/rendering/RenderSVGResourcePattern.cpp | 293 + .../WebCore/rendering/RenderSVGResourcePattern.h | 76 + .../rendering/RenderSVGResourceRadialGradient.cpp | 72 + .../rendering/RenderSVGResourceRadialGradient.h | 55 + .../rendering/RenderSVGResourceSolidColor.cpp | 93 + .../rendering/RenderSVGResourceSolidColor.h | 56 + Source/WebCore/rendering/RenderSVGRoot.cpp | 361 ++ Source/WebCore/rendering/RenderSVGRoot.h | 120 + .../rendering/RenderSVGShadowTreeRootContainer.cpp | 108 + .../rendering/RenderSVGShadowTreeRootContainer.h | 54 + .../rendering/RenderSVGTransformableContainer.cpp | 66 + .../rendering/RenderSVGTransformableContainer.h | 47 + .../rendering/RenderSVGViewportContainer.cpp | 94 + .../WebCore/rendering/RenderSVGViewportContainer.h | 66 + Source/WebCore/rendering/RenderScrollbar.cpp | 366 ++ Source/WebCore/rendering/RenderScrollbar.h | 97 + Source/WebCore/rendering/RenderScrollbarPart.cpp | 183 + Source/WebCore/rendering/RenderScrollbarPart.h | 68 + Source/WebCore/rendering/RenderScrollbarTheme.cpp | 140 + Source/WebCore/rendering/RenderScrollbarTheme.h | 82 + Source/WebCore/rendering/RenderSelectionInfo.h | 104 + Source/WebCore/rendering/RenderSlider.cpp | 337 ++ Source/WebCore/rendering/RenderSlider.h | 83 + Source/WebCore/rendering/RenderSummary.cpp | 31 + Source/WebCore/rendering/RenderSummary.h | 49 + Source/WebCore/rendering/RenderTable.cpp | 1249 ++++ Source/WebCore/rendering/RenderTable.h | 287 + Source/WebCore/rendering/RenderTableCell.cpp | 1068 ++++ Source/WebCore/rendering/RenderTableCell.h | 177 + Source/WebCore/rendering/RenderTableCol.cpp | 107 + Source/WebCore/rendering/RenderTableCol.h | 85 + Source/WebCore/rendering/RenderTableRow.cpp | 217 + Source/WebCore/rendering/RenderTableRow.h | 86 + Source/WebCore/rendering/RenderTableSection.cpp | 1320 +++++ Source/WebCore/rendering/RenderTableSection.h | 189 + Source/WebCore/rendering/RenderText.cpp | 1539 +++++ Source/WebCore/rendering/RenderText.h | 206 + Source/WebCore/rendering/RenderTextControl.cpp | 629 ++ Source/WebCore/rendering/RenderTextControl.h | 143 + .../rendering/RenderTextControlMultiLine.cpp | 163 + .../WebCore/rendering/RenderTextControlMultiLine.h | 69 + .../rendering/RenderTextControlSingleLine.cpp | 1151 ++++ .../rendering/RenderTextControlSingleLine.h | 183 + Source/WebCore/rendering/RenderTextFragment.cpp | 117 + Source/WebCore/rendering/RenderTextFragment.h | 84 + Source/WebCore/rendering/RenderTheme.cpp | 1134 ++++ Source/WebCore/rendering/RenderTheme.h | 334 ++ .../WebCore/rendering/RenderThemeChromiumLinux.cpp | 195 + .../WebCore/rendering/RenderThemeChromiumLinux.h | 87 + Source/WebCore/rendering/RenderThemeChromiumMac.h | 60 + Source/WebCore/rendering/RenderThemeChromiumMac.mm | 163 + .../WebCore/rendering/RenderThemeChromiumSkia.cpp | 888 +++ Source/WebCore/rendering/RenderThemeChromiumSkia.h | 170 + .../WebCore/rendering/RenderThemeChromiumWin.cpp | 770 +++ Source/WebCore/rendering/RenderThemeChromiumWin.h | 123 + Source/WebCore/rendering/RenderThemeMac.h | 237 + Source/WebCore/rendering/RenderThemeMac.mm | 2064 +++++++ Source/WebCore/rendering/RenderThemeSafari.cpp | 1215 ++++ Source/WebCore/rendering/RenderThemeSafari.h | 186 + Source/WebCore/rendering/RenderThemeWin.cpp | 1118 ++++ Source/WebCore/rendering/RenderThemeWin.h | 183 + Source/WebCore/rendering/RenderThemeWinCE.cpp | 647 +++ Source/WebCore/rendering/RenderThemeWinCE.h | 142 + Source/WebCore/rendering/RenderTreeAsText.cpp | 796 +++ Source/WebCore/rendering/RenderTreeAsText.h | 102 + Source/WebCore/rendering/RenderVideo.cpp | 284 + Source/WebCore/rendering/RenderVideo.h | 94 + Source/WebCore/rendering/RenderView.cpp | 802 +++ Source/WebCore/rendering/RenderView.h | 334 ++ Source/WebCore/rendering/RenderWidget.cpp | 409 ++ Source/WebCore/rendering/RenderWidget.h | 99 + Source/WebCore/rendering/RenderWidgetProtector.h | 53 + Source/WebCore/rendering/RenderWordBreak.cpp | 49 + Source/WebCore/rendering/RenderWordBreak.h | 46 + Source/WebCore/rendering/RenderingAllInOne.cpp | 115 + Source/WebCore/rendering/RootInlineBox.cpp | 557 ++ Source/WebCore/rendering/RootInlineBox.h | 180 + Source/WebCore/rendering/SVGImageBufferTools.cpp | 128 + Source/WebCore/rendering/SVGImageBufferTools.h | 53 + Source/WebCore/rendering/SVGMarkerData.h | 134 + Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp | 123 + Source/WebCore/rendering/SVGMarkerLayoutInfo.h | 74 + Source/WebCore/rendering/SVGRenderSupport.cpp | 352 ++ Source/WebCore/rendering/SVGRenderSupport.h | 84 + Source/WebCore/rendering/SVGRenderTreeAsText.cpp | 748 +++ Source/WebCore/rendering/SVGRenderTreeAsText.h | 95 + Source/WebCore/rendering/SVGResources.cpp | 684 +++ Source/WebCore/rendering/SVGResources.h | 179 + Source/WebCore/rendering/SVGResourcesCache.cpp | 165 + Source/WebCore/rendering/SVGResourcesCache.h | 65 + .../WebCore/rendering/SVGResourcesCycleSolver.cpp | 213 + Source/WebCore/rendering/SVGResourcesCycleSolver.h | 51 + Source/WebCore/rendering/SVGShadowTreeElements.cpp | 90 + Source/WebCore/rendering/SVGShadowTreeElements.h | 67 + Source/WebCore/rendering/ScrollBehavior.cpp | 55 + Source/WebCore/rendering/ScrollBehavior.h | 78 + Source/WebCore/rendering/ShadowElement.cpp | 121 + Source/WebCore/rendering/ShadowElement.h | 86 + Source/WebCore/rendering/TableLayout.h | 48 + .../WebCore/rendering/TextControlInnerElements.cpp | 505 ++ .../WebCore/rendering/TextControlInnerElements.h | 159 + .../rendering/TrailingFloatsRootInlineBox.h | 47 + Source/WebCore/rendering/TransformState.cpp | 175 + Source/WebCore/rendering/TransformState.h | 135 + Source/WebCore/rendering/VerticalPositionCache.h | 70 + Source/WebCore/rendering/break_lines.cpp | 201 + Source/WebCore/rendering/break_lines.h | 39 + Source/WebCore/rendering/style/BorderData.h | 129 + Source/WebCore/rendering/style/BorderValue.h | 79 + .../WebCore/rendering/style/CollapsedBorderValue.h | 72 + Source/WebCore/rendering/style/ContentData.cpp | 79 + Source/WebCore/rendering/style/ContentData.h | 112 + Source/WebCore/rendering/style/CounterContent.h | 62 + .../WebCore/rendering/style/CounterDirectives.cpp | 38 + Source/WebCore/rendering/style/CounterDirectives.h | 54 + Source/WebCore/rendering/style/CursorData.h | 63 + Source/WebCore/rendering/style/CursorList.h | 62 + Source/WebCore/rendering/style/DataRef.h | 71 + Source/WebCore/rendering/style/FillLayer.cpp | 284 + Source/WebCore/rendering/style/FillLayer.h | 201 + Source/WebCore/rendering/style/KeyframeList.cpp | 97 + Source/WebCore/rendering/style/KeyframeList.h | 99 + Source/WebCore/rendering/style/LineClampValue.h | 69 + Source/WebCore/rendering/style/NinePieceImage.cpp | 35 + Source/WebCore/rendering/style/NinePieceImage.h | 78 + Source/WebCore/rendering/style/OutlineValue.h | 61 + Source/WebCore/rendering/style/RenderStyle.cpp | 1473 +++++ Source/WebCore/rendering/style/RenderStyle.h | 1409 +++++ .../WebCore/rendering/style/RenderStyleConstants.h | 434 ++ Source/WebCore/rendering/style/SVGRenderStyle.cpp | 220 + Source/WebCore/rendering/style/SVGRenderStyle.h | 431 ++ .../WebCore/rendering/style/SVGRenderStyleDefs.cpp | 227 + .../WebCore/rendering/style/SVGRenderStyleDefs.h | 266 + Source/WebCore/rendering/style/ShadowData.cpp | 100 + Source/WebCore/rendering/style/ShadowData.h | 101 + Source/WebCore/rendering/style/StyleAllInOne.cpp | 49 + .../rendering/style/StyleBackgroundData.cpp | 49 + .../WebCore/rendering/style/StyleBackgroundData.h | 65 + Source/WebCore/rendering/style/StyleBoxData.cpp | 68 + Source/WebCore/rendering/style/StyleBoxData.h | 86 + .../WebCore/rendering/style/StyleCachedImage.cpp | 92 + Source/WebCore/rendering/style/StyleCachedImage.h | 67 + .../WebCore/rendering/style/StyleDashboardRegion.h | 61 + .../rendering/style/StyleFlexibleBoxData.cpp | 59 + .../WebCore/rendering/style/StyleFlexibleBoxData.h | 60 + .../rendering/style/StyleGeneratedImage.cpp | 80 + .../WebCore/rendering/style/StyleGeneratedImage.h | 69 + Source/WebCore/rendering/style/StyleImage.h | 84 + .../WebCore/rendering/style/StyleInheritedData.cpp | 65 + .../WebCore/rendering/style/StyleInheritedData.h | 69 + .../WebCore/rendering/style/StyleMarqueeData.cpp | 54 + Source/WebCore/rendering/style/StyleMarqueeData.h | 61 + .../WebCore/rendering/style/StyleMultiColData.cpp | 67 + Source/WebCore/rendering/style/StyleMultiColData.h | 76 + Source/WebCore/rendering/style/StylePendingImage.h | 71 + .../rendering/style/StyleRareInheritedData.cpp | 187 + .../rendering/style/StyleRareInheritedData.h | 114 + .../rendering/style/StyleRareNonInheritedData.cpp | 203 + .../rendering/style/StyleRareNonInheritedData.h | 140 + Source/WebCore/rendering/style/StyleReflection.h | 70 + .../WebCore/rendering/style/StyleSurroundData.cpp | 47 + Source/WebCore/rendering/style/StyleSurroundData.h | 58 + .../WebCore/rendering/style/StyleTransformData.cpp | 51 + .../WebCore/rendering/style/StyleTransformData.h | 58 + Source/WebCore/rendering/style/StyleVisualData.cpp | 49 + Source/WebCore/rendering/style/StyleVisualData.h | 62 + Source/WebCore/rendering/svg/RenderSVGInline.cpp | 124 + Source/WebCore/rendering/svg/RenderSVGInline.h | 68 + .../WebCore/rendering/svg/RenderSVGInlineText.cpp | 212 + Source/WebCore/rendering/svg/RenderSVGInlineText.h | 84 + Source/WebCore/rendering/svg/RenderSVGPath.cpp | 338 ++ Source/WebCore/rendering/svg/RenderSVGPath.h | 105 + Source/WebCore/rendering/svg/RenderSVGTSpan.cpp | 38 + Source/WebCore/rendering/svg/RenderSVGTSpan.h | 38 + Source/WebCore/rendering/svg/RenderSVGText.cpp | 260 + Source/WebCore/rendering/svg/RenderSVGText.h | 98 + Source/WebCore/rendering/svg/RenderSVGTextPath.cpp | 85 + Source/WebCore/rendering/svg/RenderSVGTextPath.h | 66 + Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp | 144 + Source/WebCore/rendering/svg/SVGInlineFlowBox.h | 61 + Source/WebCore/rendering/svg/SVGInlineTextBox.cpp | 608 ++ Source/WebCore/rendering/svg/SVGInlineTextBox.h | 92 + Source/WebCore/rendering/svg/SVGRootInlineBox.cpp | 222 + Source/WebCore/rendering/svg/SVGRootInlineBox.h | 71 + Source/WebCore/rendering/svg/SVGTextChunk.cpp | 93 + Source/WebCore/rendering/svg/SVGTextChunk.h | 68 + .../WebCore/rendering/svg/SVGTextChunkBuilder.cpp | 232 + Source/WebCore/rendering/svg/SVGTextChunkBuilder.h | 64 + Source/WebCore/rendering/svg/SVGTextFragment.h | 57 + .../rendering/svg/SVGTextLayoutAttributes.cpp | 100 + .../rendering/svg/SVGTextLayoutAttributes.h | 69 + .../svg/SVGTextLayoutAttributesBuilder.cpp | 308 + .../rendering/svg/SVGTextLayoutAttributesBuilder.h | 83 + .../WebCore/rendering/svg/SVGTextLayoutEngine.cpp | 579 ++ Source/WebCore/rendering/svg/SVGTextLayoutEngine.h | 94 + .../rendering/svg/SVGTextLayoutEngineBaseline.cpp | 234 + .../rendering/svg/SVGTextLayoutEngineBaseline.h | 54 + .../rendering/svg/SVGTextLayoutEngineSpacing.cpp | 96 + .../rendering/svg/SVGTextLayoutEngineSpacing.h | 52 + Source/WebCore/rendering/svg/SVGTextMetrics.cpp | 115 + Source/WebCore/rendering/svg/SVGTextMetrics.h | 78 + Source/WebCore/rendering/svg/SVGTextQuery.cpp | 562 ++ Source/WebCore/rendering/svg/SVGTextQuery.h | 75 + 369 files changed, 103531 insertions(+) create mode 100644 Source/WebCore/rendering/AutoTableLayout.cpp create mode 100644 Source/WebCore/rendering/AutoTableLayout.h create mode 100644 Source/WebCore/rendering/BidiRun.cpp create mode 100644 Source/WebCore/rendering/BidiRun.h create mode 100644 Source/WebCore/rendering/ColumnInfo.h create mode 100644 Source/WebCore/rendering/CounterNode.cpp create mode 100644 Source/WebCore/rendering/CounterNode.h create mode 100644 Source/WebCore/rendering/EllipsisBox.cpp create mode 100644 Source/WebCore/rendering/EllipsisBox.h create mode 100644 Source/WebCore/rendering/FixedTableLayout.cpp create mode 100644 Source/WebCore/rendering/FixedTableLayout.h create mode 100644 Source/WebCore/rendering/GapRects.h create mode 100644 Source/WebCore/rendering/HitTestRequest.h create mode 100644 Source/WebCore/rendering/HitTestResult.cpp create mode 100644 Source/WebCore/rendering/HitTestResult.h create mode 100644 Source/WebCore/rendering/InlineBox.cpp create mode 100644 Source/WebCore/rendering/InlineBox.h create mode 100644 Source/WebCore/rendering/InlineFlowBox.cpp create mode 100644 Source/WebCore/rendering/InlineFlowBox.h create mode 100644 Source/WebCore/rendering/InlineIterator.h create mode 100644 Source/WebCore/rendering/InlineTextBox.cpp create mode 100644 Source/WebCore/rendering/InlineTextBox.h create mode 100644 Source/WebCore/rendering/LayoutState.cpp create mode 100644 Source/WebCore/rendering/LayoutState.h create mode 100644 Source/WebCore/rendering/MediaControlElements.cpp create mode 100644 Source/WebCore/rendering/MediaControlElements.h create mode 100644 Source/WebCore/rendering/OverlapTestRequestClient.h create mode 100644 Source/WebCore/rendering/PaintInfo.h create mode 100644 Source/WebCore/rendering/PaintPhase.h create mode 100644 Source/WebCore/rendering/PointerEventsHitRules.cpp create mode 100644 Source/WebCore/rendering/PointerEventsHitRules.h create mode 100644 Source/WebCore/rendering/RenderApplet.cpp create mode 100644 Source/WebCore/rendering/RenderApplet.h create mode 100644 Source/WebCore/rendering/RenderArena.cpp create mode 100644 Source/WebCore/rendering/RenderArena.h create mode 100644 Source/WebCore/rendering/RenderBR.cpp create mode 100644 Source/WebCore/rendering/RenderBR.h create mode 100644 Source/WebCore/rendering/RenderBlock.cpp create mode 100644 Source/WebCore/rendering/RenderBlock.h create mode 100644 Source/WebCore/rendering/RenderBlockLineLayout.cpp create mode 100644 Source/WebCore/rendering/RenderBox.cpp create mode 100644 Source/WebCore/rendering/RenderBox.h create mode 100644 Source/WebCore/rendering/RenderBoxModelObject.cpp create mode 100644 Source/WebCore/rendering/RenderBoxModelObject.h create mode 100644 Source/WebCore/rendering/RenderButton.cpp create mode 100644 Source/WebCore/rendering/RenderButton.h create mode 100644 Source/WebCore/rendering/RenderCounter.cpp create mode 100644 Source/WebCore/rendering/RenderCounter.h create mode 100644 Source/WebCore/rendering/RenderDataGrid.cpp create mode 100644 Source/WebCore/rendering/RenderDataGrid.h create mode 100644 Source/WebCore/rendering/RenderDetails.cpp create mode 100644 Source/WebCore/rendering/RenderDetails.h create mode 100644 Source/WebCore/rendering/RenderDetailsMarker.cpp create mode 100644 Source/WebCore/rendering/RenderDetailsMarker.h create mode 100644 Source/WebCore/rendering/RenderEmbeddedObject.cpp create mode 100644 Source/WebCore/rendering/RenderEmbeddedObject.h create mode 100644 Source/WebCore/rendering/RenderFieldset.cpp create mode 100644 Source/WebCore/rendering/RenderFieldset.h create mode 100644 Source/WebCore/rendering/RenderFileUploadControl.cpp create mode 100644 Source/WebCore/rendering/RenderFileUploadControl.h create mode 100644 Source/WebCore/rendering/RenderFlexibleBox.cpp create mode 100644 Source/WebCore/rendering/RenderFlexibleBox.h create mode 100644 Source/WebCore/rendering/RenderForeignObject.cpp create mode 100644 Source/WebCore/rendering/RenderForeignObject.h create mode 100644 Source/WebCore/rendering/RenderFrame.cpp create mode 100644 Source/WebCore/rendering/RenderFrame.h create mode 100644 Source/WebCore/rendering/RenderFrameBase.cpp create mode 100644 Source/WebCore/rendering/RenderFrameBase.h create mode 100644 Source/WebCore/rendering/RenderFrameSet.cpp create mode 100644 Source/WebCore/rendering/RenderFrameSet.h create mode 100644 Source/WebCore/rendering/RenderFullScreen.cpp create mode 100644 Source/WebCore/rendering/RenderFullScreen.h create mode 100644 Source/WebCore/rendering/RenderHTMLCanvas.cpp create mode 100644 Source/WebCore/rendering/RenderHTMLCanvas.h create mode 100644 Source/WebCore/rendering/RenderIFrame.cpp create mode 100644 Source/WebCore/rendering/RenderIFrame.h create mode 100644 Source/WebCore/rendering/RenderImage.cpp create mode 100644 Source/WebCore/rendering/RenderImage.h create mode 100644 Source/WebCore/rendering/RenderImageResource.cpp create mode 100644 Source/WebCore/rendering/RenderImageResource.h create mode 100644 Source/WebCore/rendering/RenderImageResourceStyleImage.cpp create mode 100644 Source/WebCore/rendering/RenderImageResourceStyleImage.h create mode 100644 Source/WebCore/rendering/RenderIndicator.cpp create mode 100644 Source/WebCore/rendering/RenderIndicator.h create mode 100644 Source/WebCore/rendering/RenderInline.cpp create mode 100644 Source/WebCore/rendering/RenderInline.h create mode 100644 Source/WebCore/rendering/RenderInputSpeech.cpp create mode 100644 Source/WebCore/rendering/RenderInputSpeech.h create mode 100644 Source/WebCore/rendering/RenderLayer.cpp create mode 100644 Source/WebCore/rendering/RenderLayer.h create mode 100644 Source/WebCore/rendering/RenderLayerBacking.cpp create mode 100644 Source/WebCore/rendering/RenderLayerBacking.h create mode 100644 Source/WebCore/rendering/RenderLayerCompositor.cpp create mode 100644 Source/WebCore/rendering/RenderLayerCompositor.h create mode 100644 Source/WebCore/rendering/RenderLineBoxList.cpp create mode 100644 Source/WebCore/rendering/RenderLineBoxList.h create mode 100644 Source/WebCore/rendering/RenderListBox.cpp create mode 100644 Source/WebCore/rendering/RenderListBox.h create mode 100644 Source/WebCore/rendering/RenderListItem.cpp create mode 100644 Source/WebCore/rendering/RenderListItem.h create mode 100644 Source/WebCore/rendering/RenderListMarker.cpp create mode 100644 Source/WebCore/rendering/RenderListMarker.h create mode 100644 Source/WebCore/rendering/RenderMarquee.cpp create mode 100644 Source/WebCore/rendering/RenderMarquee.h create mode 100644 Source/WebCore/rendering/RenderMedia.cpp create mode 100644 Source/WebCore/rendering/RenderMedia.h create mode 100644 Source/WebCore/rendering/RenderMediaControls.cpp create mode 100644 Source/WebCore/rendering/RenderMediaControls.h create mode 100644 Source/WebCore/rendering/RenderMediaControlsChromium.cpp create mode 100644 Source/WebCore/rendering/RenderMediaControlsChromium.h create mode 100644 Source/WebCore/rendering/RenderMenuList.cpp create mode 100644 Source/WebCore/rendering/RenderMenuList.h create mode 100644 Source/WebCore/rendering/RenderMeter.cpp create mode 100644 Source/WebCore/rendering/RenderMeter.h create mode 100644 Source/WebCore/rendering/RenderObject.cpp create mode 100644 Source/WebCore/rendering/RenderObject.h create mode 100644 Source/WebCore/rendering/RenderObjectChildList.cpp create mode 100644 Source/WebCore/rendering/RenderObjectChildList.h create mode 100644 Source/WebCore/rendering/RenderOverflow.h create mode 100644 Source/WebCore/rendering/RenderPart.cpp create mode 100644 Source/WebCore/rendering/RenderPart.h create mode 100644 Source/WebCore/rendering/RenderProgress.cpp create mode 100644 Source/WebCore/rendering/RenderProgress.h create mode 100644 Source/WebCore/rendering/RenderReplaced.cpp create mode 100644 Source/WebCore/rendering/RenderReplaced.h create mode 100644 Source/WebCore/rendering/RenderReplica.cpp create mode 100644 Source/WebCore/rendering/RenderReplica.h create mode 100644 Source/WebCore/rendering/RenderRuby.cpp create mode 100644 Source/WebCore/rendering/RenderRuby.h create mode 100644 Source/WebCore/rendering/RenderRubyBase.cpp create mode 100644 Source/WebCore/rendering/RenderRubyBase.h create mode 100644 Source/WebCore/rendering/RenderRubyRun.cpp create mode 100644 Source/WebCore/rendering/RenderRubyRun.h create mode 100644 Source/WebCore/rendering/RenderRubyText.cpp create mode 100644 Source/WebCore/rendering/RenderRubyText.h create mode 100644 Source/WebCore/rendering/RenderSVGAllInOne.cpp create mode 100644 Source/WebCore/rendering/RenderSVGBlock.cpp create mode 100644 Source/WebCore/rendering/RenderSVGBlock.h create mode 100644 Source/WebCore/rendering/RenderSVGContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGContainer.h create mode 100644 Source/WebCore/rendering/RenderSVGGradientStop.cpp create mode 100644 Source/WebCore/rendering/RenderSVGGradientStop.h create mode 100644 Source/WebCore/rendering/RenderSVGHiddenContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGHiddenContainer.h create mode 100644 Source/WebCore/rendering/RenderSVGImage.cpp create mode 100644 Source/WebCore/rendering/RenderSVGImage.h create mode 100644 Source/WebCore/rendering/RenderSVGModelObject.cpp create mode 100644 Source/WebCore/rendering/RenderSVGModelObject.h create mode 100644 Source/WebCore/rendering/RenderSVGResource.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResource.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceClipper.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceClipper.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceContainer.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceFilter.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceFilter.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceGradient.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceGradient.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceLinearGradient.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceLinearGradient.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceMarker.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceMarker.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceMasker.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceMasker.h create mode 100644 Source/WebCore/rendering/RenderSVGResourcePattern.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourcePattern.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceRadialGradient.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceRadialGradient.h create mode 100644 Source/WebCore/rendering/RenderSVGResourceSolidColor.cpp create mode 100644 Source/WebCore/rendering/RenderSVGResourceSolidColor.h create mode 100644 Source/WebCore/rendering/RenderSVGRoot.cpp create mode 100644 Source/WebCore/rendering/RenderSVGRoot.h create mode 100644 Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.h create mode 100644 Source/WebCore/rendering/RenderSVGTransformableContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGTransformableContainer.h create mode 100644 Source/WebCore/rendering/RenderSVGViewportContainer.cpp create mode 100644 Source/WebCore/rendering/RenderSVGViewportContainer.h create mode 100644 Source/WebCore/rendering/RenderScrollbar.cpp create mode 100644 Source/WebCore/rendering/RenderScrollbar.h create mode 100644 Source/WebCore/rendering/RenderScrollbarPart.cpp create mode 100644 Source/WebCore/rendering/RenderScrollbarPart.h create mode 100644 Source/WebCore/rendering/RenderScrollbarTheme.cpp create mode 100644 Source/WebCore/rendering/RenderScrollbarTheme.h create mode 100644 Source/WebCore/rendering/RenderSelectionInfo.h create mode 100644 Source/WebCore/rendering/RenderSlider.cpp create mode 100644 Source/WebCore/rendering/RenderSlider.h create mode 100644 Source/WebCore/rendering/RenderSummary.cpp create mode 100644 Source/WebCore/rendering/RenderSummary.h create mode 100644 Source/WebCore/rendering/RenderTable.cpp create mode 100644 Source/WebCore/rendering/RenderTable.h create mode 100644 Source/WebCore/rendering/RenderTableCell.cpp create mode 100644 Source/WebCore/rendering/RenderTableCell.h create mode 100644 Source/WebCore/rendering/RenderTableCol.cpp create mode 100644 Source/WebCore/rendering/RenderTableCol.h create mode 100644 Source/WebCore/rendering/RenderTableRow.cpp create mode 100644 Source/WebCore/rendering/RenderTableRow.h create mode 100644 Source/WebCore/rendering/RenderTableSection.cpp create mode 100644 Source/WebCore/rendering/RenderTableSection.h create mode 100644 Source/WebCore/rendering/RenderText.cpp create mode 100644 Source/WebCore/rendering/RenderText.h create mode 100644 Source/WebCore/rendering/RenderTextControl.cpp create mode 100644 Source/WebCore/rendering/RenderTextControl.h create mode 100644 Source/WebCore/rendering/RenderTextControlMultiLine.cpp create mode 100644 Source/WebCore/rendering/RenderTextControlMultiLine.h create mode 100644 Source/WebCore/rendering/RenderTextControlSingleLine.cpp create mode 100644 Source/WebCore/rendering/RenderTextControlSingleLine.h create mode 100644 Source/WebCore/rendering/RenderTextFragment.cpp create mode 100644 Source/WebCore/rendering/RenderTextFragment.h create mode 100644 Source/WebCore/rendering/RenderTheme.cpp create mode 100644 Source/WebCore/rendering/RenderTheme.h create mode 100644 Source/WebCore/rendering/RenderThemeChromiumLinux.cpp create mode 100644 Source/WebCore/rendering/RenderThemeChromiumLinux.h create mode 100644 Source/WebCore/rendering/RenderThemeChromiumMac.h create mode 100644 Source/WebCore/rendering/RenderThemeChromiumMac.mm create mode 100644 Source/WebCore/rendering/RenderThemeChromiumSkia.cpp create mode 100644 Source/WebCore/rendering/RenderThemeChromiumSkia.h create mode 100644 Source/WebCore/rendering/RenderThemeChromiumWin.cpp create mode 100644 Source/WebCore/rendering/RenderThemeChromiumWin.h create mode 100644 Source/WebCore/rendering/RenderThemeMac.h create mode 100644 Source/WebCore/rendering/RenderThemeMac.mm create mode 100644 Source/WebCore/rendering/RenderThemeSafari.cpp create mode 100644 Source/WebCore/rendering/RenderThemeSafari.h create mode 100644 Source/WebCore/rendering/RenderThemeWin.cpp create mode 100644 Source/WebCore/rendering/RenderThemeWin.h create mode 100644 Source/WebCore/rendering/RenderThemeWinCE.cpp create mode 100644 Source/WebCore/rendering/RenderThemeWinCE.h create mode 100644 Source/WebCore/rendering/RenderTreeAsText.cpp create mode 100644 Source/WebCore/rendering/RenderTreeAsText.h create mode 100644 Source/WebCore/rendering/RenderVideo.cpp create mode 100644 Source/WebCore/rendering/RenderVideo.h create mode 100644 Source/WebCore/rendering/RenderView.cpp create mode 100644 Source/WebCore/rendering/RenderView.h create mode 100644 Source/WebCore/rendering/RenderWidget.cpp create mode 100644 Source/WebCore/rendering/RenderWidget.h create mode 100644 Source/WebCore/rendering/RenderWidgetProtector.h create mode 100644 Source/WebCore/rendering/RenderWordBreak.cpp create mode 100644 Source/WebCore/rendering/RenderWordBreak.h create mode 100644 Source/WebCore/rendering/RenderingAllInOne.cpp create mode 100644 Source/WebCore/rendering/RootInlineBox.cpp create mode 100644 Source/WebCore/rendering/RootInlineBox.h create mode 100644 Source/WebCore/rendering/SVGImageBufferTools.cpp create mode 100644 Source/WebCore/rendering/SVGImageBufferTools.h create mode 100644 Source/WebCore/rendering/SVGMarkerData.h create mode 100644 Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp create mode 100644 Source/WebCore/rendering/SVGMarkerLayoutInfo.h create mode 100644 Source/WebCore/rendering/SVGRenderSupport.cpp create mode 100644 Source/WebCore/rendering/SVGRenderSupport.h create mode 100644 Source/WebCore/rendering/SVGRenderTreeAsText.cpp create mode 100644 Source/WebCore/rendering/SVGRenderTreeAsText.h create mode 100644 Source/WebCore/rendering/SVGResources.cpp create mode 100644 Source/WebCore/rendering/SVGResources.h create mode 100644 Source/WebCore/rendering/SVGResourcesCache.cpp create mode 100644 Source/WebCore/rendering/SVGResourcesCache.h create mode 100644 Source/WebCore/rendering/SVGResourcesCycleSolver.cpp create mode 100644 Source/WebCore/rendering/SVGResourcesCycleSolver.h create mode 100644 Source/WebCore/rendering/SVGShadowTreeElements.cpp create mode 100644 Source/WebCore/rendering/SVGShadowTreeElements.h create mode 100644 Source/WebCore/rendering/ScrollBehavior.cpp create mode 100644 Source/WebCore/rendering/ScrollBehavior.h create mode 100644 Source/WebCore/rendering/ShadowElement.cpp create mode 100644 Source/WebCore/rendering/ShadowElement.h create mode 100644 Source/WebCore/rendering/TableLayout.h create mode 100644 Source/WebCore/rendering/TextControlInnerElements.cpp create mode 100644 Source/WebCore/rendering/TextControlInnerElements.h create mode 100644 Source/WebCore/rendering/TrailingFloatsRootInlineBox.h create mode 100644 Source/WebCore/rendering/TransformState.cpp create mode 100644 Source/WebCore/rendering/TransformState.h create mode 100644 Source/WebCore/rendering/VerticalPositionCache.h create mode 100644 Source/WebCore/rendering/break_lines.cpp create mode 100644 Source/WebCore/rendering/break_lines.h create mode 100644 Source/WebCore/rendering/style/BorderData.h create mode 100644 Source/WebCore/rendering/style/BorderValue.h create mode 100644 Source/WebCore/rendering/style/CollapsedBorderValue.h create mode 100644 Source/WebCore/rendering/style/ContentData.cpp create mode 100644 Source/WebCore/rendering/style/ContentData.h create mode 100644 Source/WebCore/rendering/style/CounterContent.h create mode 100644 Source/WebCore/rendering/style/CounterDirectives.cpp create mode 100644 Source/WebCore/rendering/style/CounterDirectives.h create mode 100644 Source/WebCore/rendering/style/CursorData.h create mode 100644 Source/WebCore/rendering/style/CursorList.h create mode 100644 Source/WebCore/rendering/style/DataRef.h create mode 100644 Source/WebCore/rendering/style/FillLayer.cpp create mode 100644 Source/WebCore/rendering/style/FillLayer.h create mode 100644 Source/WebCore/rendering/style/KeyframeList.cpp create mode 100644 Source/WebCore/rendering/style/KeyframeList.h create mode 100644 Source/WebCore/rendering/style/LineClampValue.h create mode 100644 Source/WebCore/rendering/style/NinePieceImage.cpp create mode 100644 Source/WebCore/rendering/style/NinePieceImage.h create mode 100644 Source/WebCore/rendering/style/OutlineValue.h create mode 100644 Source/WebCore/rendering/style/RenderStyle.cpp create mode 100644 Source/WebCore/rendering/style/RenderStyle.h create mode 100644 Source/WebCore/rendering/style/RenderStyleConstants.h create mode 100644 Source/WebCore/rendering/style/SVGRenderStyle.cpp create mode 100644 Source/WebCore/rendering/style/SVGRenderStyle.h create mode 100644 Source/WebCore/rendering/style/SVGRenderStyleDefs.cpp create mode 100644 Source/WebCore/rendering/style/SVGRenderStyleDefs.h create mode 100644 Source/WebCore/rendering/style/ShadowData.cpp create mode 100644 Source/WebCore/rendering/style/ShadowData.h create mode 100644 Source/WebCore/rendering/style/StyleAllInOne.cpp create mode 100644 Source/WebCore/rendering/style/StyleBackgroundData.cpp create mode 100644 Source/WebCore/rendering/style/StyleBackgroundData.h create mode 100644 Source/WebCore/rendering/style/StyleBoxData.cpp create mode 100644 Source/WebCore/rendering/style/StyleBoxData.h create mode 100644 Source/WebCore/rendering/style/StyleCachedImage.cpp create mode 100644 Source/WebCore/rendering/style/StyleCachedImage.h create mode 100644 Source/WebCore/rendering/style/StyleDashboardRegion.h create mode 100644 Source/WebCore/rendering/style/StyleFlexibleBoxData.cpp create mode 100644 Source/WebCore/rendering/style/StyleFlexibleBoxData.h create mode 100644 Source/WebCore/rendering/style/StyleGeneratedImage.cpp create mode 100644 Source/WebCore/rendering/style/StyleGeneratedImage.h create mode 100644 Source/WebCore/rendering/style/StyleImage.h create mode 100644 Source/WebCore/rendering/style/StyleInheritedData.cpp create mode 100644 Source/WebCore/rendering/style/StyleInheritedData.h create mode 100644 Source/WebCore/rendering/style/StyleMarqueeData.cpp create mode 100644 Source/WebCore/rendering/style/StyleMarqueeData.h create mode 100644 Source/WebCore/rendering/style/StyleMultiColData.cpp create mode 100644 Source/WebCore/rendering/style/StyleMultiColData.h create mode 100644 Source/WebCore/rendering/style/StylePendingImage.h create mode 100644 Source/WebCore/rendering/style/StyleRareInheritedData.cpp create mode 100644 Source/WebCore/rendering/style/StyleRareInheritedData.h create mode 100644 Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp create mode 100644 Source/WebCore/rendering/style/StyleRareNonInheritedData.h create mode 100644 Source/WebCore/rendering/style/StyleReflection.h create mode 100644 Source/WebCore/rendering/style/StyleSurroundData.cpp create mode 100644 Source/WebCore/rendering/style/StyleSurroundData.h create mode 100644 Source/WebCore/rendering/style/StyleTransformData.cpp create mode 100644 Source/WebCore/rendering/style/StyleTransformData.h create mode 100644 Source/WebCore/rendering/style/StyleVisualData.cpp create mode 100644 Source/WebCore/rendering/style/StyleVisualData.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGInline.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGInline.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGInlineText.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGInlineText.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGPath.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGPath.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGTSpan.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGTSpan.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGText.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGText.h create mode 100644 Source/WebCore/rendering/svg/RenderSVGTextPath.cpp create mode 100644 Source/WebCore/rendering/svg/RenderSVGTextPath.h create mode 100644 Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp create mode 100644 Source/WebCore/rendering/svg/SVGInlineFlowBox.h create mode 100644 Source/WebCore/rendering/svg/SVGInlineTextBox.cpp create mode 100644 Source/WebCore/rendering/svg/SVGInlineTextBox.h create mode 100644 Source/WebCore/rendering/svg/SVGRootInlineBox.cpp create mode 100644 Source/WebCore/rendering/svg/SVGRootInlineBox.h create mode 100644 Source/WebCore/rendering/svg/SVGTextChunk.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextChunk.h create mode 100644 Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextChunkBuilder.h create mode 100644 Source/WebCore/rendering/svg/SVGTextFragment.h create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngine.h create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h create mode 100644 Source/WebCore/rendering/svg/SVGTextMetrics.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextMetrics.h create mode 100644 Source/WebCore/rendering/svg/SVGTextQuery.cpp create mode 100644 Source/WebCore/rendering/svg/SVGTextQuery.h (limited to 'Source/WebCore/rendering') diff --git a/Source/WebCore/rendering/AutoTableLayout.cpp b/Source/WebCore/rendering/AutoTableLayout.cpp new file mode 100644 index 0000000..bb0df0b --- /dev/null +++ b/Source/WebCore/rendering/AutoTableLayout.cpp @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2008, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "AutoTableLayout.h" + +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableSection.h" + +using namespace std; + +namespace WebCore { + +AutoTableLayout::AutoTableLayout(RenderTable* table) + : TableLayout(table) + , m_hasPercent(false) + , m_effectiveLogicalWidthDirty(true) +{ +} + +AutoTableLayout::~AutoTableLayout() +{ +} + +void AutoTableLayout::recalcColumn(int effCol) +{ + Layout& columnLayout = m_layoutStruct[effCol]; + + RenderTableCell* fixedContributor = 0; + RenderTableCell* maxContributor = 0; + + for (RenderObject* child = m_table->firstChild(); child; child = child->nextSibling()) { + if (child->isTableCol()) + toRenderTableCol(child)->computePreferredLogicalWidths(); + else if (child->isTableSection()) { + RenderTableSection* section = toRenderTableSection(child); + int numRows = section->numRows(); + for (int i = 0; i < numRows; i++) { + RenderTableSection::CellStruct current = section->cellAt(i, effCol); + RenderTableCell* cell = current.primaryCell(); + + bool cellHasContent = cell && !current.inColSpan && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding()); + if (cellHasContent) + columnLayout.emptyCellsOnly = false; + + if (current.inColSpan || !cell) + continue; + + if (cell->colSpan() == 1) { + // A cell originates in this column. Ensure we have + // a min/max width of at least 1px for this column now. + columnLayout.minLogicalWidth = max(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0); + columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, 1); + if (cell->preferredLogicalWidthsDirty()) + cell->computePreferredLogicalWidths(); + columnLayout.minLogicalWidth = max(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth); + if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) { + columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth(); + maxContributor = cell; + } + + Length cellLogicalWidth = cell->styleOrColLogicalWidth(); + // FIXME: What is this arbitrary value? + if (cellLogicalWidth.rawValue() > 32760) + cellLogicalWidth.setRawValue(32760); + if (cellLogicalWidth.isNegative()) + cellLogicalWidth.setValue(0); + switch (cellLogicalWidth.type()) { + case Fixed: + // ignore width=0 + if (cellLogicalWidth.value() > 0 && columnLayout.logicalWidth.type() != Percent) { + int logicalWidth = cell->computeBorderBoxLogicalWidth(cellLogicalWidth.value()); + if (columnLayout.logicalWidth.isFixed()) { + // Nav/IE weirdness + if ((logicalWidth > columnLayout.logicalWidth.value()) || + ((columnLayout.logicalWidth.value() == logicalWidth) && (maxContributor == cell))) { + columnLayout.logicalWidth.setValue(logicalWidth); + fixedContributor = cell; + } + } else { + columnLayout.logicalWidth.setValue(Fixed, logicalWidth); + fixedContributor = cell; + } + } + break; + case Percent: + m_hasPercent = true; + if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercent() || cellLogicalWidth.rawValue() > columnLayout.logicalWidth.rawValue())) + columnLayout.logicalWidth = cellLogicalWidth; + break; + case Relative: + // FIXME: Need to understand this case and whether it makes sense to compare values + // which are not necessarily of the same type. + if (cellLogicalWidth.isAuto() || (cellLogicalWidth.isRelative() && cellLogicalWidth.value() > columnLayout.logicalWidth.rawValue())) + columnLayout.logicalWidth = cellLogicalWidth; + default: + break; + } + } else if (!effCol || section->primaryCellAt(i, effCol - 1) != cell) { + // This spanning cell originates in this column. Ensure we have + // a min/max width of at least 1px for this column now. + columnLayout.minLogicalWidth = max(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0); + columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, 1); + insertSpanCell(cell); + } + } + } + } + + // Nav/IE weirdness + if (columnLayout.logicalWidth.isFixed()) { + if (m_table->document()->inQuirksMode() && columnLayout.maxLogicalWidth > columnLayout.logicalWidth.value() && fixedContributor != maxContributor) { + columnLayout.logicalWidth = Length(); + fixedContributor = 0; + } + } + + columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, columnLayout.minLogicalWidth); +} + +void AutoTableLayout::fullRecalc() +{ + m_hasPercent = false; + m_effectiveLogicalWidthDirty = true; + + int nEffCols = m_table->numEffCols(); + m_layoutStruct.resize(nEffCols); + m_layoutStruct.fill(Layout()); + m_spanCells.fill(0); + + RenderObject* child = m_table->firstChild(); + Length groupLogicalWidth; + int currentColumn = 0; + while (child && child->isTableCol()) { + RenderTableCol* col = toRenderTableCol(child); + int span = col->span(); + if (col->firstChild()) + groupLogicalWidth = col->style()->logicalWidth(); + else { + Length colLogicalWidth = col->style()->logicalWidth(); + if (colLogicalWidth.isAuto()) + colLogicalWidth = groupLogicalWidth; + if ((colLogicalWidth.isFixed() || colLogicalWidth.isPercent()) && colLogicalWidth.isZero()) + colLogicalWidth = Length(); + int effCol = m_table->colToEffCol(currentColumn); + if (!colLogicalWidth.isAuto() && span == 1 && effCol < nEffCols && m_table->spanOfEffCol(effCol) == 1) { + m_layoutStruct[effCol].logicalWidth = colLogicalWidth; + if (colLogicalWidth.isFixed() && m_layoutStruct[effCol].maxLogicalWidth < colLogicalWidth.value()) + m_layoutStruct[effCol].maxLogicalWidth = colLogicalWidth.value(); + } + currentColumn += span; + } + + RenderObject* next = child->firstChild(); + if (!next) + next = child->nextSibling(); + if (!next && child->parent()->isTableCol()) { + next = child->parent()->nextSibling(); + groupLogicalWidth = Length(); + } + child = next; + } + + for (int i = 0; i < nEffCols; i++) + recalcColumn(i); +} + +// FIXME: This needs to be adapted for vertical writing modes. +static bool shouldScaleColumns(RenderTable* table) +{ + // A special case. If this table is not fixed width and contained inside + // a cell, then don't bloat the maxwidth by examining percentage growth. + bool scale = true; + while (table) { + Length tw = table->style()->width(); + if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) { + RenderBlock* cb = table->containingBlock(); + while (cb && !cb->isRenderView() && !cb->isTableCell() && + cb->style()->width().isAuto() && !cb->isPositioned()) + cb = cb->containingBlock(); + + table = 0; + if (cb && cb->isTableCell() && + (cb->style()->width().isAuto() || cb->style()->width().isPercent())) { + if (tw.isPercent()) + scale = false; + else { + RenderTableCell* cell = toRenderTableCell(cb); + if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto()) + scale = false; + else + table = cell->table(); + } + } + } + else + table = 0; + } + return scale; +} + +void AutoTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth) +{ + fullRecalc(); + + int spanMaxLogicalWidth = calcEffectiveLogicalWidth(); + minWidth = 0; + maxWidth = 0; + float maxPercent = 0; + float maxNonPercent = 0; + bool scaleColumns = shouldScaleColumns(m_table); + + // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero. + // FIXME: Handle the 0% cases properly. + const int epsilon = 1; + + int remainingPercent = 100 * percentScaleFactor; + for (size_t i = 0; i < m_layoutStruct.size(); ++i) { + minWidth += m_layoutStruct[i].effectiveMinLogicalWidth; + maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth; + if (scaleColumns) { + if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) { + int percent = min(m_layoutStruct[i].effectiveLogicalWidth.rawValue(), remainingPercent); + float logicalWidth = static_cast(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 * percentScaleFactor / max(percent, epsilon); + maxPercent = max(logicalWidth, maxPercent); + remainingPercent -= percent; + } else + maxNonPercent += m_layoutStruct[i].effectiveMaxLogicalWidth; + } + } + + if (scaleColumns) { + maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon); + maxWidth = max(maxWidth, static_cast(min(maxNonPercent, INT_MAX / 2.0f))); + maxWidth = max(maxWidth, static_cast(min(maxPercent, INT_MAX / 2.0f))); + } + + maxWidth = max(maxWidth, spanMaxLogicalWidth); + + int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection(); + minWidth += bordersPaddingAndSpacing; + maxWidth += bordersPaddingAndSpacing; + + Length tableLogicalWidth = m_table->style()->logicalWidth(); + if (tableLogicalWidth.isFixed() && tableLogicalWidth.value() > 0) { + minWidth = max(minWidth, tableLogicalWidth.value()); + maxWidth = minWidth; + } +} + +/* + This method takes care of colspans. + effWidth is the same as width for cells without colspans. If we have colspans, they get modified. + */ +int AutoTableLayout::calcEffectiveLogicalWidth() +{ + float maxLogicalWidth = 0; + + size_t nEffCols = m_layoutStruct.size(); + int spacingInRowDirection = m_table->hBorderSpacing(); + + for (size_t i = 0; i < nEffCols; ++i) { + m_layoutStruct[i].effectiveLogicalWidth = m_layoutStruct[i].logicalWidth; + m_layoutStruct[i].effectiveMinLogicalWidth = m_layoutStruct[i].minLogicalWidth; + m_layoutStruct[i].effectiveMaxLogicalWidth = m_layoutStruct[i].maxLogicalWidth; + } + + for (size_t i = 0; i < m_spanCells.size(); ++i) { + RenderTableCell* cell = m_spanCells[i]; + if (!cell) + break; + + int span = cell->colSpan(); + + Length cellLogicalWidth = cell->styleOrColLogicalWidth(); + if (!cellLogicalWidth.isRelative() && cellLogicalWidth.isZero()) + cellLogicalWidth = Length(); // make it Auto + + int effCol = m_table->colToEffCol(cell->col()); + size_t lastCol = effCol; + int cellMinLogicalWidth = cell->minPreferredLogicalWidth() + spacingInRowDirection; + float cellMaxLogicalWidth = cell->maxPreferredLogicalWidth() + spacingInRowDirection; + int totalPercent = 0; + int spanMinLogicalWidth = 0; + float spanMaxLogicalWidth = 0; + bool allColsArePercent = true; + bool allColsAreFixed = true; + bool haveAuto = false; + bool spanHasEmptyCellsOnly = true; + int fixedWidth = 0; + while (lastCol < nEffCols && span > 0) { + Layout& columnLayout = m_layoutStruct[lastCol]; + switch (columnLayout.logicalWidth.type()) { + case Percent: + totalPercent += columnLayout.logicalWidth.rawValue(); + allColsAreFixed = false; + break; + case Fixed: + if (columnLayout.logicalWidth.value() > 0) { + fixedWidth += columnLayout.logicalWidth.value(); + allColsArePercent = false; + // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad + // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither. + break; + } + // fall through + case Auto: + haveAuto = true; + // fall through + default: + // If the column is a percentage width, do not let the spanning cell overwrite the + // width value. This caused a mis-rendering on amazon.com. + // Sample snippet: + // < + // + // + //
12-3
12-3
+ if (!columnLayout.effectiveLogicalWidth.isPercent()) { + columnLayout.effectiveLogicalWidth = Length(); + allColsArePercent = false; + } else + totalPercent += columnLayout.effectiveLogicalWidth.rawValue(); + allColsAreFixed = false; + } + if (!columnLayout.emptyCellsOnly) + spanHasEmptyCellsOnly = false; + span -= m_table->spanOfEffCol(lastCol); + spanMinLogicalWidth += columnLayout.effectiveMinLogicalWidth; + spanMaxLogicalWidth += columnLayout.effectiveMaxLogicalWidth; + lastCol++; + cellMinLogicalWidth -= spacingInRowDirection; + cellMaxLogicalWidth -= spacingInRowDirection; + } + + // adjust table max width if needed + if (cellLogicalWidth.isPercent()) { + if (totalPercent > cellLogicalWidth.rawValue() || allColsArePercent) { + // can't satify this condition, treat as variable + cellLogicalWidth = Length(); + } else { + maxLogicalWidth = max(maxLogicalWidth, max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 * percentScaleFactor / cellLogicalWidth.rawValue()); + + // all non percent columns in the span get percent values to sum up correctly. + int percentMissing = cellLogicalWidth.rawValue() - totalPercent; + float totalWidth = 0; + for (unsigned pos = effCol; pos < lastCol; ++pos) { + if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) + totalWidth += m_layoutStruct[pos].effectiveMaxLogicalWidth; + } + + for (unsigned pos = effCol; pos < lastCol && totalWidth > 0; ++pos) { + if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) { + int percent = static_cast(percentMissing * static_cast(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth); + totalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; + percentMissing -= percent; + if (percent > 0) + m_layoutStruct[pos].effectiveLogicalWidth.setRawValue(Percent, percent); + else + m_layoutStruct[pos].effectiveLogicalWidth = Length(); + } + } + } + } + + // make sure minWidth and maxWidth of the spanning cell are honoured + if (cellMinLogicalWidth > spanMinLogicalWidth) { + if (allColsAreFixed) { + for (unsigned pos = effCol; fixedWidth > 0 && pos < lastCol; ++pos) { + int cellLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, cellMinLogicalWidth * m_layoutStruct[pos].logicalWidth.value() / fixedWidth); + fixedWidth -= m_layoutStruct[pos].logicalWidth.value(); + cellMinLogicalWidth -= cellLogicalWidth; + m_layoutStruct[pos].effectiveMinLogicalWidth = cellLogicalWidth; + } + } else { + float remainingMaxLogicalWidth = spanMaxLogicalWidth; + int remainingMinLogicalWidth = spanMinLogicalWidth; + + // Give min to variable first, to fixed second, and to others third. + for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol; ++pos) { + if (m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth) { + int colMinLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, m_layoutStruct[pos].logicalWidth.value()); + fixedWidth -= m_layoutStruct[pos].logicalWidth.value(); + remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth; + remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; + cellMinLogicalWidth -= colMinLogicalWidth; + m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth; + } + } + + for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol && remainingMinLogicalWidth < cellMinLogicalWidth; ++pos) { + if (!(m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth)) { + int colMinLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast(m_layoutStruct[pos].effectiveMaxLogicalWidth) / remainingMaxLogicalWidth : cellMinLogicalWidth)); + colMinLogicalWidth = min(m_layoutStruct[pos].effectiveMinLogicalWidth + (cellMinLogicalWidth - remainingMinLogicalWidth), colMinLogicalWidth); + remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; + remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth; + cellMinLogicalWidth -= colMinLogicalWidth; + m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth; + } + } + } + } + if (!cellLogicalWidth.isPercent()) { + if (cellMaxLogicalWidth > spanMaxLogicalWidth) { + for (unsigned pos = effCol; spanMaxLogicalWidth >= 0 && pos < lastCol; ++pos) { + int colMaxLogicalWidth = max(m_layoutStruct[pos].effectiveMaxLogicalWidth, static_cast(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast(m_layoutStruct[pos].effectiveMaxLogicalWidth) / spanMaxLogicalWidth : cellMaxLogicalWidth)); + spanMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; + cellMaxLogicalWidth -= colMaxLogicalWidth; + m_layoutStruct[pos].effectiveMaxLogicalWidth = colMaxLogicalWidth; + } + } + } else { + for (unsigned pos = effCol; pos < lastCol; ++pos) + m_layoutStruct[pos].maxLogicalWidth = max(m_layoutStruct[pos].maxLogicalWidth, m_layoutStruct[pos].minLogicalWidth); + } + // treat span ranges consisting of empty cells only as if they had content + if (spanHasEmptyCellsOnly) { + for (unsigned pos = effCol; pos < lastCol; ++pos) + m_layoutStruct[pos].emptyCellsOnly = false; + } + } + m_effectiveLogicalWidthDirty = false; + + return static_cast(min(maxLogicalWidth, INT_MAX / 2.0f)); +} + +/* gets all cells that originate in a column and have a cellspan > 1 + Sorts them by increasing cellspan +*/ +void AutoTableLayout::insertSpanCell(RenderTableCell *cell) +{ + ASSERT_ARG(cell, cell && cell->colSpan() != 1); + if (!cell || cell->colSpan() == 1) + return; + + int size = m_spanCells.size(); + if (!size || m_spanCells[size-1] != 0) { + m_spanCells.grow(size + 10); + for (int i = 0; i < 10; i++) + m_spanCells[size+i] = 0; + size += 10; + } + + // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better + unsigned int pos = 0; + int span = cell->colSpan(); + while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan()) + pos++; + memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *)); + m_spanCells[pos] = cell; +} + + +void AutoTableLayout::layout() +{ +#ifdef ANDROID_LAYOUT + if (m_table->isSingleColumn()) + return; +#endif + // table layout based on the values collected in the layout structure. + int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); + int available = tableLogicalWidth; + size_t nEffCols = m_table->numEffCols(); + + if (nEffCols != m_layoutStruct.size()) { + fullRecalc(); + nEffCols = m_table->numEffCols(); + } + + if (m_effectiveLogicalWidthDirty) + calcEffectiveLogicalWidth(); + + bool havePercent = false; + int totalRelative = 0; + int numAuto = 0; + int numFixed = 0; + float totalAuto = 0; + float totalFixed = 0; + int totalPercent = 0; + int allocAuto = 0; + unsigned numAutoEmptyCellsOnly = 0; + + // fill up every cell with its minWidth + for (size_t i = 0; i < nEffCols; ++i) { + int cellLogicalWidth = m_layoutStruct[i].effectiveMinLogicalWidth; + m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; + available -= cellLogicalWidth; + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + switch (logicalWidth.type()) { + case Percent: + havePercent = true; + totalPercent += logicalWidth.rawValue(); + break; + case Relative: + totalRelative += logicalWidth.value(); + break; + case Fixed: + numFixed++; + totalFixed += m_layoutStruct[i].effectiveMaxLogicalWidth; + // fall through + break; + case Auto: + case Static: + if (m_layoutStruct[i].emptyCellsOnly) + numAutoEmptyCellsOnly++; + else { + numAuto++; + totalAuto += m_layoutStruct[i].effectiveMaxLogicalWidth; + allocAuto += cellLogicalWidth; + } + break; + default: + break; + } + } + + // allocate width to percent cols + if (available > 0 && havePercent) { + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isPercent()) { + int cellLogicalWidth = max(m_layoutStruct[i].effectiveMinLogicalWidth, logicalWidth.calcMinValue(tableLogicalWidth)); + available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth; + m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; + } + } + if (totalPercent > 100 * percentScaleFactor) { + // remove overallocated space from the last columns + int excess = tableLogicalWidth * (totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor); + for (int i = nEffCols - 1; i >= 0; --i) { + if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) { + int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth; + int reduction = min(cellLogicalWidth, excess); + // the lines below might look inconsistent, but that's the way it's handled in mozilla + excess -= reduction; + int newLogicalWidth = max(m_layoutStruct[i].effectiveMinLogicalWidth, cellLogicalWidth - reduction); + available += cellLogicalWidth - newLogicalWidth; + m_layoutStruct[i].computedLogicalWidth = newLogicalWidth; + } + } + } + } + + // then allocate width to fixed cols + if (available > 0) { + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isFixed() && logicalWidth.value() > m_layoutStruct[i].computedLogicalWidth) { + available += m_layoutStruct[i].computedLogicalWidth - logicalWidth.value(); + m_layoutStruct[i].computedLogicalWidth = logicalWidth.value(); + } + } + } + + // now satisfy relative + if (available > 0) { + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isRelative() && logicalWidth.value() != 0) { + // width=0* gets effMinWidth. + int cellLogicalWidth = logicalWidth.value() * tableLogicalWidth / totalRelative; + available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth; + m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; + } + } + } + + // now satisfy variable + if (available > 0 && numAuto) { + available += allocAuto; // this gets redistributed + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isAuto() && totalAuto && !m_layoutStruct[i].emptyCellsOnly) { + int cellLogicalWidth = max(m_layoutStruct[i].computedLogicalWidth, static_cast(available * static_cast(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalAuto)); + available -= cellLogicalWidth; + totalAuto -= m_layoutStruct[i].effectiveMaxLogicalWidth; + m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; + } + } + } + + // spread over fixed columns + if (available > 0 && numFixed) { + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isFixed()) { + int cellLogicalWidth = static_cast(available * static_cast(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalFixed); + available -= cellLogicalWidth; + totalFixed -= m_layoutStruct[i].effectiveMaxLogicalWidth; + m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; + } + } + } + + // spread over percent colums + if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) { + for (size_t i = 0; i < nEffCols; ++i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isPercent()) { + int cellLogicalWidth = available * logicalWidth.rawValue() / totalPercent; + available -= cellLogicalWidth; + totalPercent -= logicalWidth.rawValue(); + m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; + if (!available || !totalPercent) + break; + } + } + } + + // spread over the rest + if (available > 0 && nEffCols > numAutoEmptyCellsOnly) { + int total = nEffCols - numAutoEmptyCellsOnly; + // still have some width to spread + for (int i = nEffCols - 1; i >= 0; --i) { + // variable columns with empty cells only don't get any width + if (m_layoutStruct[i].effectiveLogicalWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly) + continue; + int cellLogicalWidth = available / total; + available -= cellLogicalWidth; + total--; + m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; + } + } + + // If we have overallocated, reduce every cell according to the difference between desired width and minwidth + // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing. + if (available < 0) { + // Need to reduce cells with the following prioritization: + // (1) Auto + // (2) Relative + // (3) Fixed + // (4) Percent + // This is basically the reverse of how we grew the cells. + if (available < 0) { + int logicalWidthBeyondMin = 0; + for (int i = nEffCols - 1; i >= 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isAuto()) + logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + } + + for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isAuto()) { + int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + int reduce = available * minMaxDiff / logicalWidthBeyondMin; + m_layoutStruct[i].computedLogicalWidth += reduce; + available -= reduce; + logicalWidthBeyondMin -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int logicalWidthBeyondMin = 0; + for (int i = nEffCols - 1; i >= 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isRelative()) + logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + } + + for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isRelative()) { + int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + int reduce = available * minMaxDiff / logicalWidthBeyondMin; + m_layoutStruct[i].computedLogicalWidth += reduce; + available -= reduce; + logicalWidthBeyondMin -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int logicalWidthBeyondMin = 0; + for (int i = nEffCols - 1; i >= 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isFixed()) + logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + } + + for (int i = nEffCols - 1; i >= 0 && logicalWidthBeyondMin > 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isFixed()) { + int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + int reduce = available * minMaxDiff / logicalWidthBeyondMin; + m_layoutStruct[i].computedLogicalWidth += reduce; + available -= reduce; + logicalWidthBeyondMin -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int logicalWidthBeyondMin = 0; + for (int i = nEffCols - 1; i >= 0; --i) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isPercent()) + logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + } + + for (int i = nEffCols-1; i >= 0 && logicalWidthBeyondMin > 0; i--) { + Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; + if (logicalWidth.isPercent()) { + int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; + int reduce = available * minMaxDiff / logicalWidthBeyondMin; + m_layoutStruct[i].computedLogicalWidth += reduce; + available -= reduce; + logicalWidthBeyondMin -= minMaxDiff; + if (available >= 0) + break; + } + } + } + } + + int pos = 0; + for (size_t i = 0; i < nEffCols; ++i) { + m_table->columnPositions()[i] = pos; + pos += m_layoutStruct[i].computedLogicalWidth + m_table->hBorderSpacing(); + } + m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos; +} + +} diff --git a/Source/WebCore/rendering/AutoTableLayout.h b/Source/WebCore/rendering/AutoTableLayout.h new file mode 100644 index 0000000..7ade0d6 --- /dev/null +++ b/Source/WebCore/rendering/AutoTableLayout.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef AutoTableLayout_h +#define AutoTableLayout_h + +#include "Length.h" +#include "TableLayout.h" +#include + +namespace WebCore { + +class RenderTable; +class RenderTableCell; + +class AutoTableLayout : public TableLayout { +public: + AutoTableLayout(RenderTable*); + ~AutoTableLayout(); + + virtual void computePreferredLogicalWidths(int& minWidth, int& maxWidth); + virtual void layout(); + +private: + void fullRecalc(); + void recalcColumn(int effCol); + + int calcEffectiveLogicalWidth(); + + void insertSpanCell(RenderTableCell*); + + struct Layout { + Layout() + : minLogicalWidth(0) + , maxLogicalWidth(0) + , effectiveMinLogicalWidth(0) + , effectiveMaxLogicalWidth(0) + , computedLogicalWidth(0) + , emptyCellsOnly(true) + { + } + + Length logicalWidth; + Length effectiveLogicalWidth; + int minLogicalWidth; + int maxLogicalWidth; + int effectiveMinLogicalWidth; + int effectiveMaxLogicalWidth; + int computedLogicalWidth; + bool emptyCellsOnly; + }; + + Vector m_layoutStruct; + Vector m_spanCells; + bool m_hasPercent : 1; + mutable bool m_effectiveLogicalWidthDirty : 1; +}; + +} // namespace WebCore + +#endif // AutoTableLayout_h diff --git a/Source/WebCore/rendering/BidiRun.cpp b/Source/WebCore/rendering/BidiRun.cpp new file mode 100644 index 0000000..ac13046 --- /dev/null +++ b/Source/WebCore/rendering/BidiRun.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "BidiRun.h" +#include "InlineBox.h" +#include "RenderArena.h" +#include + +using namespace WTF; + +namespace WebCore { + +#ifndef NDEBUG +static RefCountedLeakCounter bidiRunCounter("BidiRun"); + +static bool inBidiRunDestroy; +#endif + +void BidiRun::destroy() +{ +#ifndef NDEBUG + inBidiRunDestroy = true; +#endif + RenderArena* renderArena = m_object->renderArena(); + delete this; +#ifndef NDEBUG + inBidiRunDestroy = false; +#endif + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*reinterpret_cast(this), this); +} + +void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw() +{ +#ifndef NDEBUG + bidiRunCounter.increment(); +#endif + return renderArena->allocate(sz); +} + +void BidiRun::operator delete(void* ptr, size_t sz) +{ +#ifndef NDEBUG + bidiRunCounter.decrement(); +#endif + ASSERT(inBidiRunDestroy); + + // Stash size where destroy() can find it. + *(size_t*)ptr = sz; +} + +} diff --git a/Source/WebCore/rendering/BidiRun.h b/Source/WebCore/rendering/BidiRun.h new file mode 100644 index 0000000..5dbb07b --- /dev/null +++ b/Source/WebCore/rendering/BidiRun.h @@ -0,0 +1,67 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef BidiRun_h +#define BidiRun_h + +#include +#include "BidiResolver.h" +#include "RenderText.h" + +namespace WebCore { + +class BidiContext; +class InlineBox; + +struct BidiRun : BidiCharacterRun { + BidiRun(int start, int stop, RenderObject* object, BidiContext* context, WTF::Unicode::Direction dir) + : BidiCharacterRun(start, stop, context, dir) + , m_object(object) + , m_box(0) + , m_hasHyphen(false) + { + } + + void destroy(); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + BidiRun* next() { return static_cast(m_next); } + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: + RenderObject* m_object; + InlineBox* m_box; + bool m_hasHyphen; +}; + +} + +#endif // BidiRun_h diff --git a/Source/WebCore/rendering/ColumnInfo.h b/Source/WebCore/rendering/ColumnInfo.h new file mode 100644 index 0000000..5e6f619 --- /dev/null +++ b/Source/WebCore/rendering/ColumnInfo.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ColumnInfo_h +#define ColumnInfo_h + +#include +#include "IntRect.h" + +namespace WebCore { + +class ColumnInfo : public Noncopyable { +public: + ColumnInfo() + : m_desiredColumnWidth(0) + , m_desiredColumnCount(1) + , m_columnCount(1) + , m_columnHeight(0) + , m_minimumColumnHeight(0) + , m_forcedBreaks(0) + , m_maximumDistanceBetweenForcedBreaks(0) + , m_forcedBreakOffset(0) + { } + + int desiredColumnWidth() const { return m_desiredColumnWidth; } + void setDesiredColumnWidth(int width) { m_desiredColumnWidth = width; } + + unsigned desiredColumnCount() const { return m_desiredColumnCount; } + void setDesiredColumnCount(unsigned count) { m_desiredColumnCount = count; } + + unsigned columnCount() const { return m_columnCount; } + int columnHeight() const { return m_columnHeight; } + + // Set our count and height. This is enough info for a RenderBlock to compute page rects + // dynamically. + void setColumnCountAndHeight(int count, int height) + { + m_columnCount = count; + m_columnHeight = height; + } + void setColumnHeight(int height) { m_columnHeight = height; } + + void updateMinimumColumnHeight(int height) { m_minimumColumnHeight = std::max(height, m_minimumColumnHeight); } + int minimumColumnHeight() const { return m_minimumColumnHeight; } + + int forcedBreaks() const { return m_forcedBreaks; } + int forcedBreakOffset() const { return m_forcedBreakOffset; } + int maximumDistanceBetweenForcedBreaks() const { return m_maximumDistanceBetweenForcedBreaks; } + void clearForcedBreaks() + { + m_forcedBreaks = 0; + m_maximumDistanceBetweenForcedBreaks = 0; + m_forcedBreakOffset = 0; + } + void addForcedBreak(int offsetFromFirstPage) + { + ASSERT(!m_columnHeight); + int distanceFromLastBreak = offsetFromFirstPage - m_forcedBreakOffset; + if (!distanceFromLastBreak) + return; + m_forcedBreaks++; + m_maximumDistanceBetweenForcedBreaks = std::max(m_maximumDistanceBetweenForcedBreaks, distanceFromLastBreak); + m_forcedBreakOffset = offsetFromFirstPage; + } + +private: + int m_desiredColumnWidth; + unsigned m_desiredColumnCount; + + unsigned m_columnCount; + int m_columnHeight; + int m_minimumColumnHeight; + int m_forcedBreaks; // FIXME: We will ultimately need to cache more information to balance around forced breaks properly. + int m_maximumDistanceBetweenForcedBreaks; + int m_forcedBreakOffset; +}; + +} + +#endif diff --git a/Source/WebCore/rendering/CounterNode.cpp b/Source/WebCore/rendering/CounterNode.cpp new file mode 100644 index 0000000..ac83d5a --- /dev/null +++ b/Source/WebCore/rendering/CounterNode.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "CounterNode.h" + +#include "RenderCounter.h" +#include "RenderObject.h" +#include + +namespace WebCore { + +CounterNode::CounterNode(RenderObject* o, bool hasResetType, int value) + : m_hasResetType(hasResetType) + , m_value(value) + , m_countInParent(0) + , m_renderer(o) + , m_parent(0) + , m_previousSibling(0) + , m_nextSibling(0) + , m_firstChild(0) + , m_lastChild(0) +{ +} + +PassRefPtr CounterNode::create(RenderObject* renderer, bool hasResetType, int value) +{ + return adoptRef(new CounterNode(renderer, hasResetType, value)); +} + +CounterNode* CounterNode::nextInPreOrderAfterChildren(const CounterNode* stayWithin) const +{ + if (this == stayWithin) + return 0; + + const CounterNode* current = this; + CounterNode* next; + while (!(next = current->m_nextSibling)) { + current = current->m_parent; + if (!current || current == stayWithin) + return 0; + } + return next; +} + +CounterNode* CounterNode::nextInPreOrder(const CounterNode* stayWithin) const +{ + if (CounterNode* next = m_firstChild) + return next; + + return nextInPreOrderAfterChildren(stayWithin); +} + +CounterNode* CounterNode::lastDescendant() const +{ + CounterNode* last = m_lastChild; + if (!last) + return 0; + + while (CounterNode* lastChild = last->m_lastChild) + last = lastChild; + + return last; +} + +CounterNode* CounterNode::previousInPreOrder() const +{ + CounterNode* previous = m_previousSibling; + if (!previous) + return m_parent; + + while (CounterNode* lastChild = previous->m_lastChild) + previous = lastChild; + + return previous; +} + +int CounterNode::computeCountInParent() const +{ + int increment = actsAsReset() ? 0 : m_value; + if (m_previousSibling) + return m_previousSibling->m_countInParent + increment; + ASSERT(m_parent->m_firstChild == this); + return m_parent->m_value + increment; +} + +void CounterNode::resetRenderer(const AtomicString& identifier) const +{ + if (!m_renderer || m_renderer->documentBeingDestroyed()) + return; + if (RenderObjectChildList* children = m_renderer->virtualChildren()) + children->invalidateCounters(m_renderer, identifier); +} + +void CounterNode::resetRenderers(const AtomicString& identifier) const +{ + const CounterNode* node = this; + do { + node->resetRenderer(identifier); + node = node->nextInPreOrder(this); + } while (node); +} + +void CounterNode::recount(const AtomicString& identifier) +{ + for (CounterNode* node = this; node; node = node->m_nextSibling) { + int oldCount = node->m_countInParent; + int newCount = node->computeCountInParent(); + if (oldCount == newCount) + break; + node->m_countInParent = newCount; + node->resetRenderers(identifier); + } +} + +void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild, const AtomicString& identifier) +{ + ASSERT(newChild); + ASSERT(!newChild->m_parent); + ASSERT(!newChild->m_previousSibling); + ASSERT(!newChild->m_nextSibling); + ASSERT(!refChild || refChild->m_parent == this); + + if (newChild->m_hasResetType) { + while (m_lastChild != refChild) + RenderCounter::destroyCounterNode(m_lastChild->renderer(), identifier); + } + + CounterNode* next; + + if (refChild) { + next = refChild->m_nextSibling; + refChild->m_nextSibling = newChild; + } else { + next = m_firstChild; + m_firstChild = newChild; + } + + newChild->m_parent = this; + newChild->m_previousSibling = refChild; + + if (!newChild->m_firstChild || newChild->m_hasResetType) { + newChild->m_nextSibling = next; + if (next) { + ASSERT(next->m_previousSibling == refChild); + next->m_previousSibling = newChild; + } else { + ASSERT(m_lastChild == refChild); + m_lastChild = newChild; + } + + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderers(identifier); + if (next) + next->recount(identifier); + return; + } + + // The code below handles the case when a formerly root increment counter is loosing its root position + // and therefore its children become next siblings. + CounterNode* last = newChild->m_lastChild; + CounterNode* first = newChild->m_firstChild; + + newChild->m_nextSibling = first; + first->m_previousSibling = newChild; + // The case when the original next sibling of the inserted node becomes a child of + // one of the former children of the inserted node is not handled as it is believed + // to be impossible since: + // 1. if the increment counter node lost it's root position as a result of another + // counter node being created, it will be inserted as the last child so next is null. + // 2. if the increment counter node lost it's root position as a result of a renderer being + // inserted into the document's render tree, all its former children counters are attached + // to children of the inserted renderer and hence cannot be in scope for counter nodes + // attached to renderers that were already in the document's render tree. + last->m_nextSibling = next; + if (next) + next->m_previousSibling = last; + else + m_lastChild = last; + for (next = first; ; next = next->m_nextSibling) { + next->m_parent = this; + if (last == next) + break; + } + newChild->m_firstChild = 0; + newChild->m_lastChild = 0; + newChild->m_countInParent = newChild->computeCountInParent(); + newChild->resetRenderer(identifier); + first->recount(identifier); +} + +void CounterNode::removeChild(CounterNode* oldChild, const AtomicString& identifier) +{ + ASSERT(oldChild); + ASSERT(!oldChild->m_firstChild); + ASSERT(!oldChild->m_lastChild); + + CounterNode* next = oldChild->m_nextSibling; + CounterNode* previous = oldChild->m_previousSibling; + + oldChild->m_nextSibling = 0; + oldChild->m_previousSibling = 0; + oldChild->m_parent = 0; + + if (previous) + previous->m_nextSibling = next; + else { + ASSERT(m_firstChild == oldChild); + m_firstChild = next; + } + + if (next) + next->m_previousSibling = previous; + else { + ASSERT(m_lastChild == oldChild); + m_lastChild = previous; + } + + if (next) + next->recount(identifier); +} + +#ifndef NDEBUG + +static void showTreeAndMark(const CounterNode* node) +{ + const CounterNode* root = node; + while (root->parent()) + root = root->parent(); + + for (const CounterNode* current = root; current; current = current->nextInPreOrder()) { + fwrite((current == node) ? "*" : " ", 1, 1, stderr); + for (const CounterNode* parent = current; parent && parent != root; parent = parent->parent()) + fwrite(" ", 1, 2, stderr); + fprintf(stderr, "%p %s: %d %d P:%p PS:%p NS:%p R:%p\n", + current, current->actsAsReset() ? "reset____" : "increment", current->value(), + current->countInParent(), current->parent(), current->previousSibling(), + current->nextSibling(), current->renderer()); + } +} + +#endif + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::CounterNode* counter) +{ + if (counter) + showTreeAndMark(counter); +} + +#endif diff --git a/Source/WebCore/rendering/CounterNode.h b/Source/WebCore/rendering/CounterNode.h new file mode 100644 index 0000000..529d409 --- /dev/null +++ b/Source/WebCore/rendering/CounterNode.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ + +#ifndef CounterNode_h +#define CounterNode_h + +#include +#include +#include + +// This implements a counter tree that is used for finding parents in counters() lookup, +// and for propagating count changes when nodes are added or removed. + +// Parents represent unique counters and their scope, which are created either explicitly +// by "counter-reset" style rules or implicitly by referring to a counter that is not in scope. +// Such nodes are tagged as "reset" nodes, although they are not all due to "counter-reset". + +// Not that render tree children are often counter tree siblings due to counter scoping rules. + +namespace WebCore { + +class RenderObject; + +class CounterNode : public RefCounted { +public: + static PassRefPtr create(RenderObject*, bool isReset, int value); + + bool actsAsReset() const { return m_hasResetType || !m_parent; } + bool hasResetType() const { return m_hasResetType; } + int value() const { return m_value; } + int countInParent() const { return m_countInParent; } + RenderObject* renderer() const { return m_renderer; } + + CounterNode* parent() const { return m_parent; } + CounterNode* previousSibling() const { return m_previousSibling; } + CounterNode* nextSibling() const { return m_nextSibling; } + CounterNode* firstChild() const { return m_firstChild; } + CounterNode* lastChild() const { return m_lastChild; } + CounterNode* lastDescendant() const; + CounterNode* previousInPreOrder() const; + CounterNode* nextInPreOrder(const CounterNode* stayWithin = 0) const; + CounterNode* nextInPreOrderAfterChildren(const CounterNode* stayWithin = 0) const; + + void insertAfter(CounterNode* newChild, CounterNode* beforeChild, const AtomicString& identifier); + + // identifier must match the identifier of this counter. + void removeChild(CounterNode*, const AtomicString& identifier); + +private: + CounterNode(RenderObject*, bool isReset, int value); + int computeCountInParent() const; + void recount(const AtomicString& identifier); + + // Invalidates the text in the renderer of this counter, if any. + // identifier must match the identifier of this counter. + void resetRenderer(const AtomicString& identifier) const; + + // Invalidates the text in the renderer of this counter, if any, + // and in the renderers of all descendants of this counter, if any. + // identifier must match the identifier of this counter. + void resetRenderers(const AtomicString& identifier) const; + + bool m_hasResetType; + int m_value; + int m_countInParent; + RenderObject* m_renderer; + + CounterNode* m_parent; + CounterNode* m_previousSibling; + CounterNode* m_nextSibling; + CounterNode* m_firstChild; + CounterNode* m_lastChild; +}; + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::CounterNode*); +#endif + +#endif // CounterNode_h diff --git a/Source/WebCore/rendering/EllipsisBox.cpp b/Source/WebCore/rendering/EllipsisBox.cpp new file mode 100644 index 0000000..f9c4f03 --- /dev/null +++ b/Source/WebCore/rendering/EllipsisBox.cpp @@ -0,0 +1,126 @@ +/** + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "EllipsisBox.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "RootInlineBox.h" + +namespace WebCore { + +void EllipsisBox::paint(PaintInfo& paintInfo, int tx, int ty) +{ + GraphicsContext* context = paintInfo.context; + RenderStyle* style = m_renderer->style(m_firstLine); + Color textColor = style->visitedDependentColor(CSSPropertyColor); + if (textColor != context->fillColor()) + context->setFillColor(textColor, style->colorSpace()); + bool setShadow = false; + if (style->textShadow()) { + context->setShadow(IntSize(style->textShadow()->x(), style->textShadow()->y()), + style->textShadow()->blur(), style->textShadow()->color(), style->colorSpace()); + setShadow = true; + } + + if (selectionState() != RenderObject::SelectionNone) { + paintSelection(context, tx, ty, style, style->font()); + + // Select the correct color for painting the text. + Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + if (foreground.isValid() && foreground != textColor) + context->setFillColor(foreground, style->colorSpace()); + } + + const String& str = m_str; + context->drawText(style->font(), TextRun(str.characters(), str.length(), false, 0, 0, false, style->visuallyOrdered()), IntPoint(m_x + tx, m_y + ty + style->font().ascent())); + + // Restore the regular fill color. + if (textColor != context->fillColor()) + context->setFillColor(textColor, style->colorSpace()); + + if (setShadow) + context->clearShadow(); + + if (m_markupBox) { + // Paint the markup box + tx += m_x + m_logicalWidth - m_markupBox->x(); + ty += m_y + style->font().ascent() - (m_markupBox->y() + m_markupBox->renderer()->style(m_firstLine)->font().ascent()); + m_markupBox->paint(paintInfo, tx, ty); + } +} + +IntRect EllipsisBox::selectionRect(int tx, int ty) +{ + RenderStyle* style = m_renderer->style(m_firstLine); + const Font& f = style->font(); + return enclosingIntRect(f.selectionRectForText(TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + root()->selectionTop()), root()->selectionHeight())); +} + +void EllipsisBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font& font) +{ + Color textColor = style->visitedDependentColor(CSSPropertyColor); + Color c = m_renderer->selectionBackgroundColor(); + if (!c.isValid() || !c.alpha()) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + context->save(); + int y = root()->selectionTop(); + int h = root()->selectionHeight(); + context->clip(IntRect(m_x + tx, y + ty, m_logicalWidth, h)); + context->drawHighlightForText(font, TextRun(m_str.characters(), m_str.length(), false, 0, 0, false, style->visuallyOrdered()), + IntPoint(m_x + tx, m_y + ty + y), h, c, style->colorSpace()); + context->restore(); +} + +bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + tx += m_x; + ty += m_y; + + // Hit test the markup box. + if (m_markupBox) { + RenderStyle* style = m_renderer->style(m_firstLine); + int mtx = tx + m_logicalWidth - m_markupBox->x(); + int mty = ty + style->font().ascent() - (m_markupBox->y() + m_markupBox->renderer()->style(m_firstLine)->font().ascent()); + if (m_markupBox->nodeAtPoint(request, result, x, y, mtx, mty)) { + renderer()->updateHitTestResult(result, IntPoint(x - mtx, y - mty)); + return true; + } + } + + IntRect boundsRect = IntRect(tx, ty, m_logicalWidth, m_height); + if (visibleToHitTesting() && boundsRect.intersects(result.rectForPoint(x, y))) { + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, boundsRect)) + return true; + } + + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/EllipsisBox.h b/Source/WebCore/rendering/EllipsisBox.h new file mode 100644 index 0000000..ec1b00b --- /dev/null +++ b/Source/WebCore/rendering/EllipsisBox.h @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef EllipsisBox_h +#define EllipsisBox_h + +#include "InlineBox.h" + +namespace WebCore { + +class HitTestRequest; +class HitTestResult; + +class EllipsisBox : public InlineBox { +public: + EllipsisBox(RenderObject* obj, const AtomicString& ellipsisStr, InlineFlowBox* parent, + int width, int height, int y, bool firstLine, bool isVertical, InlineBox* markupBox) + : InlineBox(obj, 0, y, width, firstLine, true, false, false, isVertical, 0, 0, parent) + , m_height(height) + , m_str(ellipsisStr) + , m_markupBox(markupBox) + , m_selectionState(RenderObject::SelectionNone) + { + } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + void setSelectionState(RenderObject::SelectionState s) { m_selectionState = s; } + IntRect selectionRect(int tx, int ty); + +private: + virtual int height() const { return m_height; } + virtual RenderObject::SelectionState selectionState() { return m_selectionState; } + void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font&); + + int m_height; + AtomicString m_str; + InlineBox* m_markupBox; + RenderObject::SelectionState m_selectionState; +}; + +} // namespace WebCore + +#endif // EllipsisBox_h diff --git a/Source/WebCore/rendering/FixedTableLayout.cpp b/Source/WebCore/rendering/FixedTableLayout.cpp new file mode 100644 index 0000000..3285d15 --- /dev/null +++ b/Source/WebCore/rendering/FixedTableLayout.cpp @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "FixedTableLayout.h" + +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableSection.h" + +/* + The text below is from the CSS 2.1 specs. + + Fixed table layout + + With this (fast) algorithm, the horizontal layout of the table does + not depend on the contents of the cells; it only depends on the + table's width, the width of the columns, and borders or cell + spacing. + + The table's width may be specified explicitly with the 'width' + property. A value of 'auto' (for both 'display: table' and 'display: + inline-table') means use the automatic table layout algorithm. + + In the fixed table layout algorithm, the width of each column is + determined as follows: + + 1. A column element with a value other than 'auto' for the 'width' + property sets the width for that column. + + 2. Otherwise, a cell in the first row with a value other than + 'auto' for the 'width' property sets the width for that column. If + the cell spans more than one column, the width is divided over the + columns. + + 3. Any remaining columns equally divide the remaining horizontal + table space (minus borders or cell spacing). + + The width of the table is then the greater of the value of the + 'width' property for the table element and the sum of the column + widths (plus cell spacing or borders). If the table is wider than + the columns, the extra space should be distributed over the columns. + + + In this manner, the user agent can begin to lay out the table once + the entire first row has been received. Cells in subsequent rows do + not affect column widths. Any cell that has content that overflows + uses the 'overflow' property to determine whether to clip the + overflow content. +*/ + +using namespace std; + +namespace WebCore { + +FixedTableLayout::FixedTableLayout(RenderTable* table) + : TableLayout(table) +{ +} + +int FixedTableLayout::calcWidthArray(int) +{ + int usedWidth = 0; + + // iterate over all elements + RenderObject* child = m_table->firstChild(); + int nEffCols = m_table->numEffCols(); + m_width.resize(nEffCols); + m_width.fill(Length(Auto)); + + int currentEffectiveColumn = 0; + Length grpWidth; + while (child && child->isTableCol()) { + RenderTableCol* col = toRenderTableCol(child); + if (col->firstChild()) + grpWidth = col->style()->logicalWidth(); + else { + Length w = col->style()->logicalWidth(); + if (w.isAuto()) + w = grpWidth; + int effWidth = 0; + if (w.isFixed() && w.value() > 0) + effWidth = w.value(); + + int span = col->span(); + while (span) { + int spanInCurrentEffectiveColumn; + if (currentEffectiveColumn >= nEffCols) { + m_table->appendColumn(span); + nEffCols++; + m_width.append(Length()); + spanInCurrentEffectiveColumn = span; + } else { + if (span < m_table->spanOfEffCol(currentEffectiveColumn)) { + m_table->splitColumn(currentEffectiveColumn, span); + nEffCols++; + m_width.append(Length()); + } + spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn); + } + if ((w.isFixed() || w.isPercent()) && w.isPositive()) { + m_width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn); + usedWidth += effWidth * spanInCurrentEffectiveColumn; + } + span -= spanInCurrentEffectiveColumn; + currentEffectiveColumn++; + } + } + col->computePreferredLogicalWidths(); + + RenderObject* next = child->firstChild(); + if (!next) + next = child->nextSibling(); + if (!next && child->parent()->isTableCol()) { + next = child->parent()->nextSibling(); + grpWidth = Length(); + } + child = next; + } + + // Iterate over the first row in case some are unspecified. + RenderTableSection* section = m_table->header(); + if (!section) + section = m_table->firstBody(); + if (!section) + section = m_table->footer(); + if (section && !section->numRows()) + section = m_table->sectionBelow(section, true); + if (section) { + int cCol = 0; + RenderObject* firstRow = section->firstChild(); + child = firstRow->firstChild(); + while (child) { + if (child->isTableCell()) { + RenderTableCell* cell = toRenderTableCell(child); + if (cell->preferredLogicalWidthsDirty()) + cell->computePreferredLogicalWidths(); + + Length w = cell->styleOrColLogicalWidth(); + int span = cell->colSpan(); + int effWidth = 0; + if (w.isFixed() && w.isPositive()) + effWidth = w.value(); + + int usedSpan = 0; + int i = 0; + while (usedSpan < span && cCol + i < nEffCols) { + int eSpan = m_table->spanOfEffCol(cCol + i); + // Only set if no col element has already set it. + if (m_width[cCol + i].isAuto() && w.type() != Auto) { + m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span); + usedWidth += effWidth * eSpan / span; + } + usedSpan += eSpan; + i++; + } + cCol += i; + } + child = child->nextSibling(); + } + } + + return usedWidth; +} + +// Use a very large value (in effect infinite). But not too large! +// numeric_limits::max() will too easily overflow widths. +// Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp +#define TABLE_MAX_WIDTH 15000 + +void FixedTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth) +{ + // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. + + // we might want to wait until we have all of the first row before + // layouting for the first time. + + // only need to calculate the minimum width as the sum of the + // cols/cells with a fixed width. + // + // The maximum width is max(minWidth, tableWidth). + int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection(); + + int tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0; + int mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing; + + minWidth = max(mw, tableLogicalWidth); + maxWidth = minWidth; + + // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths(). + // Here's the example for this one: + /* +
+
+
+ Content +
+
+
+ */ + // In this example, the two inner tables should be as large as the outer table. + // We can achieve this effect by making the maxwidth of fixed tables with percentage + // widths be infinite. + if (m_table->document()->inQuirksMode() && m_table->style()->logicalWidth().isPercent() && maxWidth < TABLE_MAX_WIDTH) + maxWidth = TABLE_MAX_WIDTH; +} + +void FixedTableLayout::layout() +{ + int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); + int nEffCols = m_table->numEffCols(); + Vector calcWidth(nEffCols, 0); + + int numAuto = 0; + int autoSpan = 0; + int totalFixedWidth = 0; + int totalPercentWidth = 0; + int totalRawPercent = 0; + + // Compute requirements and try to satisfy fixed and percent widths. + // Percentages are of the table's width, so for example + // for a table width of 100px with columns (40px, 10%), the 10% compute + // to 10px here, and will scale up to 20px in the final (80px, 20px). + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isFixed()) { + calcWidth[i] = m_width[i].value(); + totalFixedWidth += calcWidth[i]; + } else if (m_width[i].isPercent()) { + calcWidth[i] = m_width[i].calcValue(tableLogicalWidth); + totalPercentWidth += calcWidth[i]; + totalRawPercent += m_width[i].rawValue(); + } else if (m_width[i].isAuto()) { + numAuto++; + autoSpan += m_table->spanOfEffCol(i); + } + } + + int hspacing = m_table->hBorderSpacing(); + int totalWidth = totalFixedWidth + totalPercentWidth; + if (!numAuto || totalWidth > tableLogicalWidth) { + // If there are no auto columns, or if the total is too wide, take + // what we have and scale it to fit as necessary. + if (totalWidth != tableLogicalWidth) { + // Fixed widths only scale up + if (totalFixedWidth && totalWidth < tableLogicalWidth) { + totalFixedWidth = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isFixed()) { + calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth; + totalFixedWidth += calcWidth[i]; + } + } + } + if (totalRawPercent) { + totalPercentWidth = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isPercent()) { + calcWidth[i] = m_width[i].rawValue() * (tableLogicalWidth - totalFixedWidth) / totalRawPercent; + totalPercentWidth += calcWidth[i]; + } + } + } + totalWidth = totalFixedWidth + totalPercentWidth; + } + } else { + // Divide the remaining width among the auto columns. + int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); + int lastAuto = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isAuto()) { + int span = m_table->spanOfEffCol(i); + int w = remainingWidth * span / autoSpan; + calcWidth[i] = w + hspacing * (span - 1); + remainingWidth -= w; + if (!remainingWidth) + break; + lastAuto = i; + numAuto--; + autoSpan -= span; + } + } + // Last one gets the remainder. + if (remainingWidth) + calcWidth[lastAuto] += remainingWidth; + totalWidth = tableLogicalWidth; + } + + if (totalWidth < tableLogicalWidth) { + // Spread extra space over columns. + int remainingWidth = tableLogicalWidth - totalWidth; + int total = nEffCols; + while (total) { + int w = remainingWidth / total; + remainingWidth -= w; + calcWidth[--total] += w; + } + if (nEffCols > 0) + calcWidth[nEffCols - 1] += remainingWidth; + } + + int pos = 0; + for (int i = 0; i < nEffCols; i++) { + m_table->columnPositions()[i] = pos; + pos += calcWidth[i] + hspacing; + } + int colPositionsSize = m_table->columnPositions().size(); + if (colPositionsSize > 0) + m_table->columnPositions()[colPositionsSize - 1] = pos; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/FixedTableLayout.h b/Source/WebCore/rendering/FixedTableLayout.h new file mode 100644 index 0000000..6c135c0 --- /dev/null +++ b/Source/WebCore/rendering/FixedTableLayout.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef FixedTableLayout_h +#define FixedTableLayout_h + +#include "Length.h" +#include "TableLayout.h" +#include + +namespace WebCore { + +class RenderTable; + +class FixedTableLayout : public TableLayout { +public: + FixedTableLayout(RenderTable*); + + virtual void computePreferredLogicalWidths(int& minWidth, int& maxWidth); + virtual void layout(); + +private: + int calcWidthArray(int tableWidth); + + Vector m_width; +}; + +} // namespace WebCore + +#endif // FixedTableLayout_h diff --git a/Source/WebCore/rendering/GapRects.h b/Source/WebCore/rendering/GapRects.h new file mode 100644 index 0000000..a762ae5 --- /dev/null +++ b/Source/WebCore/rendering/GapRects.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2005, 2006 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + + Some useful definitions needed for laying out elements +*/ + +#ifndef GapRects_h +#define GapRects_h + +#include "IntRect.h" + +namespace WebCore { + + struct GapRects { + const IntRect& left() const { return m_left; } + const IntRect& center() const { return m_center; } + const IntRect& right() const { return m_right; } + + void uniteLeft(const IntRect& r) { m_left.unite(r); } + void uniteCenter(const IntRect& r) { m_center.unite(r); } + void uniteRight(const IntRect& r) { m_right.unite(r); } + void unite(const GapRects& o) { uniteLeft(o.left()); uniteCenter(o.center()); uniteRight(o.right()); } + + operator IntRect() const + { + IntRect result = m_left; + result.unite(m_center); + result.unite(m_right); + return result; + } + + bool operator==(const GapRects& other) + { + return m_left == other.left() && m_center == other.center() && m_right == other.right(); + } + bool operator!=(const GapRects& other) { return !(*this == other); } + + private: + IntRect m_left; + IntRect m_center; + IntRect m_right; + }; + +} // namespace WebCore + +#endif // GapRects_h diff --git a/Source/WebCore/rendering/HitTestRequest.h b/Source/WebCore/rendering/HitTestRequest.h new file mode 100644 index 0000000..745791a --- /dev/null +++ b/Source/WebCore/rendering/HitTestRequest.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ + +#ifndef HitTestRequest_h +#define HitTestRequest_h + +namespace WebCore { + +class HitTestRequest { +public: + enum RequestType { + ReadOnly = 1 << 1, + Active = 1 << 2, + MouseMove = 1 << 3, + MouseUp = 1 << 4, + IgnoreClipping = 1 << 5, + SVGClipContent = 1 << 6 + }; + + typedef unsigned HitTestRequestType; + + HitTestRequest(HitTestRequestType requestType) + : m_requestType(requestType) + { + } + + bool readOnly() const { return m_requestType & ReadOnly; } + bool active() const { return m_requestType & Active; } + bool mouseMove() const { return m_requestType & MouseMove; } + bool mouseUp() const { return m_requestType & MouseUp; } + bool ignoreClipping() const { return m_requestType & IgnoreClipping; } + bool svgClipContent() const { return m_requestType & SVGClipContent; } + +private: + HitTestRequestType m_requestType; +}; + +} // namespace WebCore + +#endif // HitTestRequest_h diff --git a/Source/WebCore/rendering/HitTestResult.cpp b/Source/WebCore/rendering/HitTestResult.cpp new file mode 100644 index 0000000..5d9b3df --- /dev/null +++ b/Source/WebCore/rendering/HitTestResult.cpp @@ -0,0 +1,579 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ + +#include "config.h" +#include "HitTestResult.h" + +#include "DocumentMarkerController.h" +#include "Frame.h" +#include "FrameTree.h" +#include "HTMLAnchorElement.h" +#include "HTMLVideoElement.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "RenderImage.h" +#include "Scrollbar.h" +#include "SelectionController.h" + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "XLinkNames.h" +#endif + +#if ENABLE(WML) +#include "WMLImageElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +HitTestResult::HitTestResult() + : m_isOverWidget(false) + , m_isRectBased(false) + , m_topPadding(0) + , m_rightPadding(0) + , m_bottomPadding(0) + , m_leftPadding(0) +{ +} + +HitTestResult::HitTestResult(const IntPoint& point) + : m_point(point) + , m_isOverWidget(false) + , m_isRectBased(false) + , m_topPadding(0) + , m_rightPadding(0) + , m_bottomPadding(0) + , m_leftPadding(0) +{ +} + +HitTestResult::HitTestResult(const IntPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) + : m_point(centerPoint) + , m_isOverWidget(false) + , m_topPadding(topPadding) + , m_rightPadding(rightPadding) + , m_bottomPadding(bottomPadding) + , m_leftPadding(leftPadding) +{ + // If all padding values passed in are zero then it is not a rect based hit test. + m_isRectBased = topPadding || rightPadding || bottomPadding || leftPadding; + + // Make sure all padding values are clamped to zero if it is not a rect hit test. + if (!m_isRectBased) + m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0; +} + +HitTestResult::HitTestResult(const HitTestResult& other) + : m_innerNode(other.innerNode()) + , m_innerNonSharedNode(other.innerNonSharedNode()) + , m_point(other.point()) + , m_localPoint(other.localPoint()) + , m_innerURLElement(other.URLElement()) + , m_scrollbar(other.scrollbar()) + , m_isOverWidget(other.isOverWidget()) +{ + // Only copy the padding and ListHashSet in case of rect hit test. + // Copying the later is rather expensive. + if ((m_isRectBased = other.isRectBasedTest())) { + m_topPadding = other.m_topPadding; + m_rightPadding = other.m_rightPadding; + m_bottomPadding = other.m_bottomPadding; + m_leftPadding = other.m_leftPadding; + m_rectBasedTestResult = other.rectBasedTestResult(); + } else + m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0; +} + +HitTestResult::~HitTestResult() +{ +} + +HitTestResult& HitTestResult::operator=(const HitTestResult& other) +{ + m_innerNode = other.innerNode(); + m_innerNonSharedNode = other.innerNonSharedNode(); + m_point = other.point(); + m_localPoint = other.localPoint(); + m_innerURLElement = other.URLElement(); + m_scrollbar = other.scrollbar(); + m_isOverWidget = other.isOverWidget(); + // Only copy the padding and ListHashSet in case of rect hit test. + // Copying the later is rather expensive. + if ((m_isRectBased = other.isRectBasedTest())) { + m_topPadding = other.m_topPadding; + m_rightPadding = other.m_rightPadding; + m_bottomPadding = other.m_bottomPadding; + m_leftPadding = other.m_leftPadding; + m_rectBasedTestResult = other.rectBasedTestResult(); + } else + m_topPadding = m_rightPadding = m_bottomPadding = m_leftPadding = 0; + return *this; +} + +void HitTestResult::setToNonShadowAncestor() +{ + Node* node = innerNode(); + if (node) + node = node->shadowAncestorNode(); + setInnerNode(node); + node = innerNonSharedNode(); + if (node) + node = node->shadowAncestorNode(); + setInnerNonSharedNode(node); +} + +void HitTestResult::setInnerNode(Node* n) +{ + m_innerNode = n; +} + +void HitTestResult::setInnerNonSharedNode(Node* n) +{ + m_innerNonSharedNode = n; +} + +void HitTestResult::setURLElement(Element* n) +{ + m_innerURLElement = n; +} + +void HitTestResult::setScrollbar(Scrollbar* s) +{ + m_scrollbar = s; +} + +Frame* HitTestResult::targetFrame() const +{ + if (!m_innerURLElement) + return 0; + + Frame* frame = m_innerURLElement->document()->frame(); + if (!frame) + return 0; + + return frame->tree()->find(m_innerURLElement->target()); +} + +bool HitTestResult::isSelected() const +{ + if (!m_innerNonSharedNode) + return false; + + Frame* frame = m_innerNonSharedNode->document()->frame(); + if (!frame) + return false; + + return frame->selection()->contains(m_point); +} + +String HitTestResult::spellingToolTip(TextDirection& dir) const +{ + dir = LTR; + // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar + // currently supply strings, but maybe someday markers associated with misspelled words will also. + if (!m_innerNonSharedNode) + return String(); + + DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Grammar); + if (!marker) + return String(); + + if (RenderObject* renderer = m_innerNonSharedNode->renderer()) + dir = renderer->style()->direction(); + return marker->description; +} + +String HitTestResult::replacedString() const +{ + // Return the replaced string associated with this point, if any. This marker is created when a string is autocorrected, + // and is used for generating a contextual menu item that allows it to easily be changed back if desired. + if (!m_innerNonSharedNode) + return String(); + + DocumentMarker* marker = m_innerNonSharedNode->document()->markers()->markerContainingPoint(m_point, DocumentMarker::Replacement); + if (!marker) + return String(); + + return marker->description; +} + +String HitTestResult::title(TextDirection& dir) const +{ + dir = LTR; + // Find the title in the nearest enclosing DOM node. + // For tags in image maps, walk the tree for the , not the using it. + for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) { + if (titleNode->isElementNode()) { + String title = static_cast(titleNode)->title(); + if (!title.isEmpty()) { + if (RenderObject* renderer = titleNode->renderer()) + dir = renderer->style()->direction(); + return title; + } + } + } + return String(); +} + +String displayString(const String& string, const Node* node) +{ + if (!node) + return string; + return node->document()->displayStringModifiedByEncoding(string); +} + +String HitTestResult::altDisplayString() const +{ + if (!m_innerNonSharedNode) + return String(); + + if (m_innerNonSharedNode->hasTagName(imgTag)) { + HTMLImageElement* image = static_cast(m_innerNonSharedNode.get()); + return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get()); + } + + if (m_innerNonSharedNode->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast(m_innerNonSharedNode.get()); + return displayString(input->alt(), m_innerNonSharedNode.get()); + } + +#if ENABLE(WML) + if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) { + WMLImageElement* image = static_cast(m_innerNonSharedNode.get()); + return displayString(image->altText(), m_innerNonSharedNode.get()); + } +#endif + + return String(); +} + +Image* HitTestResult::image() const +{ + if (!m_innerNonSharedNode) + return 0; + + RenderObject* renderer = m_innerNonSharedNode->renderer(); + if (renderer && renderer->isImage()) { + RenderImage* image = static_cast(renderer); + if (image->cachedImage() && !image->cachedImage()->errorOccurred()) + return image->cachedImage()->image(); + } + + return 0; +} + +IntRect HitTestResult::imageRect() const +{ + if (!image()) + return IntRect(); + return m_innerNonSharedNode->renderBox()->absoluteContentQuad().enclosingBoundingBox(); +} + +KURL HitTestResult::absoluteImageURL() const +{ + if (!(m_innerNonSharedNode && m_innerNonSharedNode->document())) + return KURL(); + + if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage())) + return KURL(); + + AtomicString urlString; + if (m_innerNonSharedNode->hasTagName(embedTag) + || m_innerNonSharedNode->hasTagName(imgTag) + || m_innerNonSharedNode->hasTagName(inputTag) + || m_innerNonSharedNode->hasTagName(objectTag) +#if ENABLE(SVG) + || m_innerNonSharedNode->hasTagName(SVGNames::imageTag) +#endif +#if ENABLE(WML) + || m_innerNonSharedNode->hasTagName(WMLNames::imgTag) +#endif + ) { + Element* element = static_cast(m_innerNonSharedNode.get()); + urlString = element->getAttribute(element->imageSourceAttributeName()); + } else + return KURL(); + + return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); +} + +KURL HitTestResult::absoluteMediaURL() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return m_innerNonSharedNode->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(mediaElt->currentSrc())); + return KURL(); +#else + return KURL(); +#endif +} + +bool HitTestResult::mediaSupportsFullscreen() const +{ +#if ENABLE(VIDEO) + HTMLMediaElement* mediaElt(mediaElement()); + return (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag) && mediaElt->supportsFullscreen()); +#else + return false; +#endif +} + +#if ENABLE(VIDEO) +HTMLMediaElement* HitTestResult::mediaElement() const +{ + if (!(m_innerNonSharedNode && m_innerNonSharedNode->document())) + return 0; + + if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isMedia())) + return 0; + + if (m_innerNonSharedNode->hasTagName(HTMLNames::videoTag) || m_innerNonSharedNode->hasTagName(HTMLNames::audioTag)) + return static_cast(m_innerNonSharedNode.get()); + return 0; +} +#endif + +void HitTestResult::toggleMediaControlsDisplay() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + mediaElt->setControls(!mediaElt->controls()); +#endif +} + +void HitTestResult::toggleMediaLoopPlayback() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + mediaElt->setLoop(!mediaElt->loop()); +#endif +} + +void HitTestResult::enterFullscreenForVideo() const +{ +#if ENABLE(VIDEO) + HTMLMediaElement* mediaElt(mediaElement()); + if (mediaElt && mediaElt->hasTagName(HTMLNames::videoTag)) { + HTMLVideoElement* videoElt = static_cast(mediaElt); + if (!videoElt->isFullscreen() && mediaElt->supportsFullscreen()) + videoElt->enterFullscreen(); + } +#endif +} + +bool HitTestResult::mediaControlsEnabled() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return mediaElt->controls(); +#endif + return false; +} + +bool HitTestResult::mediaLoopEnabled() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return mediaElt->loop(); +#endif + return false; +} + +bool HitTestResult::mediaPlaying() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return !mediaElt->paused(); +#endif + return false; +} + +void HitTestResult::toggleMediaPlayState() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + mediaElt->togglePlayState(); +#endif +} + +bool HitTestResult::mediaHasAudio() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return mediaElt->hasAudio(); +#endif + return false; +} + +bool HitTestResult::mediaIsVideo() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return mediaElt->hasTagName(HTMLNames::videoTag); +#endif + return false; +} + +bool HitTestResult::mediaMuted() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + return mediaElt->muted(); +#endif + return false; +} + +void HitTestResult::toggleMediaMuteState() const +{ +#if ENABLE(VIDEO) + if (HTMLMediaElement* mediaElt = mediaElement()) + mediaElt->setMuted(!mediaElt->muted()); +#endif +} + +KURL HitTestResult::absoluteLinkURL() const +{ + if (!(m_innerURLElement && m_innerURLElement->document())) + return KURL(); + + AtomicString urlString; + if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag)) + urlString = m_innerURLElement->getAttribute(hrefAttr); +#if ENABLE(SVG) + else if (m_innerURLElement->hasTagName(SVGNames::aTag)) + urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr); +#endif +#if ENABLE(WML) + else if (m_innerURLElement->hasTagName(WMLNames::aTag)) + urlString = m_innerURLElement->getAttribute(hrefAttr); +#endif + else + return KURL(); + + return m_innerURLElement->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); +} + +bool HitTestResult::isLiveLink() const +{ + if (!(m_innerURLElement && m_innerURLElement->document())) + return false; + + if (m_innerURLElement->hasTagName(aTag)) + return static_cast(m_innerURLElement.get())->isLiveLink(); +#if ENABLE(SVG) + if (m_innerURLElement->hasTagName(SVGNames::aTag)) + return m_innerURLElement->isLink(); +#endif +#if ENABLE(WML) + if (m_innerURLElement->hasTagName(WMLNames::aTag)) + return m_innerURLElement->isLink(); +#endif + + return false; +} + +String HitTestResult::titleDisplayString() const +{ + if (!m_innerURLElement) + return String(); + + return displayString(m_innerURLElement->title(), m_innerURLElement.get()); +} + +String HitTestResult::textContent() const +{ + if (!m_innerURLElement) + return String(); + return m_innerURLElement->textContent(); +} + +// FIXME: This function needs a better name and may belong in a different class. It's not +// really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this +// function would make more sense in the ContextMenu class, except that WebElementDictionary +// hooks into it. Anyway, we should architect this better. +bool HitTestResult::isContentEditable() const +{ + if (!m_innerNonSharedNode) + return false; + + if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag)) + return true; + + if (m_innerNonSharedNode->hasTagName(inputTag)) + return static_cast(m_innerNonSharedNode.get())->isTextField(); + + return m_innerNonSharedNode->isContentEditable(); +} + +bool HitTestResult::addNodeToRectBasedTestResult(Node* node, int x, int y, const IntRect& rect) +{ + // If it is not a rect-based hit test, this method has to be no-op. + // Return false, so the hit test stops. + if (!isRectBasedTest()) + return false; + + // If node is null, return true so the hit test can continue. + if (!node) + return true; + + node = node->shadowAncestorNode(); + m_rectBasedTestResult.add(node); + + return !rect.contains(rectForPoint(x, y)); +} + +void HitTestResult::append(const HitTestResult& other) +{ + ASSERT(isRectBasedTest() && other.isRectBasedTest()); + + if (!m_innerNode && other.innerNode()) { + m_innerNode = other.innerNode(); + m_innerNonSharedNode = other.innerNonSharedNode(); + m_localPoint = other.localPoint(); + m_innerURLElement = other.URLElement(); + m_scrollbar = other.scrollbar(); + m_isOverWidget = other.isOverWidget(); + } + + const ListHashSet >& list = other.rectBasedTestResult(); + ListHashSet >::const_iterator last = list.end(); + for (ListHashSet >::const_iterator it = list.begin(); it != last; ++it) + m_rectBasedTestResult.add(it->get()); +} + +IntRect HitTestResult::rectForPoint(const IntPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding) +{ + IntPoint actualPoint(point); + actualPoint -= IntSize(leftPadding, topPadding); + + IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding); + // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1". + actualPadding += IntSize(1, 1); + + return IntRect(actualPoint, actualPadding); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/HitTestResult.h b/Source/WebCore/rendering/HitTestResult.h new file mode 100644 index 0000000..1545da9 --- /dev/null +++ b/Source/WebCore/rendering/HitTestResult.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * +*/ +#ifndef HitTestResult_h +#define HitTestResult_h + +#include "IntPoint.h" +#include "IntRect.h" +#include "IntSize.h" +#include "TextDirection.h" +#include +#include +#include + +namespace WebCore { + +class Element; +class Frame; +#if ENABLE(VIDEO) +class HTMLMediaElement; +#endif +class Image; +class IntRect; +class KURL; +class Node; +class Scrollbar; + +class HitTestResult { +public: + HitTestResult(); + HitTestResult(const IntPoint&); + // Pass non-negative padding values to perform a rect-based hit test. + HitTestResult(const IntPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); + HitTestResult(const HitTestResult&); + ~HitTestResult(); + HitTestResult& operator=(const HitTestResult&); + + Node* innerNode() const { return m_innerNode.get(); } + Node* innerNonSharedNode() const { return m_innerNonSharedNode.get(); } + IntPoint point() const { return m_point; } + IntPoint localPoint() const { return m_localPoint; } + Element* URLElement() const { return m_innerURLElement.get(); } + Scrollbar* scrollbar() const { return m_scrollbar.get(); } + bool isOverWidget() const { return m_isOverWidget; } + + void setToNonShadowAncestor(); + + void setInnerNode(Node*); + void setInnerNonSharedNode(Node*); + void setPoint(const IntPoint& p) { m_point = p; } + void setLocalPoint(const IntPoint& p) { m_localPoint = p; } + void setURLElement(Element*); + void setScrollbar(Scrollbar*); + void setIsOverWidget(bool b) { m_isOverWidget = b; } + + Frame* targetFrame() const; + bool isSelected() const; + String spellingToolTip(TextDirection&) const; + String replacedString() const; + String title(TextDirection&) const; + String altDisplayString() const; + String titleDisplayString() const; + Image* image() const; + IntRect imageRect() const; + KURL absoluteImageURL() const; + KURL absoluteMediaURL() const; + KURL absoluteLinkURL() const; + String textContent() const; + bool isLiveLink() const; + bool isContentEditable() const; + void toggleMediaControlsDisplay() const; + void toggleMediaLoopPlayback() const; + void enterFullscreenForVideo() const; + bool mediaControlsEnabled() const; + bool mediaLoopEnabled() const; + bool mediaPlaying() const; + bool mediaSupportsFullscreen() const; + void toggleMediaPlayState() const; + bool mediaHasAudio() const; + bool mediaIsVideo() const; + bool mediaMuted() const; + void toggleMediaMuteState() const; + + // Rect-based hit test related methods. + bool isRectBasedTest() const { return m_isRectBased; } + IntRect rectForPoint(int x, int y) const; + IntRect rectForPoint(const IntPoint&) const; + static IntRect rectForPoint(const IntPoint&, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding); + int topPadding() const { return m_topPadding; } + int rightPadding() const { return m_rightPadding; } + int bottomPadding() const { return m_bottomPadding; } + int leftPadding() const { return m_leftPadding; } + + // Returns true if it is rect-based hit test and needs to continue until the rect is fully + // enclosed by the boundaries of a node. + bool addNodeToRectBasedTestResult(Node*, int x, int y, const IntRect& rect = IntRect()); + const ListHashSet >& rectBasedTestResult() const { return m_rectBasedTestResult; } + void append(const HitTestResult&); + +private: + +#if ENABLE(VIDEO) + HTMLMediaElement* mediaElement() const; +#endif + + RefPtr m_innerNode; + RefPtr m_innerNonSharedNode; + IntPoint m_point; + IntPoint m_localPoint; // A point in the local coordinate space of m_innerNonSharedNode's renderer. Allows us to efficiently + // determine where inside the renderer we hit on subsequent operations. + RefPtr m_innerURLElement; + RefPtr m_scrollbar; + bool m_isOverWidget; // Returns true if we are over a widget (and not in the border/padding area of a RenderWidget for example). + bool m_isRectBased; + int m_topPadding; + int m_rightPadding; + int m_bottomPadding; + int m_leftPadding; + ListHashSet > m_rectBasedTestResult; +}; + +inline IntRect HitTestResult::rectForPoint(int x, int y) const +{ + return rectForPoint(IntPoint(x, y), m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); +} + +// Formula: +// x = p.x() - rightPadding +// y = p.y() - topPadding +// width = leftPadding + rightPadding + 1 +// height = topPadding + bottomPadding + 1 +inline IntRect HitTestResult::rectForPoint(const IntPoint& point) const +{ + return rectForPoint(point, m_topPadding, m_rightPadding, m_bottomPadding, m_leftPadding); +} + +String displayString(const String&, const Node*); + +} // namespace WebCore + +#endif // HitTestResult_h diff --git a/Source/WebCore/rendering/InlineBox.cpp b/Source/WebCore/rendering/InlineBox.cpp new file mode 100644 index 0000000..145096b --- /dev/null +++ b/Source/WebCore/rendering/InlineBox.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "InlineBox.h" + +#include "HitTestResult.h" +#include "InlineFlowBox.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "RootInlineBox.h" + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG +static bool inInlineBoxDetach; +#endif + +#ifndef NDEBUG + +InlineBox::~InlineBox() +{ + if (!m_hasBadParent && m_parent) + m_parent->setHasBadChildList(); +} + +#endif + +void InlineBox::remove() +{ + if (parent()) + parent()->removeChild(this); +} + +void InlineBox::destroy(RenderArena* renderArena) +{ +#ifndef NDEBUG + inInlineBoxDetach = true; +#endif + delete this; +#ifndef NDEBUG + inInlineBoxDetach = false; +#endif + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void InlineBox::operator delete(void* ptr, size_t sz) +{ + ASSERT(inInlineBoxDetach); + + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +#ifndef NDEBUG +void InlineBox::showTreeForThis() const +{ + if (m_renderer) + m_renderer->showTreeForThis(); +} +#endif + +int InlineBox::logicalHeight() const +{ +#if ENABLE(SVG) + if (hasVirtualLogicalHeight()) + return virtualLogicalHeight(); +#endif + + if (renderer()->isText()) + return m_isText ? renderer()->style(m_firstLine)->font().height() : 0; + if (renderer()->isBox() && parent()) + return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width(); + + ASSERT(isInlineFlowBox()); + RenderBoxModelObject* flowObject = boxModelObject(); + const Font& font = renderer()->style(m_firstLine)->font(); + int result = font.height(); + if (parent()) + result += flowObject->borderAndPaddingLogicalHeight(); + return result; +} + +int InlineBox::caretMinOffset() const +{ + return m_renderer->caretMinOffset(); +} + +int InlineBox::caretMaxOffset() const +{ + return m_renderer->caretMaxOffset(); +} + +unsigned InlineBox::caretMaxRenderedOffset() const +{ + return 1; +} + +void InlineBox::dirtyLineBoxes() +{ + markDirty(); + for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) + curr->markDirty(); +} + +void InlineBox::deleteLine(RenderArena* arena) +{ + if (!m_extracted && m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(0); + destroy(arena); +} + +void InlineBox::extractLine() +{ + m_extracted = true; + if (m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(0); +} + +void InlineBox::attachLine() +{ + m_extracted = false; + if (m_renderer->isBox()) + toRenderBox(m_renderer)->setInlineBoxWrapper(this); +} + +void InlineBox::adjustPosition(int dx, int dy) +{ + m_x += dx; + m_y += dy; + if (m_renderer->isReplaced()) { + RenderBox* box = toRenderBox(m_renderer); + box->move(dx, dy); + } +} + +void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) + return; + + IntPoint childPoint = IntPoint(tx, ty); + if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). + childPoint = renderer()->containingBlock()->flipForWritingMode(toRenderBox(renderer()), childPoint, RenderBox::ParentToChildFlippingAdjustment); + + // Paint all phases of replaced elements atomically, as though the replaced element established its + // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 + // specification.) + bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; + PaintInfo info(paintInfo); + info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; + renderer()->paint(info, childPoint.x(), childPoint.y()); + if (!preservePhase) { + info.phase = PaintPhaseChildBlockBackgrounds; + renderer()->paint(info, childPoint.x(), childPoint.y()); + info.phase = PaintPhaseFloat; + renderer()->paint(info, childPoint.x(), childPoint.y()); + info.phase = PaintPhaseForeground; + renderer()->paint(info, childPoint.x(), childPoint.y()); + info.phase = PaintPhaseOutline; + renderer()->paint(info, childPoint.x(), childPoint.y()); + } +} + +bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + // Hit test all phases of replaced elements atomically, as though the replaced element established its + // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 + // specification.) + return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty); +} + +const RootInlineBox* InlineBox::root() const +{ + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast(this); +} + +RootInlineBox* InlineBox::root() +{ + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast(this); +} + +bool InlineBox::nextOnLineExists() const +{ + if (!m_determinedIfNextOnLineExists) { + m_determinedIfNextOnLineExists = true; + + if (!parent()) + m_nextOnLineExists = false; + else if (nextOnLine()) + m_nextOnLineExists = true; + else + m_nextOnLineExists = parent()->nextOnLineExists(); + } + return m_nextOnLineExists; +} + +bool InlineBox::prevOnLineExists() const +{ + if (!m_determinedIfPrevOnLineExists) { + m_determinedIfPrevOnLineExists = true; + + if (!parent()) + m_prevOnLineExists = false; + else if (prevOnLine()) + m_prevOnLineExists = true; + else + m_prevOnLineExists = parent()->prevOnLineExists(); + } + return m_prevOnLineExists; +} + +InlineBox* InlineBox::nextLeafChild() const +{ + InlineBox* leaf = 0; + for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine()) + leaf = box->isLeaf() ? box : static_cast(box)->firstLeafChild(); + if (!leaf && parent()) + leaf = parent()->nextLeafChild(); + return leaf; +} + +InlineBox* InlineBox::prevLeafChild() const +{ + InlineBox* leaf = 0; + for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine()) + leaf = box->isLeaf() ? box : static_cast(box)->lastLeafChild(); + if (!leaf && parent()) + leaf = parent()->prevLeafChild(); + return leaf; +} + +RenderObject::SelectionState InlineBox::selectionState() +{ + return renderer()->selectionState(); +} + +bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) +{ + // Non-replaced elements can always accommodate an ellipsis. + if (!m_renderer || !m_renderer->isReplaced()) + return true; + + IntRect boxRect(m_x, 0, m_logicalWidth, 10); + IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10); + return !(boxRect.intersects(ellipsisRect)); +} + +int InlineBox::placeEllipsisBox(bool, int, int, int, bool&) +{ + // Use -1 to mean "we didn't set the position." + return -1; +} + +IntPoint InlineBox::locationIncludingFlipping() +{ + if (!renderer()->style()->isFlippedBlocksWritingMode()) + return IntPoint(x(), y()); + RenderBlock* block = root()->block(); + if (block->style()->isHorizontalWritingMode()) + return IntPoint(x(), block->height() - height() - y()); + else + return IntPoint(block->width() - width() - x(), y()); +} + +void InlineBox::flipForWritingMode(IntRect& rect) +{ + if (!renderer()->style()->isFlippedBlocksWritingMode()) + return; + root()->block()->flipForWritingMode(rect); +} + +IntPoint InlineBox::flipForWritingMode(const IntPoint& point) +{ + if (!renderer()->style()->isFlippedBlocksWritingMode()) + return point; + return root()->block()->flipForWritingMode(point); +} + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::InlineBox* b) +{ + if (b) + b->showTreeForThis(); +} + +#endif diff --git a/Source/WebCore/rendering/InlineBox.h b/Source/WebCore/rendering/InlineBox.h new file mode 100644 index 0000000..5b3f682 --- /dev/null +++ b/Source/WebCore/rendering/InlineBox.h @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef InlineBox_h +#define InlineBox_h + +#include "RenderBR.h" +#include "RenderBoxModelObject.h" +#include "TextDirection.h" + +namespace WebCore { + +class HitTestRequest; +class HitTestResult; +class RootInlineBox; + +// InlineBox represents a rectangle that occurs on a line. It corresponds to +// some RenderObject (i.e., it represents a portion of that RenderObject). +class InlineBox { +public: + InlineBox(RenderObject* obj) + : m_next(0) + , m_prev(0) + , m_parent(0) + , m_renderer(obj) + , m_x(0) + , m_y(0) + , m_logicalWidth(0) + , m_firstLine(false) + , m_constructed(false) + , m_bidiEmbeddingLevel(0) + , m_dirty(false) + , m_extracted(false) +#if ENABLE(SVG) + , m_hasVirtualLogicalHeight(false) +#endif + , m_isHorizontal(true) + , m_endsWithBreak(false) + , m_hasSelectedChildren(false) + , m_hasEllipsisBoxOrHyphen(false) + , m_dirOverride(false) + , m_isText(false) + , m_determinedIfNextOnLineExists(false) + , m_determinedIfPrevOnLineExists(false) + , m_nextOnLineExists(false) + , m_prevOnLineExists(false) + , m_toAdd(0) +#ifndef NDEBUG + , m_hasBadParent(false) +#endif + { + } + + InlineBox(RenderObject* obj, int x, int y, int logicalWidth, bool firstLine, bool constructed, + bool dirty, bool extracted, bool isHorizontal, InlineBox* next, InlineBox* prev, InlineFlowBox* parent) + : m_next(next) + , m_prev(prev) + , m_parent(parent) + , m_renderer(obj) + , m_x(x) + , m_y(y) + , m_logicalWidth(logicalWidth) + , m_firstLine(firstLine) + , m_constructed(constructed) + , m_bidiEmbeddingLevel(0) + , m_dirty(dirty) + , m_extracted(extracted) +#if ENABLE(SVG) + , m_hasVirtualLogicalHeight(false) +#endif + , m_isHorizontal(isHorizontal) + , m_endsWithBreak(false) + , m_hasSelectedChildren(false) + , m_hasEllipsisBoxOrHyphen(false) + , m_dirOverride(false) + , m_isText(false) + , m_determinedIfNextOnLineExists(false) + , m_determinedIfPrevOnLineExists(false) + , m_nextOnLineExists(false) + , m_prevOnLineExists(false) + , m_toAdd(0) +#ifndef NDEBUG + , m_hasBadParent(false) +#endif + { + } + + virtual ~InlineBox(); + + virtual void destroy(RenderArena*); + + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + + virtual bool isLineBreak() const { return false; } + + virtual void adjustPosition(int dx, int dy); + void adjustLineDirectionPosition(int delta) + { + if (isHorizontal()) + adjustPosition(delta, 0); + else + adjustPosition(0, delta); + } + void adjustBlockDirectionPosition(int delta) + { + if (isHorizontal()) + adjustPosition(0, delta); + else + adjustPosition(delta, 0); + } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + InlineBox* next() const { return m_next; } + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: +#ifndef NDEBUG + void showTreeForThis() const; +#endif + + bool isText() const { return m_isText; } + void setIsText(bool b) { m_isText = b; } + + virtual bool isInlineFlowBox() const { return false; } + virtual bool isInlineTextBox() const { return false; } + virtual bool isRootInlineBox() const { return false; } +#if ENABLE(SVG) + virtual bool isSVGInlineTextBox() const { return false; } + virtual bool isSVGInlineFlowBox() const { return false; } + virtual bool isSVGRootInlineBox() const { return false; } +#endif + + bool hasVirtualLogicalHeight() const { return m_hasVirtualLogicalHeight; } + void setHasVirtualLogicalHeight() { m_hasVirtualLogicalHeight = true; } + virtual int virtualLogicalHeight() const + { + ASSERT_NOT_REACHED(); + return 0; + } + + bool isHorizontal() const { return m_isHorizontal; } + void setIsHorizontal(bool horizontal) { m_isHorizontal = horizontal; } + + virtual IntRect calculateBoundaries() const + { + ASSERT_NOT_REACHED(); + return IntRect(); + } + + bool isConstructed() { return m_constructed; } + virtual void setConstructed() { m_constructed = true; } + + void setExtracted(bool b = true) { m_extracted = b; } + + void setFirstLineStyleBit(bool f) { m_firstLine = f; } + bool isFirstLineStyle() const { return m_firstLine; } + + void remove(); + + InlineBox* nextOnLine() const { return m_next; } + InlineBox* prevOnLine() const { return m_prev; } + void setNextOnLine(InlineBox* next) + { + ASSERT(m_parent || !next); + m_next = next; + } + void setPrevOnLine(InlineBox* prev) + { + ASSERT(m_parent || !prev); + m_prev = prev; + } + bool nextOnLineExists() const; + bool prevOnLineExists() const; + + virtual bool isLeaf() const { return true; } + + InlineBox* nextLeafChild() const; + InlineBox* prevLeafChild() const; + + RenderObject* renderer() const { return m_renderer; } + + InlineFlowBox* parent() const + { + ASSERT(!m_hasBadParent); + return m_parent; + } + void setParent(InlineFlowBox* par) { m_parent = par; } + + const RootInlineBox* root() const; + RootInlineBox* root(); + + // x() is the left side of the box in the containing block's coordinate system. + void setX(int x) { m_x = x; } + int x() const { return m_x; } + + // y() is the top side of the box in the containing block's coordinate system. + void setY(int y) { m_y = y; } + int y() const { return m_y; } + + int width() const { return isHorizontal() ? logicalWidth() : logicalHeight(); } + int height() const { return isHorizontal() ? logicalHeight() : logicalWidth(); } + + IntRect frameRect() const { return IntRect(x(), y(), width(), height()); } + + // The logicalLeft position is the left edge of the line box in a horizontal line and the top edge in a vertical line. + int logicalLeft() const { return isHorizontal() ? m_x : m_y; } + int logicalRight() const { return logicalLeft() + logicalWidth(); } + void setLogicalLeft(int left) + { + if (isHorizontal()) + m_x = left; + else + m_y = left; + } + + // The logicalTop[ position is the top edge of the line box in a horizontal line and the left edge in a vertical line. + int logicalTop() const { return isHorizontal() ? m_y : m_x; } + int logicalBottom() const { return logicalTop() + logicalHeight(); } + void setLogicalTop(int top) + { + if (isHorizontal()) + m_y = top; + else + m_x = top; + } + + // The logical width is our extent in the line's overall inline direction, i.e., width for horizontal text and height for vertical text. + void setLogicalWidth(int w) { m_logicalWidth = w; } + int logicalWidth() const { return m_logicalWidth; } + + // The logical height is our extent in the block flow direction, i.e., height for horizontal text and width for vertical text. + int logicalHeight() const; + + virtual int baselinePosition(FontBaseline baselineType) const { return boxModelObject()->baselinePosition(baselineType, m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine); } + virtual int lineHeight() const { return boxModelObject()->lineHeight(m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine); } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + unsigned char bidiLevel() const { return m_bidiEmbeddingLevel; } + void setBidiLevel(unsigned char level) { m_bidiEmbeddingLevel = level; } + TextDirection direction() const { return m_bidiEmbeddingLevel % 2 ? RTL : LTR; } + bool isLeftToRightDirection() const { return direction() == LTR; } + int caretLeftmostOffset() const { return isLeftToRightDirection() ? caretMinOffset() : caretMaxOffset(); } + int caretRightmostOffset() const { return isLeftToRightDirection() ? caretMaxOffset() : caretMinOffset(); } + + virtual void clearTruncation() { } + + bool isDirty() const { return m_dirty; } + void markDirty(bool dirty = true) { m_dirty = dirty; } + + void dirtyLineBoxes(); + + virtual RenderObject::SelectionState selectionState(); + + virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth); + // visibleLeftEdge, visibleRightEdge are in the parent's coordinate system. + virtual int placeEllipsisBox(bool ltr, int visibleLeftEdge, int visibleRightEdge, int ellipsisWidth, bool&); + + void setHasBadParent(); + + int toAdd() const { return m_toAdd; } + + bool visibleToHitTesting() const { return renderer()->style()->visibility() == VISIBLE && renderer()->style()->pointerEvents() != PE_NONE; } + + // Use with caution! The type is not checked! + RenderBoxModelObject* boxModelObject() const + { + if (!m_renderer->isText()) + return toRenderBoxModelObject(m_renderer); + return 0; + } + + IntPoint locationIncludingFlipping(); + void flipForWritingMode(IntRect&); + IntPoint flipForWritingMode(const IntPoint&); + +private: + InlineBox* m_next; // The next element on the same line as us. + InlineBox* m_prev; // The previous element on the same line as us. + + InlineFlowBox* m_parent; // The box that contains us. + +public: + RenderObject* m_renderer; + + int m_x; + int m_y; + int m_logicalWidth; + + // Some of these bits are actually for subclasses and moved here to compact the structures. + + // for this class +protected: + bool m_firstLine : 1; +private: + bool m_constructed : 1; + unsigned char m_bidiEmbeddingLevel : 6; +protected: + bool m_dirty : 1; + bool m_extracted : 1; + bool m_hasVirtualLogicalHeight : 1; + + bool m_isHorizontal : 1; + + // for RootInlineBox + bool m_endsWithBreak : 1; // Whether the line ends with a
. + bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the
that terminates our line is selected). + bool m_hasEllipsisBoxOrHyphen : 1; + + // for InlineTextBox +public: + bool m_dirOverride : 1; + bool m_isText : 1; // Whether or not this object represents text with a non-zero height. Includes non-image list markers, text boxes. +protected: + mutable bool m_determinedIfNextOnLineExists : 1; + mutable bool m_determinedIfPrevOnLineExists : 1; + mutable bool m_nextOnLineExists : 1; + mutable bool m_prevOnLineExists : 1; + int m_toAdd : 11; // for justified text + +#ifndef NDEBUG +private: + bool m_hasBadParent; +#endif +}; + +#ifdef NDEBUG +inline InlineBox::~InlineBox() +{ +} +#endif + +inline void InlineBox::setHasBadParent() +{ +#ifndef NDEBUG + m_hasBadParent = true; +#endif +} + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::InlineBox*); +#endif + +#endif // InlineBox_h diff --git a/Source/WebCore/rendering/InlineFlowBox.cpp b/Source/WebCore/rendering/InlineFlowBox.cpp new file mode 100644 index 0000000..75b23c5 --- /dev/null +++ b/Source/WebCore/rendering/InlineFlowBox.cpp @@ -0,0 +1,1358 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "InlineFlowBox.h" + +#include "CachedImage.h" +#include "CSSPropertyNames.h" +#include "Document.h" +#include "EllipsisBox.h" +#include "GraphicsContext.h" +#include "InlineTextBox.h" +#include "HitTestResult.h" +#include "RootInlineBox.h" +#include "RenderBlock.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderListMarker.h" +#include "RenderRubyBase.h" +#include "RenderRubyRun.h" +#include "RenderRubyText.h" +#include "RenderTableCell.h" +#include "RootInlineBox.h" +#include "Text.h" +#include "VerticalPositionCache.h" + +#include + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG + +InlineFlowBox::~InlineFlowBox() +{ + if (!m_hasBadChildList) + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->setHasBadParent(); +} + +#endif + +int InlineFlowBox::getFlowSpacingLogicalWidth() +{ + int totWidth = marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight(); + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->isInlineFlowBox()) + totWidth += static_cast(curr)->getFlowSpacingLogicalWidth(); + } + return totWidth; +} + +void InlineFlowBox::addToLine(InlineBox* child) +{ + ASSERT(!child->parent()); + ASSERT(!child->nextOnLine()); + ASSERT(!child->prevOnLine()); + checkConsistency(); + + child->setParent(this); + if (!m_firstChild) { + m_firstChild = child; + m_lastChild = child; + } else { + m_lastChild->setNextOnLine(child); + child->setPrevOnLine(m_lastChild); + m_lastChild = child; + } + child->setFirstLineStyleBit(m_firstLine); + child->setIsHorizontal(isHorizontal()); + if (child->isText()) + m_hasTextChildren = true; + + checkConsistency(); +} + +void InlineFlowBox::removeChild(InlineBox* child) +{ + checkConsistency(); + + if (!m_dirty) + dirtyLineBoxes(); + + root()->childRemoved(child); + + if (child == m_firstChild) + m_firstChild = child->nextOnLine(); + if (child == m_lastChild) + m_lastChild = child->prevOnLine(); + if (child->nextOnLine()) + child->nextOnLine()->setPrevOnLine(child->prevOnLine()); + if (child->prevOnLine()) + child->prevOnLine()->setNextOnLine(child->nextOnLine()); + + child->setParent(0); + + checkConsistency(); +} + +void InlineFlowBox::deleteLine(RenderArena* arena) +{ + InlineBox* child = firstChild(); + InlineBox* next = 0; + while (child) { + ASSERT(this == child->parent()); + next = child->nextOnLine(); +#ifndef NDEBUG + child->setParent(0); +#endif + child->deleteLine(arena); + child = next; + } +#ifndef NDEBUG + m_firstChild = 0; + m_lastChild = 0; +#endif + + removeLineBoxFromRenderObject(); + destroy(arena); +} + +void InlineFlowBox::removeLineBoxFromRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->removeLineBox(this); +} + +void InlineFlowBox::extractLine() +{ + if (!m_extracted) + extractLineBoxFromRenderObject(); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->extractLine(); +} + +void InlineFlowBox::extractLineBoxFromRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->extractLineBox(this); +} + +void InlineFlowBox::attachLine() +{ + if (m_extracted) + attachLineBoxToRenderObject(); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->attachLine(); +} + +void InlineFlowBox::attachLineBoxToRenderObject() +{ + toRenderInline(renderer())->lineBoxes()->attachLineBox(this); +} + +void InlineFlowBox::adjustPosition(int dx, int dy) +{ + InlineBox::adjustPosition(dx, dy); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->adjustPosition(dx, dy); + if (m_overflow) + m_overflow->move(dx, dy); +} + +RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const +{ + return toRenderInline(renderer())->lineBoxes(); +} + +bool InlineFlowBox::onEndChain(RenderObject* endObject) +{ + if (!endObject) + return false; + + if (endObject == renderer()) + return true; + + RenderObject* curr = endObject; + RenderObject* parent = curr->parent(); + while (parent && !parent->isRenderBlock()) { + if (parent->lastChild() != curr || parent == renderer()) + return false; + + curr = parent; + parent = curr->parent(); + } + + return true; +} + +void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject) +{ + // All boxes start off open. They will not apply any margins/border/padding on + // any side. + bool includeLeftEdge = false; + bool includeRightEdge = false; + + // The root inline box never has borders/margins/padding. + if (parent()) { + bool ltr = renderer()->style()->isLeftToRightDirection(); + + // Check to see if all initial lines are unconstructed. If so, then + // we know the inline began on this line (unless we are a continuation). + RenderLineBoxList* lineBoxList = rendererLineBoxes(); + if (!lineBoxList->firstLineBox()->isConstructed() && !renderer()->isInlineElementContinuation()) { + if (ltr && lineBoxList->firstLineBox() == this) + includeLeftEdge = true; + else if (!ltr && lineBoxList->lastLineBox() == this) + includeRightEdge = true; + } + + // In order to determine if the inline ends on this line, we check three things: + // (1) If we are the last line and we don't have a continuation(), then we can + // close up. + // (2) If the last line box for the flow has an object following it on the line (ltr, + // reverse for rtl), then the inline has closed. + // (3) The line may end on the inline. If we are the last child (climbing up + // the end object's chain), then we just closed as well. + if (!lineBoxList->lastLineBox()->isConstructed()) { + RenderInline* inlineFlow = toRenderInline(renderer()); + if (ltr) { + if (!nextLineBox() && + ((lastLine && !inlineFlow->continuation()) || nextOnLineExists() || onEndChain(endObject))) + includeRightEdge = true; + } else { + if ((!prevLineBox() || prevLineBox()->isConstructed()) && + ((lastLine && !inlineFlow->continuation()) || prevOnLineExists() || onEndChain(endObject))) + includeLeftEdge = true; + } + } + } + + setEdges(includeLeftEdge, includeRightEdge); + + // Recur into our children. + for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { + if (currChild->isInlineFlowBox()) { + InlineFlowBox* currFlow = static_cast(currChild); + currFlow->determineSpacingForFlowBoxes(lastLine, endObject); + } + } +} + +int InlineFlowBox::placeBoxesInInlineDirection(int logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +{ + // Set our x position. + setLogicalLeft(logicalLeft); + + int startLogicalLeft = logicalLeft; + logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isText()) { + InlineTextBox* text = static_cast(curr); + RenderText* rt = toRenderText(text->renderer()); + if (rt->textLength()) { + if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()])) + logicalLeft += rt->style(m_firstLine)->font().wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]); + } + text->setLogicalLeft(logicalLeft); + logicalLeft += text->logicalWidth(); + } else { + if (curr->renderer()->isPositioned()) { + if (curr->renderer()->parent()->style()->isLeftToRightDirection()) + curr->setLogicalLeft(logicalLeft); + else + // Our offset that we cache needs to be from the edge of the right border box and + // not the left border box. We have to subtract |x| from the width of the block + // (which can be obtained from the root line box). + curr->setLogicalLeft(root()->block()->logicalWidth() - logicalLeft); + continue; // The positioned object has no effect on the width. + } + if (curr->renderer()->isRenderInline()) { + InlineFlowBox* flow = static_cast(curr); + logicalLeft += flow->marginLogicalLeft(); + logicalLeft = flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); + logicalLeft += flow->marginLogicalRight(); + } else if (!curr->renderer()->isListMarker() || toRenderListMarker(curr->renderer())->isInside()) { + // The box can have a different writing-mode than the overall line, so this is a bit complicated. + // Just get all the physical margin and overflow values by hand based off |isVertical|. + int logicalLeftMargin = isHorizontal() ? curr->boxModelObject()->marginLeft() : curr->boxModelObject()->marginTop(); + int logicalRightMargin = isHorizontal() ? curr->boxModelObject()->marginRight() : curr->boxModelObject()->marginBottom(); + + logicalLeft += logicalLeftMargin; + curr->setLogicalLeft(logicalLeft); + logicalLeft += curr->logicalWidth() + logicalRightMargin; + } + } + } + + logicalLeft += borderLogicalRight() + paddingLogicalRight(); + setLogicalWidth(logicalLeft - startLogicalLeft); + return logicalLeft; +} + +bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap& textBoxDataMap) const +{ + if (isHorizontal()) + return false; + + if (renderer()->style(m_firstLine)->font().primaryFont()->orientation() == Vertical) + return true; + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) { + if (static_cast(curr)->requiresIdeographicBaseline(textBoxDataMap)) + return true; + } else { + if (curr->renderer()->style(m_firstLine)->font().primaryFont()->orientation() == Vertical) + return true; + + const Vector* usedFonts = 0; + if (curr->isInlineTextBox()) { + GlyphOverflowAndFallbackFontsMap::const_iterator it = textBoxDataMap.find(static_cast(curr)); + usedFonts = it == textBoxDataMap.end() ? 0 : &it->second.first; + } + + if (usedFonts) { + for (size_t i = 0; i < usedFonts->size(); ++i) { + if (usedFonts->at(i)->orientation() == Vertical) + return true; + } + } + } + } + + return false; +} + +void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, + int maxPositionTop, int maxPositionBottom) +{ + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + // The computed lineheight needs to be extended for the + // positioned elements + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + if (curr->logicalTop() == PositionTop || curr->logicalTop() == PositionBottom) { + int lineHeight = curr->lineHeight(); + if (curr->logicalTop() == PositionTop) { + if (maxAscent + maxDescent < lineHeight) + maxDescent = lineHeight - maxAscent; + } + else { + if (maxAscent + maxDescent < lineHeight) + maxAscent = lineHeight - maxDescent; + } + + if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom)) + break; + } + + if (curr->isInlineFlowBox()) + static_cast(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + } +} + +static int verticalPositionForBox(InlineBox* box, FontBaseline baselineType, bool firstLine, VerticalPositionCache& verticalPositionCache) +{ + if (box->renderer()->isText()) + return box->parent()->logicalTop(); + + RenderBoxModelObject* renderer = box->boxModelObject(); + ASSERT(renderer->isInline()); + if (!renderer->isInline()) + return 0; + + // This method determines the vertical position for inline elements. + if (firstLine && !renderer->document()->usesFirstLineRules()) + firstLine = false; + + // Check the cache. + bool isRenderInline = renderer->isRenderInline(); + if (isRenderInline && !firstLine) { + int verticalPosition = verticalPositionCache.get(renderer, baselineType); + if (verticalPosition != PositionUndefined) + return verticalPosition; + } + + int verticalPosition = 0; + EVerticalAlign verticalAlign = renderer->style()->verticalAlign(); + if (verticalAlign == TOP) + verticalPosition = PositionTop; + else if (verticalAlign == BOTTOM) + verticalPosition = PositionBottom; + else { + RenderObject* parent = renderer->parent(); + if (parent->isRenderInline() && parent->style()->verticalAlign() != TOP && parent->style()->verticalAlign() != BOTTOM) + verticalPosition = box->parent()->logicalTop(); + + if (verticalAlign != BASELINE) { + const Font& font = parent->style(firstLine)->font(); + int fontSize = font.pixelSize(); + + LineDirectionMode lineDirection = parent->style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + + if (verticalAlign == SUB) + verticalPosition += fontSize / 5 + 1; + else if (verticalAlign == SUPER) + verticalPosition -= fontSize / 3 + 1; + else if (verticalAlign == TEXT_TOP) + verticalPosition += renderer->baselinePosition(baselineType, firstLine, lineDirection) - font.ascent(baselineType); + else if (verticalAlign == MIDDLE) + verticalPosition += -static_cast(font.xHeight() / 2) - renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType, firstLine, lineDirection); + else if (verticalAlign == TEXT_BOTTOM) { + verticalPosition += font.descent(baselineType); + // lineHeight - baselinePosition is always 0 for replaced elements (except inline blocks), so don't bother wasting time in that case. + if (!renderer->isReplaced() || renderer->isInlineBlockOrInlineTable()) + verticalPosition -= (renderer->lineHeight(firstLine, lineDirection) - renderer->baselinePosition(baselineType, firstLine, lineDirection)); + } else if (verticalAlign == BASELINE_MIDDLE) + verticalPosition += -renderer->lineHeight(firstLine, lineDirection) / 2 + renderer->baselinePosition(baselineType, firstLine, lineDirection); + else if (verticalAlign == LENGTH) + verticalPosition -= renderer->style()->verticalAlignLength().calcValue(renderer->lineHeight(firstLine, lineDirection)); + } + } + + // Store the cached value. + if (isRenderInline && !firstLine) + verticalPositionCache.set(renderer, baselineType, verticalPosition); + + return verticalPosition; +} + +void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, + int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, + bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + FontBaseline baselineType, VerticalPositionCache& verticalPositionCache) +{ + // The primary purpose of this function is to compute the maximal ascent and descent values for + // a line. + // + // The maxAscent value represents the distance of the highest point of any box (including line-height) from + // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box + // (also including line-height) from the root box baseline. These values can be negative. + // + // A secondary purpose of this function is to store the offset of very box's baseline from the root box's + // baseline. This information is cached in the logicalTop() of every box. We're effectively just using + // the logicalTop() as scratch space. + if (isRootInlineBox()) { + // Examine our root box. + int height = lineHeight(); + int baseline = baselinePosition(baselineType); + if (hasTextChildren() || strictMode) { + int ascent = baseline; + int descent = height - ascent; + if (maxAscent < ascent || !setMaxAscent) { + maxAscent = ascent; + setMaxAscent = true; + } + if (maxDescent < descent || !setMaxDescent) { + maxDescent = descent; + setMaxDescent = true; + } + } + } + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + bool isInlineFlow = curr->isInlineFlowBox(); + + // Because a box can be positioned such that it ends up fully above or fully below the + // root line box, we only consider it to affect the maxAscent and maxDescent values if some + // part of the box (EXCLUDING line-height) is above (for ascent) or below (for descent) the root box's baseline. + bool affectsAscent = false; + bool affectsDescent = false; + + // The verticalPositionForBox function returns the distance between the child box's baseline + // and the root box's baseline. The value is negative if the child box's baseline is above the + // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline. + curr->setLogicalTop(verticalPositionForBox(curr, baselineType, m_firstLine, verticalPositionCache)); + + int lineHeight; + int baseline; + Vector* usedFonts = 0; + if (curr->isInlineTextBox()) { + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast(curr)); + usedFonts = it == textBoxDataMap.end() ? 0 : &it->second.first; + } + + if (usedFonts && !usedFonts->isEmpty() && curr->renderer()->style(m_firstLine)->lineHeight().isNegative()) { + usedFonts->append(curr->renderer()->style(m_firstLine)->font().primaryFont()); + bool baselineSet = false; + baseline = 0; + int baselineToBottom = 0; + for (size_t i = 0; i < usedFonts->size(); ++i) { + int halfLeading = (usedFonts->at(i)->lineSpacing() - usedFonts->at(i)->height()) / 2; + int usedFontBaseline = halfLeading + usedFonts->at(i)->ascent(baselineType); + int usedFontBaselineToBottom = usedFonts->at(i)->lineSpacing() - usedFontBaseline; + if (!baselineSet) { + baselineSet = true; + baseline = usedFontBaseline; + baselineToBottom = usedFontBaselineToBottom; + } else { + baseline = max(baseline, usedFontBaseline); + baselineToBottom = max(baselineToBottom, usedFontBaselineToBottom); + } + if (!affectsAscent) + affectsAscent = usedFonts->at(i)->ascent() - curr->logicalTop() > 0; + if (!affectsDescent) + affectsDescent = usedFonts->at(i)->descent() + curr->logicalTop() > 0; + } + lineHeight = baseline + baselineToBottom; + } else { + lineHeight = curr->lineHeight(); + baseline = curr->baselinePosition(baselineType); + if (curr->isText() || isInlineFlow) { + // Examine the font box for inline flows and text boxes to see if any part of it is above the baseline. + // If the top of our font box relative to the root box baseline is above the root box baseline, then + // we are contributing to the maxAscent value. + affectsAscent = curr->renderer()->style(m_firstLine)->font().ascent(baselineType) - curr->logicalTop() > 0; + + // Descent is similar. If any part of our font box is below the root box's baseline, then + // we contribute to the maxDescent value. + affectsDescent = curr->renderer()->style(m_firstLine)->font().descent(baselineType) + curr->logicalTop() > 0; + } else { + // Replaced elements always affect both the ascent and descent. + affectsAscent = true; + affectsDescent = true; + } + } + + if (curr->logicalTop() == PositionTop) { + if (maxPositionTop < lineHeight) + maxPositionTop = lineHeight; + } else if (curr->logicalTop() == PositionBottom) { + if (maxPositionBottom < lineHeight) + maxPositionBottom = lineHeight; + } else if ((!isInlineFlow || static_cast(curr)->hasTextChildren()) || curr->boxModelObject()->hasInlineDirectionBordersOrPadding() || strictMode) { + // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values + // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height + // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it + // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and + // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative + // values. + int ascent = baseline - curr->logicalTop(); + int descent = lineHeight - ascent; + if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) { + maxAscent = ascent; + setMaxAscent = true; + } + if (affectsDescent && (maxDescent < descent || !setMaxDescent)) { + maxDescent = descent; + setMaxDescent = true; + } + } + + if (curr->isInlineFlowBox()) + static_cast(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, + setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, + baselineType, verticalPositionCache); + } +} + +void InlineFlowBox::placeBoxesInBlockDirection(int top, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom, bool& setLineTop, + int& lineTopIncludingMargins, int& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType) +{ + if (isRootInlineBox()) + setLogicalTop(top + maxAscent - baselinePosition(baselineType)); // Place our root box. + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + // Adjust boxes to use their real box y/height and not the logical height (as dictated by + // line-height). + bool isInlineFlow = curr->isInlineFlowBox(); + if (isInlineFlow) + static_cast(curr)->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop, + lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType); + + bool childAffectsTopBottomPos = true; + if (curr->logicalTop() == PositionTop) + curr->setLogicalTop(top); + else if (curr->logicalTop() == PositionBottom) + curr->setLogicalTop(top + maxHeight - curr->lineHeight()); + else { + if ((isInlineFlow && !static_cast(curr)->hasTextChildren()) && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() && !strictMode) + childAffectsTopBottomPos = false; + int posAdjust = maxAscent - curr->baselinePosition(baselineType); + curr->setLogicalTop(curr->logicalTop() + top + posAdjust); + } + + int newLogicalTop = curr->logicalTop(); + int newLogicalTopIncludingMargins; + int boxHeight = curr->logicalHeight(); + int boxHeightIncludingMargins = boxHeight; + + if (curr->isText() || curr->isInlineFlowBox()) { + const Font& font = curr->renderer()->style(m_firstLine)->font(); + newLogicalTop += curr->baselinePosition(baselineType) - font.ascent(baselineType); + if (curr->isInlineFlowBox()) { + RenderBoxModelObject* boxObject = toRenderBoxModelObject(curr->renderer()); + newLogicalTop -= boxObject->style(m_firstLine)->isHorizontalWritingMode() ? boxObject->borderTop() + boxObject->paddingTop() : + boxObject->borderRight() + boxObject->paddingRight(); + } + newLogicalTopIncludingMargins = newLogicalTop; + } else if (!curr->renderer()->isBR()) { + RenderBox* box = toRenderBox(curr->renderer()); + newLogicalTopIncludingMargins = newLogicalTop; + int overSideMargin = curr->isHorizontal() ? box->marginTop() : box->marginRight(); + int underSideMargin = curr->isHorizontal() ? box->marginBottom() : box->marginLeft(); + newLogicalTop += overSideMargin; + boxHeightIncludingMargins += overSideMargin + underSideMargin; + } + + curr->setLogicalTop(newLogicalTop); + + if (childAffectsTopBottomPos) { + if (curr->renderer()->isRubyRun()) { + // Treat the leading on the first and last lines of ruby runs as not being part of the overall lineTop/lineBottom. + // Really this is a workaround hack for the fact that ruby should have been done as line layout and not done using + // inline-block. + if (!renderer()->style()->isFlippedLinesWritingMode()) + hasAnnotationsBefore = true; + else + hasAnnotationsAfter = true; + + RenderRubyRun* rubyRun = static_cast(curr->renderer()); + if (RenderRubyBase* rubyBase = rubyRun->rubyBase()) { + int bottomRubyBaseLeading = (curr->logicalHeight() - rubyBase->logicalBottom()) + rubyBase->logicalHeight() - (rubyBase->lastRootBox() ? rubyBase->lastRootBox()->lineBottom() : 0); + int topRubyBaseLeading = rubyBase->logicalTop() + (rubyBase->firstRootBox() ? rubyBase->firstRootBox()->lineTop() : 0); + newLogicalTop += !renderer()->style()->isFlippedLinesWritingMode() ? topRubyBaseLeading : bottomRubyBaseLeading; + boxHeight -= (topRubyBaseLeading + bottomRubyBaseLeading); + } + } + if (curr->isInlineTextBox()) { + TextEmphasisPosition emphasisMarkPosition; + if (static_cast(curr)->getEmphasisMarkPosition(curr->renderer()->style(m_firstLine), emphasisMarkPosition)) { + bool emphasisMarkIsOver = emphasisMarkPosition == TextEmphasisPositionOver; + if (emphasisMarkIsOver != curr->renderer()->style(m_firstLine)->isFlippedLinesWritingMode()) + hasAnnotationsBefore = true; + else + hasAnnotationsAfter = true; + } + } + + if (!setLineTop) { + setLineTop = true; + lineTop = newLogicalTop; + lineTopIncludingMargins = min(lineTop, newLogicalTopIncludingMargins); + } else { + lineTop = min(lineTop, newLogicalTop); + lineTopIncludingMargins = min(lineTop, min(lineTopIncludingMargins, newLogicalTopIncludingMargins)); + } + lineBottom = max(lineBottom, newLogicalTop + boxHeight); + lineBottomIncludingMargins = max(lineBottom, max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins)); + } + } + + if (isRootInlineBox()) { + const Font& font = renderer()->style(m_firstLine)->font(); + setLogicalTop(logicalTop() + baselinePosition(baselineType) - font.ascent(baselineType)); + + if (hasTextChildren() || strictMode) { + if (!setLineTop) { + setLineTop = true; + lineTop = logicalTop(); + lineTopIncludingMargins = lineTop; + } else { + lineTop = min(lineTop, logicalTop()); + lineTopIncludingMargins = min(lineTop, lineTopIncludingMargins); + } + lineBottom = max(lineBottom, logicalTop() + logicalHeight()); + lineBottomIncludingMargins = max(lineBottom, lineBottomIncludingMargins); + } + + if (renderer()->style()->isFlippedLinesWritingMode()) + flipLinesInBlockDirection(lineTopIncludingMargins, lineBottomIncludingMargins); + } +} + +void InlineFlowBox::flipLinesInBlockDirection(int lineTop, int lineBottom) +{ + // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop. + setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight()); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders aren't affected here. + + if (curr->isInlineFlowBox()) + static_cast(curr)->flipLinesInBlockDirection(lineTop, lineBottom); + else + curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - curr->logicalHeight()); + } +} + +void InlineFlowBox::addBoxShadowVisualOverflow(IntRect& logicalVisualOverflow) +{ + if (!parent()) + return; // Box-shadow doesn't apply to root line boxes. + + int boxShadowLogicalTop; + int boxShadowLogicalBottom; + renderer()->style(m_firstLine)->getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, boxShadowLogicalBottom); + + int logicalTopVisualOverflow = min(logicalTop() + boxShadowLogicalTop, logicalVisualOverflow.y()); + int logicalBottomVisualOverflow = max(logicalBottom() + boxShadowLogicalBottom, logicalVisualOverflow.bottom()); + + int boxShadowLogicalLeft; + int boxShadowLogicalRight; + renderer()->style(m_firstLine)->getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight); + + int logicalLeftVisualOverflow = min(logicalLeft() + boxShadowLogicalLeft, logicalVisualOverflow.x()); + int logicalRightVisualOverflow = max(logicalRight() + boxShadowLogicalRight, logicalVisualOverflow.right()); + + logicalVisualOverflow = IntRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, + logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); +} + +void InlineFlowBox::addTextBoxVisualOverflow(const InlineTextBox* textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, IntRect& logicalVisualOverflow) +{ + RenderStyle* style = renderer()->style(m_firstLine); + int strokeOverflow = static_cast(ceilf(style->textStrokeWidth() / 2.0f)); + + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(textBox); + GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->second.second; + + bool isFlippedLine = style->isFlippedLinesWritingMode(); + + int topGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->bottom : glyphOverflow->top) : 0; + int bottomGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->top : glyphOverflow->bottom) : 0; + int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0; + int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0; + + int topGlyphOverflow = -strokeOverflow - topGlyphEdge; + int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge; + int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge; + int rightGlyphOverflow = strokeOverflow + rightGlyphEdge; + + TextEmphasisPosition emphasisMarkPosition; + if (style->textEmphasisMark() != TextEmphasisMarkNone && textBox->getEmphasisMarkPosition(style, emphasisMarkPosition)) { + int emphasisMarkHeight = style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + if ((emphasisMarkPosition == TextEmphasisPositionOver) == (!style->isFlippedLinesWritingMode())) + topGlyphOverflow = min(topGlyphOverflow, -emphasisMarkHeight); + else + bottomGlyphOverflow = max(bottomGlyphOverflow, emphasisMarkHeight); + } + + // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is + // applied to the right, so this is not an issue with left overflow. + int letterSpacing = min(0, (int)style->font().letterSpacing()); + rightGlyphOverflow -= letterSpacing; + + int textShadowLogicalTop; + int textShadowLogicalBottom; + style->getTextShadowBlockDirectionExtent(textShadowLogicalTop, textShadowLogicalBottom); + + int childOverflowLogicalTop = min(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow); + int childOverflowLogicalBottom = max(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow); + + int textShadowLogicalLeft; + int textShadowLogicalRight; + style->getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight); + + int childOverflowLogicalLeft = min(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow); + int childOverflowLogicalRight = max(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow); + + int logicalTopVisualOverflow = min(textBox->logicalTop() + childOverflowLogicalTop, logicalVisualOverflow.y()); + int logicalBottomVisualOverflow = max(textBox->logicalBottom() + childOverflowLogicalBottom, logicalVisualOverflow.bottom()); + int logicalLeftVisualOverflow = min(textBox->logicalLeft() + childOverflowLogicalLeft, logicalVisualOverflow.x()); + int logicalRightVisualOverflow = max(textBox->logicalRight() + childOverflowLogicalRight, logicalVisualOverflow.right()); + + logicalVisualOverflow = IntRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, + logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); +} + +void InlineFlowBox::addReplacedChildOverflow(const InlineBox* inlineBox, IntRect& logicalLayoutOverflow, IntRect& logicalVisualOverflow) +{ + RenderBox* box = toRenderBox(inlineBox->renderer()); + + // Visual overflow only propagates if the box doesn't have a self-painting layer. This rectangle does not include + // transforms or relative positioning (since those objects always have self-painting layers), but it does need to be adjusted + // for writing-mode differences. + if (!box->hasSelfPaintingLayer()) { + IntRect childLogicalVisualOverflow = box->logicalVisualOverflowRectForPropagation(renderer()->style()); + childLogicalVisualOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); + logicalVisualOverflow.unite(childLogicalVisualOverflow); + } + + // Layout overflow internal to the child box only propagates if the child box doesn't have overflow clip set. + // Otherwise the child border box propagates as layout overflow. This rectangle must include transforms and relative positioning + // and be adjusted for writing-mode differences. + IntRect childLogicalLayoutOverflow = box->logicalLayoutOverflowRectForPropagation(renderer()->style()); + childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); + logicalLayoutOverflow.unite(childLogicalLayoutOverflow); +} + +void InlineFlowBox::computeOverflow(int lineTop, int lineBottom, bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +{ + // Any spillage outside of the line top and bottom is not considered overflow. We just ignore this, since it only happens + // from the "your ascent/descent don't affect the line" quirk. + int topOverflow = max(logicalTop(), lineTop); + int bottomOverflow = min(logicalBottom(), lineBottom); + + // Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored. + // Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in + // transforms, relative positioning, etc. + IntRect logicalLayoutOverflow(logicalLeft(), topOverflow, logicalWidth(), bottomOverflow - topOverflow); + IntRect logicalVisualOverflow(logicalLayoutOverflow); + + // box-shadow on root line boxes is applying to the block and not to the lines. + addBoxShadowVisualOverflow(logicalVisualOverflow); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->renderer()->isText()) { + InlineTextBox* text = static_cast(curr); + RenderText* rt = toRenderText(text->renderer()); + if (rt->isBR()) + continue; + addTextBoxVisualOverflow(text, textBoxDataMap, logicalVisualOverflow); + } else if (curr->renderer()->isRenderInline()) { + InlineFlowBox* flow = static_cast(curr); + flow->computeOverflow(lineTop, lineBottom, strictMode, textBoxDataMap); + if (!flow->boxModelObject()->hasSelfPaintingLayer()) + logicalVisualOverflow.unite(flow->logicalVisualOverflowRect()); + IntRect childLayoutOverflow = flow->logicalLayoutOverflowRect(); + childLayoutOverflow.move(flow->boxModelObject()->relativePositionLogicalOffset()); + logicalLayoutOverflow.unite(childLayoutOverflow); + } else + addReplacedChildOverflow(curr, logicalLayoutOverflow, logicalVisualOverflow); + } + + setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow); +} + +// FIXME: You will notice there is no contains() check here. If the rect is smaller than the frame box it actually +// becomes the new overflow. The reason for this is that in quirks mode we don't let inline flow boxes paint +// outside of the root line box's lineTop and lineBottom values. We accomplish this visual clamping by actually +// insetting the overflow rect so that it's smaller than the frame rect. +// +// The reason we don't just mutate the frameRect in quirks mode is that we'd have to put the m_height member variable +// back into InlineBox. Basically the tradeoff is 4 bytes in all modes (for m_height) added to InlineFlowBox, or +// the allocation of a RenderOverflow struct for InlineFlowBoxes in quirks mode only. For now, we're opting to award +// the smaller memory consumption to strict mode pages. +// +// It might be possible to hash a custom height, or to require that lineTop and lineBottom be passed in to +// all functions that query overflow. +void InlineFlowBox::setLayoutOverflow(const IntRect& rect) +{ + IntRect frameBox = frameRect(); + if (frameBox == rect || rect.isEmpty()) + return; + + if (!m_overflow) + m_overflow.set(new RenderOverflow(frameBox, frameBox)); + + m_overflow->setLayoutOverflow(rect); +} + +void InlineFlowBox::setVisualOverflow(const IntRect& rect) +{ + IntRect frameBox = frameRect(); + if (frameBox == rect || rect.isEmpty()) + return; + + if (!m_overflow) + m_overflow.set(new RenderOverflow(frameBox, frameBox)); + + m_overflow->setVisualOverflow(rect); +} + +void InlineFlowBox::setOverflowFromLogicalRects(const IntRect& logicalLayoutOverflow, const IntRect& logicalVisualOverflow) +{ + IntRect layoutOverflow(isHorizontal() ? logicalLayoutOverflow : logicalLayoutOverflow.transposedRect()); + setLayoutOverflow(layoutOverflow); + + IntRect visualOverflow(isHorizontal() ? logicalVisualOverflow : logicalVisualOverflow.transposedRect()); + setVisualOverflow(visualOverflow); +} + +bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + IntRect overflowRect(visualOverflowRect()); + flipForWritingMode(overflowRect); + overflowRect.move(tx, ty); + if (!overflowRect.intersects(result.rectForPoint(x, y))) + return false; + + // Check children first. + for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { + if ((curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) && curr->nodeAtPoint(request, result, x, y, tx, ty)) { + renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + + // Now check ourselves. + IntPoint boxOrigin = locationIncludingFlipping(); + boxOrigin.move(tx, ty); + IntRect rect(boxOrigin, IntSize(width(), height())); + if (visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) { + renderer()->updateHitTestResult(result, flipForWritingMode(IntPoint(x - tx, y - ty))); // Don't add in m_x or m_y here, we want coords in the containing block's space. + if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect)) + return true; + } + + return false; +} + +void InlineFlowBox::paint(PaintInfo& paintInfo, int tx, int ty) +{ + IntRect overflowRect(visualOverflowRect()); + overflowRect.inflate(renderer()->maximalOutlineSize(paintInfo.phase)); + flipForWritingMode(overflowRect); + overflowRect.move(tx, ty); + + if (!paintInfo.rect.intersects(overflowRect)) + return; + + if (paintInfo.phase != PaintPhaseChildOutlines) { + if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { + // Add ourselves to the paint info struct's list of inlines that need to paint their + // outlines. + if (renderer()->style()->visibility() == VISIBLE && renderer()->hasOutline() && !isRootInlineBox()) { + RenderInline* inlineFlow = toRenderInline(renderer()); + + RenderBlock* cb = 0; + bool containingBlockPaintsContinuationOutline = inlineFlow->continuation() || inlineFlow->isInlineElementContinuation(); + if (containingBlockPaintsContinuationOutline) { + cb = renderer()->containingBlock()->containingBlock(); + + for (RenderBoxModelObject* box = boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) { + if (box->hasSelfPaintingLayer()) { + containingBlockPaintsContinuationOutline = false; + break; + } + } + } + + if (containingBlockPaintsContinuationOutline) { + // Add ourselves to the containing block of the entire continuation so that it can + // paint us atomically. + cb->addContinuationWithOutline(toRenderInline(renderer()->node()->renderer())); + } else if (!inlineFlow->isInlineElementContinuation()) + paintInfo.outlineObjects->add(inlineFlow); + } + } else if (paintInfo.phase == PaintPhaseMask) { + paintMask(paintInfo, tx, ty); + return; + } else { + // Paint our background, border and box-shadow. + paintBoxDecorations(paintInfo, tx, ty); + } + } + + if (paintInfo.phase == PaintPhaseMask) + return; + + PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; + PaintInfo childInfo(paintInfo); + childInfo.phase = paintPhase; + childInfo.updatePaintingRootForChildren(renderer()); + + // Paint our children. + if (paintPhase != PaintPhaseSelfOutline) { + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) + curr->paint(childInfo, tx, ty); + } + } +} + +void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int _tx, int _ty, int w, int h, CompositeOperator op) +{ + if (!fillLayer) + return; + paintFillLayers(paintInfo, c, fillLayer->next(), _tx, _ty, w, h, op); + paintFillLayer(paintInfo, c, fillLayer, _tx, _ty, w, h, op); +} + +void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int w, int h, CompositeOperator op) +{ + StyleImage* img = fillLayer->image(); + bool hasFillImage = img && img->canRender(renderer()->style()->effectiveZoom()); + if ((!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) + boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, w, h, this, op); + else { + // We have a fill image that spans multiple lines. + // We need to adjust tx and ty by the width of all previous lines. + // Think of background painting on inlines as though you had one long line, a single continuous + // strip. Even though that strip has been broken up across multiple lines, you still paint it + // as though you had one single line. This means each line has to pick up the background where + // the previous line left off. + // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, + // but it isn't even clear how this should work at all. + int logicalOffsetOnLine = 0; + for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + logicalOffsetOnLine += curr->logicalWidth(); + int totalLogicalWidth = logicalOffsetOnLine; + for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) + totalLogicalWidth += curr->logicalWidth(); + int stripX = tx - (isHorizontal() ? logicalOffsetOnLine : 0); + int stripY = ty - (isHorizontal() ? 0 : logicalOffsetOnLine); + int stripWidth = isHorizontal() ? totalLogicalWidth : width(); + int stripHeight = isHorizontal() ? height() : totalLogicalWidth; + paintInfo.context->save(); + paintInfo.context->clip(IntRect(tx, ty, width(), height())); + boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, stripX, stripY, stripWidth, stripHeight, this, op); + paintInfo.context->restore(); + } +} + +void InlineFlowBox::paintBoxShadow(GraphicsContext* context, RenderStyle* s, ShadowStyle shadowStyle, int tx, int ty, int w, int h) +{ + if ((!prevLineBox() && !nextLineBox()) || !parent()) + boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle); + else { + // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't + // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines + boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge()); + } +} + +void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) + return; + + int x = m_x; + int y = m_y; + int w = width(); + int h = height(); + + // Constrain our background/border painting to the line top and bottom if necessary. + bool noQuirksMode = renderer()->document()->inNoQuirksMode(); + if (!hasTextChildren() && !noQuirksMode) { + RootInlineBox* rootBox = root(); + int& top = isHorizontal() ? y : x; + int& logicalHeight = isHorizontal() ? h : w; + int bottom = min(rootBox->lineBottom(), top + logicalHeight); + top = max(rootBox->lineTop(), top); + logicalHeight = bottom - top; + } + + // Move x/y to our coordinates. + IntRect localRect(x, y, w, h); + flipForWritingMode(localRect); + tx += localRect.x(); + ty += localRect.y(); + + GraphicsContext* context = paintInfo.context; + + // You can use p::first-line to specify a background. If so, the root line boxes for + // a line may actually have to paint a background. + RenderStyle* styleToUse = renderer()->style(m_firstLine); + if ((!parent() && m_firstLine && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) { + // Shadow comes first and is behind the background and border. + if (styleToUse->boxShadow()) + paintBoxShadow(context, styleToUse, Normal, tx, ty, w, h); + + Color c = styleToUse->visitedDependentColor(CSSPropertyBackgroundColor); + paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), tx, ty, w, h); + + if (styleToUse->boxShadow()) + paintBoxShadow(context, styleToUse, Inset, tx, ty, w, h); + + // :first-line cannot be used to put borders on a line. Always paint borders with our + // non-first-line style. + if (parent() && renderer()->style()->hasBorder()) { + StyleImage* borderImage = renderer()->style()->borderImage().image(); + bool hasBorderImage = borderImage && borderImage->canRender(styleToUse->effectiveZoom()); + if (hasBorderImage && !borderImage->isLoaded()) + return; // Don't paint anything while we wait for the image to load. + + // The simple case is where we either have no border image or we are the only box for this object. In those + // cases only a single call to draw is required. + if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) + boxModelObject()->paintBorder(context, tx, ty, w, h, renderer()->style(), includeLogicalLeftEdge(), includeLogicalRightEdge()); + else { + // We have a border image that spans multiple lines. + // We need to adjust tx and ty by the width of all previous lines. + // Think of border image painting on inlines as though you had one long line, a single continuous + // strip. Even though that strip has been broken up across multiple lines, you still paint it + // as though you had one single line. This means each line has to pick up the image where + // the previous line left off. + // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, + // but it isn't even clear how this should work at all. + int logicalOffsetOnLine = 0; + for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + logicalOffsetOnLine += curr->logicalWidth(); + int totalLogicalWidth = logicalOffsetOnLine; + for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) + totalLogicalWidth += curr->logicalWidth(); + int stripX = tx - (isHorizontal() ? logicalOffsetOnLine : 0); + int stripY = ty - (isHorizontal() ? 0 : logicalOffsetOnLine); + int stripWidth = isHorizontal() ? totalLogicalWidth : w; + int stripHeight = isHorizontal() ? h : totalLogicalWidth; + context->save(); + context->clip(IntRect(tx, ty, w, h)); + boxModelObject()->paintBorder(context, stripX, stripY, stripWidth, stripHeight, renderer()->style()); + context->restore(); + } + } + } +} + +void InlineFlowBox::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + int x = m_x; + int y = m_y; + int w = width(); + int h = height(); + + // Constrain our background/border painting to the line top and bottom if necessary. + bool noQuirksMode = renderer()->document()->inNoQuirksMode(); + if (!hasTextChildren() && !noQuirksMode) { + RootInlineBox* rootBox = root(); + int& top = isHorizontal() ? y : x; + int& logicalHeight = isHorizontal() ? h : w; + int bottom = min(rootBox->lineBottom(), top + logicalHeight); + top = max(rootBox->lineTop(), top); + logicalHeight = bottom - top; + } + + // Move x/y to our coordinates. + IntRect localRect(x, y, w, h); + flipForWritingMode(localRect); + tx += localRect.x(); + ty += localRect.y(); + + const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); + StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); + + // Figure out if we need to push a transparency layer to render our mask. + bool pushTransparencyLayer = false; + bool compositedMask = renderer()->hasLayer() && boxModelObject()->layer()->hasCompositedMask(); + CompositeOperator compositeOp = CompositeSourceOver; + if (!compositedMask) { + if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) + pushTransparencyLayer = true; + + compositeOp = CompositeDestinationIn; + if (pushTransparencyLayer) { + paintInfo.context->setCompositeOperation(CompositeDestinationIn); + paintInfo.context->beginTransparencyLayer(1.0f); + compositeOp = CompositeSourceOver; + } + } + + paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), tx, ty, w, h, compositeOp); + + bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer()->style()->effectiveZoom()); + if (!hasBoxImage || !maskBoxImage->isLoaded()) + return; // Don't paint anything while we wait for the image to load. + + // The simple case is where we are the only box for this object. In those + // cases only a single call to draw is required. + if (!prevLineBox() && !nextLineBox()) { + boxModelObject()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, renderer()->style(), maskNinePieceImage, compositeOp); + } else { + // We have a mask image that spans multiple lines. + // We need to adjust _tx and _ty by the width of all previous lines. + int logicalOffsetOnLine = 0; + for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + logicalOffsetOnLine += curr->logicalWidth(); + int totalLogicalWidth = logicalOffsetOnLine; + for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) + totalLogicalWidth += curr->logicalWidth(); + int stripX = tx - (isHorizontal() ? logicalOffsetOnLine : 0); + int stripY = ty - (isHorizontal() ? 0 : logicalOffsetOnLine); + int stripWidth = isHorizontal() ? totalLogicalWidth : w; + int stripHeight = isHorizontal() ? h : totalLogicalWidth; + paintInfo.context->save(); + paintInfo.context->clip(IntRect(tx, ty, w, h)); + boxModelObject()->paintNinePieceImage(paintInfo.context, stripX, stripY, stripWidth, stripHeight, renderer()->style(), maskNinePieceImage, compositeOp); + paintInfo.context->restore(); + } + + if (pushTransparencyLayer) + paintInfo.context->endTransparencyLayer(); +} + +InlineBox* InlineFlowBox::firstLeafChild() const +{ + InlineBox* leaf = 0; + for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine()) + leaf = child->isLeaf() ? child : static_cast(child)->firstLeafChild(); + return leaf; +} + +InlineBox* InlineFlowBox::lastLeafChild() const +{ + InlineBox* leaf = 0; + for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine()) + leaf = child->isLeaf() ? child : static_cast(child)->lastLeafChild(); + return leaf; +} + +RenderObject::SelectionState InlineFlowBox::selectionState() +{ + return RenderObject::SelectionNone; +} + +bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) +{ + for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { + if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth)) + return false; + } + return true; +} + +int InlineFlowBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox) +{ + int result = -1; + // We iterate over all children, the foundBox variable tells us when we've found the + // box containing the ellipsis. All boxes after that one in the flow are hidden. + // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate + // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis. + InlineBox *box = ltr ? firstChild() : lastChild(); + + // NOTE: these will cross after foundBox = true. + int visibleLeftEdge = blockLeftEdge; + int visibleRightEdge = blockRightEdge; + + while (box) { + int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, foundBox); + if (currResult != -1 && result == -1) + result = currResult; + + if (ltr) { + visibleLeftEdge += box->logicalWidth(); + box = box->nextOnLine(); + } + else { + visibleRightEdge -= box->logicalWidth(); + box = box->prevOnLine(); + } + } + return result; +} + +void InlineFlowBox::clearTruncation() +{ + for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) + box->clearTruncation(); +} + +int InlineFlowBox::computeOverAnnotationAdjustment(int allowedPosition) const +{ + int result = 0; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) + result = max(result, static_cast(curr)->computeOverAnnotationAdjustment(allowedPosition)); + + if (curr->renderer()->isReplaced() && curr->renderer()->isRubyRun()) { + RenderRubyRun* rubyRun = static_cast(curr->renderer()); + RenderRubyText* rubyText = rubyRun->rubyText(); + if (!rubyText) + continue; + + if (!rubyRun->style()->isFlippedLinesWritingMode()) { + int topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : 0); + if (topOfFirstRubyTextLine >= 0) + continue; + topOfFirstRubyTextLine += curr->logicalTop(); + result = max(result, allowedPosition - topOfFirstRubyTextLine); + } else { + int bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight()); + if (bottomOfLastRubyTextLine <= curr->logicalHeight()) + continue; + bottomOfLastRubyTextLine += curr->logicalTop(); + result = max(result, bottomOfLastRubyTextLine - allowedPosition); + } + } + + if (curr->isInlineTextBox()) { + RenderStyle* style = curr->renderer()->style(m_firstLine); + TextEmphasisPosition emphasisMarkPosition; + if (style->textEmphasisMark() != TextEmphasisMarkNone && static_cast(curr)->getEmphasisMarkPosition(style, emphasisMarkPosition) && emphasisMarkPosition == TextEmphasisPositionOver) { + if (!style->isFlippedLinesWritingMode()) { + int topOfEmphasisMark = curr->logicalTop() - style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = max(result, allowedPosition - topOfEmphasisMark); + } else { + int bottomOfEmphasisMark = curr->logicalBottom() + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = max(result, bottomOfEmphasisMark - allowedPosition); + } + } + } + } + return result; +} + +int InlineFlowBox::computeUnderAnnotationAdjustment(int allowedPosition) const +{ + int result = 0; + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->renderer()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) + result = max(result, static_cast(curr)->computeUnderAnnotationAdjustment(allowedPosition)); + + if (curr->isInlineTextBox()) { + RenderStyle* style = curr->renderer()->style(m_firstLine); + if (style->textEmphasisMark() != TextEmphasisMarkNone && style->textEmphasisPosition() == TextEmphasisPositionUnder) { + if (!style->isFlippedLinesWritingMode()) { + int bottomOfEmphasisMark = curr->logicalBottom() + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = max(result, bottomOfEmphasisMark - allowedPosition); + } else { + int topOfEmphasisMark = curr->logicalTop() - style->font().emphasisMarkHeight(style->textEmphasisMarkString()); + result = max(result, allowedPosition - topOfEmphasisMark); + } + } + } + } + return result; +} + +#ifndef NDEBUG + +void InlineFlowBox::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + ASSERT(!m_hasBadChildList); + const InlineBox* prev = 0; + for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) { + ASSERT(child->parent() == this); + ASSERT(child->prevOnLine() == prev); + prev = child; + } + ASSERT(prev == m_lastChild); +#endif +} + +#endif + +} // namespace WebCore diff --git a/Source/WebCore/rendering/InlineFlowBox.h b/Source/WebCore/rendering/InlineFlowBox.h new file mode 100644 index 0000000..63263fd --- /dev/null +++ b/Source/WebCore/rendering/InlineFlowBox.h @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef InlineFlowBox_h +#define InlineFlowBox_h + +#include "InlineBox.h" +#include "RenderOverflow.h" + +namespace WebCore { + +class HitTestRequest; +class HitTestResult; +class InlineTextBox; +class RenderLineBoxList; +class VerticalPositionCache; + +typedef HashMap, GlyphOverflow> > GlyphOverflowAndFallbackFontsMap; + +class InlineFlowBox : public InlineBox { +public: + InlineFlowBox(RenderObject* obj) + : InlineBox(obj) + , m_firstChild(0) + , m_lastChild(0) + , m_prevLineBox(0) + , m_nextLineBox(0) + , m_includeLogicalLeftEdge(false) + , m_includeLogicalRightEdge(false) +#ifndef NDEBUG + , m_hasBadChildList(false) +#endif + { + // Internet Explorer and Firefox always create a marker for list items, even when the list-style-type is none. We do not make a marker + // in the list-style-type: none case, since it is wasteful to do so. However, in order to match other browsers we have to pretend like + // an invisible marker exists. The side effect of having an invisible marker is that the quirks mode behavior of shrinking lines with no + // text children must not apply. This change also means that gaps will exist between image bullet list items. Even when the list bullet + // is an image, the line is still considered to be immune from the quirk. + m_hasTextChildren = obj->style()->display() == LIST_ITEM; + } + +#ifndef NDEBUG + virtual ~InlineFlowBox(); +#endif + + InlineFlowBox* prevLineBox() const { return m_prevLineBox; } + InlineFlowBox* nextLineBox() const { return m_nextLineBox; } + void setNextLineBox(InlineFlowBox* n) { m_nextLineBox = n; } + void setPreviousLineBox(InlineFlowBox* p) { m_prevLineBox = p; } + + InlineBox* firstChild() const { checkConsistency(); return m_firstChild; } + InlineBox* lastChild() const { checkConsistency(); return m_lastChild; } + + virtual bool isLeaf() const { return false; } + + InlineBox* firstLeafChild() const; + InlineBox* lastLeafChild() const; + + virtual void setConstructed() + { + InlineBox::setConstructed(); + for (InlineBox* child = firstChild(); child; child = child->next()) + child->setConstructed(); + } + + void addToLine(InlineBox* child); + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + virtual void adjustPosition(int dx, int dy); + + virtual void extractLineBoxFromRenderObject(); + virtual void attachLineBoxToRenderObject(); + virtual void removeLineBoxFromRenderObject(); + + virtual void clearTruncation(); + + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo&, int tx, int ty); + void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int w, int h, CompositeOperator = CompositeSourceOver); + void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int w, int h, CompositeOperator = CompositeSourceOver); + void paintBoxShadow(GraphicsContext*, RenderStyle*, ShadowStyle, int tx, int ty, int w, int h); + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + virtual RenderLineBoxList* rendererLineBoxes() const; + + // logicalLeft = left in a horizontal line and top in a vertical line. + int marginBorderPaddingLogicalLeft() const { return marginLogicalLeft() + borderLogicalLeft() + paddingLogicalLeft(); } + int marginBorderPaddingLogicalRight() const { return marginLogicalRight() + borderLogicalRight() + paddingLogicalRight(); } + int marginLogicalLeft() const + { + if (!includeLogicalLeftEdge()) + return 0; + return isHorizontal() ? boxModelObject()->marginLeft() : boxModelObject()->marginTop(); + } + int marginLogicalRight() const + { + if (!includeLogicalRightEdge()) + return 0; + return isHorizontal() ? boxModelObject()->marginRight() : boxModelObject()->marginBottom(); + } + int borderLogicalLeft() const + { + if (!includeLogicalLeftEdge()) + return 0; + return isHorizontal() ? renderer()->style()->borderLeftWidth() : renderer()->style()->borderTopWidth(); + } + int borderLogicalRight() const + { + if (!includeLogicalRightEdge()) + return 0; + return isHorizontal() ? renderer()->style()->borderRightWidth() : renderer()->style()->borderBottomWidth(); + } + int paddingLogicalLeft() const + { + if (!includeLogicalLeftEdge()) + return 0; + return isHorizontal() ? boxModelObject()->paddingLeft() : boxModelObject()->paddingTop(); + } + int paddingLogicalRight() const + { + if (!includeLogicalRightEdge()) + return 0; + return isHorizontal() ? boxModelObject()->paddingRight() : boxModelObject()->paddingBottom(); + } + + bool includeLogicalLeftEdge() const { return m_includeLogicalLeftEdge; } + bool includeLogicalRightEdge() const { return m_includeLogicalRightEdge; } + void setEdges(bool includeLeft, bool includeRight) + { + m_includeLogicalLeftEdge = includeLeft; + m_includeLogicalRightEdge = includeRight; + } + + // Helper functions used during line construction and placement. + void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); + int getFlowSpacingLogicalWidth(); + bool onEndChain(RenderObject* endObject); + int placeBoxesInInlineDirection(int logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap&); + void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, + int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, + bool strictMode, GlyphOverflowAndFallbackFontsMap&, FontBaseline, VerticalPositionCache&); + void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, + int maxPositionTop, int maxPositionBottom); + void placeBoxesInBlockDirection(int logicalTop, int maxHeight, int maxAscent, bool strictMode, int& lineTop, int& lineBottom, bool& setLineTop, + int& lineTopIncludingMargins, int& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline); + void flipLinesInBlockDirection(int lineTop, int lineBottom); + bool requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap&) const; + + int computeOverAnnotationAdjustment(int allowedPosition) const; + int computeUnderAnnotationAdjustment(int allowedPosition) const; + + void computeOverflow(int lineTop, int lineBottom, bool strictMode, GlyphOverflowAndFallbackFontsMap&); + + void removeChild(InlineBox* child); + + virtual RenderObject::SelectionState selectionState(); + + virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth); + virtual int placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool&); + + bool hasTextChildren() const { return m_hasTextChildren; } + + void checkConsistency() const; + void setHasBadChildList(); + + // Line visual and layout overflow are in the coordinate space of the block. This means that - unlike other unprefixed uses of the words + // top/right/bottom/left in the code - these aren't purely physical directions. For horizontal-tb and vertical-lr they will match physical + // directions, but for horizontal-bt and vertical-rl, the top/bottom and left/right respectively are inverted when compared to + // their physical counterparts. + int topLayoutOverflow() const { return m_overflow ? m_overflow->topLayoutOverflow() : m_y; } + int bottomLayoutOverflow() const { return m_overflow ? m_overflow->bottomLayoutOverflow() : m_y + height(); } + int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : m_x; } + int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : m_x + width(); } + IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : IntRect(m_x, m_y, width(), height()); } + int logicalLeftLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? leftLayoutOverflow() : topLayoutOverflow(); } + int logicalRightLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? rightLayoutOverflow() : bottomLayoutOverflow(); } + int logicalTopLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? topVisualOverflow() : leftVisualOverflow(); } + int logicalBottomLayoutOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? bottomLayoutOverflow() : rightLayoutOverflow(); } + IntRect logicalLayoutOverflowRect() const + { + IntRect result = layoutOverflowRect(); + if (!renderer()->style()->isHorizontalWritingMode()) + result = result.transposedRect(); + return result; + } + + int topVisualOverflow() const { return m_overflow ? m_overflow->topVisualOverflow() : m_y; } + int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : m_y + height(); } + int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : m_x; } + int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : m_x + width(); } + IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : IntRect(m_x, m_y, width(), height()); } + int logicalLeftVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? leftVisualOverflow() : topVisualOverflow(); } + int logicalRightVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? rightVisualOverflow() : bottomVisualOverflow(); } + int logicalTopVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? topVisualOverflow() : leftVisualOverflow(); } + int logicalBottomVisualOverflow() const { return renderer()->style()->isHorizontalWritingMode() ? bottomVisualOverflow() : rightVisualOverflow(); } + IntRect logicalVisualOverflowRect() const + { + IntRect result = visualOverflowRect(); + if (!renderer()->style()->isHorizontalWritingMode()) + result = result.transposedRect(); + return result; + } + + void setOverflowFromLogicalRects(const IntRect& logicalLayoutOverflow, const IntRect& logicalVisualOverflow); + void setLayoutOverflow(const IntRect&); + void setVisualOverflow(const IntRect&); + +private: + void addBoxShadowVisualOverflow(IntRect& logicalVisualOverflow); + void addTextBoxVisualOverflow(const InlineTextBox*, GlyphOverflowAndFallbackFontsMap&, IntRect& logicalVisualOverflow); + void addReplacedChildOverflow(const InlineBox*, IntRect& logicalLayoutOverflow, IntRect& logicalVisualOverflow); + +protected: + OwnPtr m_overflow; + + virtual bool isInlineFlowBox() const { return true; } + + InlineBox* m_firstChild; + InlineBox* m_lastChild; + + InlineFlowBox* m_prevLineBox; // The previous box that also uses our RenderObject + InlineFlowBox* m_nextLineBox; // The next box that also uses our RenderObject + + bool m_includeLogicalLeftEdge : 1; + bool m_includeLogicalRightEdge : 1; + bool m_hasTextChildren : 1; + +#ifndef NDEBUG + bool m_hasBadChildList; +#endif +}; + +#ifdef NDEBUG +inline void InlineFlowBox::checkConsistency() const +{ +} +#endif + +inline void InlineFlowBox::setHasBadChildList() +{ +#ifndef NDEBUG + m_hasBadChildList = true; +#endif +} + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::InlineFlowBox*); +#endif + +#endif // InlineFlowBox_h diff --git a/Source/WebCore/rendering/InlineIterator.h b/Source/WebCore/rendering/InlineIterator.h new file mode 100644 index 0000000..270364f --- /dev/null +++ b/Source/WebCore/rendering/InlineIterator.h @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef InlineIterator_h +#define InlineIterator_h + +#include "BidiRun.h" +#include "RenderBlock.h" +#include "RenderText.h" +#include +#include + +namespace WebCore { + +class InlineIterator { +public: + InlineIterator() + : block(0) + , obj(0) + , pos(0) + , nextBreakablePosition(-1) + { + } + + InlineIterator(RenderBlock* b, RenderObject* o, unsigned p) + : block(b) + , obj(o) + , pos(p) + , nextBreakablePosition(-1) + { + } + + void increment(InlineBidiResolver* resolver = 0); + bool atEnd() const; + + UChar current() const; + ALWAYS_INLINE WTF::Unicode::Direction direction() const; + + RenderBlock* block; + RenderObject* obj; + unsigned pos; + int nextBreakablePosition; +}; + +inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos == it2.pos && it1.obj == it2.obj; +} + +inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos != it2.pos || it1.obj != it2.obj; +} + +static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0) +{ + RenderObject* next = 0; + bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; + bool endOfInline = false; + + while (current) { + next = 0; + if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned() && !current->isText()) { + next = current->firstChild(); + if (next && resolver && next->isRenderInline()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + } + + if (!next) { + if (!skipInlines && !oldEndOfInline && current->isRenderInline()) { + next = current; + endOfInline = true; + break; + } + + while (current && current != block) { + if (resolver && current->isRenderInline() && current->style()->unicodeBidi() != UBNormal) + resolver->embed(WTF::Unicode::PopDirectionalFormat); + + next = current->nextSibling(); + if (next) { + if (resolver && next->isRenderInline()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding: WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + break; + } + + current = current->parent(); + if (!skipInlines && current && current != block && current->isRenderInline()) { + next = current; + endOfInline = true; + break; + } + } + } + + if (!next) + break; + + if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned() + || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines. + && next->isRenderInline())) + break; + current = next; + } + + if (endOfInlinePtr) + *endOfInlinePtr = endOfInline; + + return next; +} + +static inline RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true) +{ + if (!block->firstChild()) + return 0; + + RenderObject* o = block->firstChild(); + if (o->isRenderInline()) { + if (resolver) { + EUnicodeBidi ub = o->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = o->style()->direction(); + WTF::Unicode::Direction d = (ub == Embed + ? (dir == RTL ? WTF::Unicode::RightToLeftEmbedding : WTF::Unicode::LeftToRightEmbedding) + : (dir == RTL ? WTF::Unicode::RightToLeftOverride : WTF::Unicode::LeftToRightOverride)); + resolver->embed(d); + } + } + if (skipInlines && o->firstChild()) + o = bidiNext(block, o, resolver, skipInlines); + else { + // Never skip empty inlines. + if (resolver) + resolver->commitExplicitEmbedding(); + return o; + } + } + + if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned()) + o = bidiNext(block, o, resolver, skipInlines); + + if (resolver) + resolver->commitExplicitEmbedding(); + return o; +} + +inline void InlineIterator::increment(InlineBidiResolver* resolver) +{ + if (!obj) + return; + if (obj->isText()) { + pos++; + if (pos >= toRenderText(obj)->textLength()) { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } + } else { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } +} + +inline bool InlineIterator::atEnd() const +{ + return !obj; +} + +inline UChar InlineIterator::current() const +{ + if (!obj || !obj->isText()) + return 0; + + RenderText* text = toRenderText(obj); + if (pos >= text->textLength()) + return 0; + + return text->characters()[pos]; +} + +ALWAYS_INLINE WTF::Unicode::Direction InlineIterator::direction() const +{ + if (UChar c = current()) + return WTF::Unicode::direction(c); + + if (obj && obj->isListMarker()) + return obj->style()->isLeftToRightDirection() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; + + return WTF::Unicode::OtherNeutral; +} + +template<> +inline void InlineBidiResolver::increment() +{ + current.increment(this); +} + +template <> +inline void InlineBidiResolver::appendRun() +{ + if (!emptyRun && !eor.atEnd()) { + int start = sor.pos; + RenderObject *obj = sor.obj; + while (obj && obj != eor.obj && obj != endOfLine.obj) { + RenderBlock::appendRunsForObject(start, obj->length(), obj, *this); + start = 0; + obj = bidiNext(sor.block, obj); + } + if (obj) { + unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX; + if (obj == endOfLine.obj && endOfLine.pos <= pos) { + reachedEndOfLine = true; + pos = endOfLine.pos; + } + // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be + int end = obj->length() ? pos+1 : 0; + RenderBlock::appendRunsForObject(start, end, obj, *this); + } + + eor.increment(); + sor = eor; + } + + m_direction = WTF::Unicode::OtherNeutral; + m_status.eor = WTF::Unicode::OtherNeutral; +} + +} + +#endif // InlineIterator_h diff --git a/Source/WebCore/rendering/InlineTextBox.cpp b/Source/WebCore/rendering/InlineTextBox.cpp new file mode 100644 index 0000000..8884ed1 --- /dev/null +++ b/Source/WebCore/rendering/InlineTextBox.cpp @@ -0,0 +1,1292 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "InlineTextBox.h" + +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "DocumentMarkerController.h" +#include "Editor.h" +#include "EllipsisBox.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "RenderRubyRun.h" +#include "RenderRubyText.h" +#include "RenderTheme.h" +#include "Text.h" +#include "break_lines.h" +#include + +using namespace std; + +namespace WebCore { + +int InlineTextBox::baselinePosition(FontBaseline baselineType) const +{ + if (!isText() || !parent()) + return 0; + return parent()->baselinePosition(baselineType); +} + +int InlineTextBox::lineHeight() const +{ + if (!isText() || !parent()) + return 0; + if (m_renderer->isBR()) + return toRenderBR(m_renderer)->lineHeight(m_firstLine); + return parent()->lineHeight(); +} + +int InlineTextBox::selectionTop() +{ + return root()->selectionTop(); +} + +int InlineTextBox::selectionBottom() +{ + return root()->selectionBottom(); +} + +int InlineTextBox::selectionHeight() +{ + return root()->selectionHeight(); +} + +bool InlineTextBox::isSelected(int startPos, int endPos) const +{ + int sPos = max(startPos - m_start, 0); + int ePos = min(endPos - m_start, (int)m_len); + return (sPos < ePos); +} + +RenderObject::SelectionState InlineTextBox::selectionState() +{ + RenderObject::SelectionState state = renderer()->selectionState(); + if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) { + int startPos, endPos; + renderer()->selectionStartEnd(startPos, endPos); + // The position after a hard line break is considered to be past its end. + int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); + + bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len); + bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable); + if (start && end) + state = RenderObject::SelectionBoth; + else if (start) + state = RenderObject::SelectionStart; + else if (end) + state = RenderObject::SelectionEnd; + else if ((state == RenderObject::SelectionEnd || startPos < m_start) && + (state == RenderObject::SelectionStart || endPos > lastSelectable)) + state = RenderObject::SelectionInside; + else if (state == RenderObject::SelectionBoth) + state = RenderObject::SelectionNone; + } + + // If there are ellipsis following, make sure their selection is updated. + if (m_truncation != cNoTruncation && root()->ellipsisBox()) { + EllipsisBox* ellipsis = root()->ellipsisBox(); + if (state != RenderObject::SelectionNone) { + int start, end; + selectionStartEnd(start, end); + // The ellipsis should be considered to be selected if the end of + // the selection is past the beginning of the truncation and the + // beginning of the selection is before or at the beginning of the + // truncation. + ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ? + RenderObject::SelectionInside : RenderObject::SelectionNone); + } else + ellipsis->setSelectionState(RenderObject::SelectionNone); + } + + return state; +} + +typedef Vector BufferForAppendingHyphen; + +static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, RenderStyle* style, const UChar*& characters, int& length) +{ + const AtomicString& hyphenString = style->hyphenString(); + charactersWithHyphen.reserveCapacity(length + hyphenString.length()); + charactersWithHyphen.append(characters, length); + charactersWithHyphen.append(hyphenString.characters(), hyphenString.length()); + characters = charactersWithHyphen.data(); + length += hyphenString.length(); +} + +IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) +{ + int sPos = max(startPos - m_start, 0); + int ePos = min(endPos - m_start, (int)m_len); + + if (sPos > ePos) + return IntRect(); + + RenderText* textObj = textRenderer(); + int selTop = selectionTop(); + int selHeight = selectionHeight(); + RenderStyle* styleToUse = textObj->style(m_firstLine); + const Font& f = styleToUse->font(); + + const UChar* characters = textObj->text()->characters() + m_start; + int len = m_len; + BufferForAppendingHyphen charactersWithHyphen; + if (ePos == len && hasHyphen()) { + adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, len); + ePos = len; + } + +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + TextRun textRun = TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride); + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); + IntRect r = enclosingIntRect(f.selectionRectForText(textRun, IntPoint(), selHeight, sPos, ePos)); +#else + IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(characters, len, textObj->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride), + IntPoint(), selHeight, sPos, ePos)); +#endif + + int logicalWidth = r.width(); + if (r.x() > m_logicalWidth) + logicalWidth = 0; + else if (r.right() > m_logicalWidth) + logicalWidth = m_logicalWidth - r.x(); + + IntPoint topPoint = isHorizontal() ? IntPoint(tx + m_x + r.x(), ty + selTop) : IntPoint(tx + selTop, ty + m_y + r.x()); + int width = isHorizontal() ? logicalWidth : selHeight; + int height = isHorizontal() ? selHeight : logicalWidth; + + return IntRect(topPoint, IntSize(width, height)); +} + +void InlineTextBox::deleteLine(RenderArena* arena) +{ + toRenderText(renderer())->removeTextBox(this); + destroy(arena); +} + +void InlineTextBox::extractLine() +{ + if (m_extracted) + return; + + toRenderText(renderer())->extractTextBox(this); +} + +void InlineTextBox::attachLine() +{ + if (!m_extracted) + return; + + toRenderText(renderer())->attachTextBox(this); +} + +int InlineTextBox::placeEllipsisBox(bool flowIsLTR, int visibleLeftEdge, int visibleRightEdge, int ellipsisWidth, bool& foundBox) +{ + if (foundBox) { + m_truncation = cFullTruncation; + return -1; + } + + // For LTR this is the left edge of the box, for RTL, the right edge in parent coordinates. + int ellipsisX = flowIsLTR ? visibleRightEdge - ellipsisWidth : visibleLeftEdge + ellipsisWidth; + + // Criteria for full truncation: + // LTR: the left edge of the ellipsis is to the left of our text run. + // RTL: the right edge of the ellipsis is to the right of our text run. + bool ltrFullTruncation = flowIsLTR && ellipsisX <= m_x; + bool rtlFullTruncation = !flowIsLTR && ellipsisX >= (m_x + m_logicalWidth); + if (ltrFullTruncation || rtlFullTruncation) { + // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box. + m_truncation = cFullTruncation; + foundBox = true; + return -1; + } + + bool ltrEllipsisWithinBox = flowIsLTR && (ellipsisX < m_x + m_logicalWidth); + bool rtlEllipsisWithinBox = !flowIsLTR && (ellipsisX > m_x); + if (ltrEllipsisWithinBox || rtlEllipsisWithinBox) { + foundBox = true; + + // The inline box may have different directionality than it's parent. Since truncation + // behavior depends both on both the parent and the inline block's directionality, we + // must keep track of these separately. + bool ltr = isLeftToRightDirection(); + if (ltr != flowIsLTR) { + // Width in pixels of the visible portion of the box, excluding the ellipsis. + int visibleBoxWidth = visibleRightEdge - visibleLeftEdge - ellipsisWidth; + ellipsisX = ltr ? m_x + visibleBoxWidth : m_x + m_logicalWidth - visibleBoxWidth; + } + + int offset = offsetForPosition(ellipsisX, false); + if (offset == 0) { + // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start + // and the ellipsis edge. + m_truncation = cFullTruncation; + return min(ellipsisX, m_x); + } + + // Set the truncation index on the text run. + m_truncation = offset; + + // If we got here that means that we were only partially truncated and we need to return the pixel offset at which + // to place the ellipsis. + int widthOfVisibleText = toRenderText(renderer())->width(m_start, offset, textPos(), m_firstLine); + + // The ellipsis needs to be placed just after the last visible character. + // Where "after" is defined by the flow directionality, not the inline + // box directionality. + // e.g. In the case of an LTR inline box truncated in an RTL flow then we can + // have a situation such as |Hello| -> |...He| + if (flowIsLTR) + return m_x + widthOfVisibleText; + else + return (m_x + m_logicalWidth) - widthOfVisibleText - ellipsisWidth; + } + return -1; +} + +Color correctedTextColor(Color textColor, Color backgroundColor) +{ + // Adjust the text color if it is too close to the background color, + // by darkening or lightening it to move it further away. + + int d = differenceSquared(textColor, backgroundColor); + // semi-arbitrarily chose 65025 (255^2) value here after a few tests; + if (d > 65025) { + return textColor; + } + + int distanceFromWhite = differenceSquared(textColor, Color::white); + int distanceFromBlack = differenceSquared(textColor, Color::black); + + if (distanceFromWhite < distanceFromBlack) { + return textColor.dark(); + } + + return textColor.light(); +} + +void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace colorSpace) +{ + TextDrawingModeFlags mode = context->textDrawingMode(); + if (strokeThickness > 0) { + TextDrawingModeFlags newMode = mode | TextModeStroke; + if (mode != newMode) { + context->setTextDrawingMode(newMode); + mode = newMode; + } + } + + if (mode & TextModeFill && (fillColor != context->fillColor() || colorSpace != context->fillColorSpace())) + context->setFillColor(fillColor, colorSpace); + + if (mode & TextModeStroke) { + if (strokeColor != context->strokeColor()) + context->setStrokeColor(strokeColor, colorSpace); + if (strokeThickness != context->strokeThickness()) + context->setStrokeThickness(strokeThickness); + } +} + +bool InlineTextBox::isLineBreak() const +{ + return renderer()->isBR() || (renderer()->style()->preserveNewline() && len() == 1 && (*textRenderer()->text())[start()] == '\n'); +} + +bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty) +{ + if (isLineBreak()) + return false; + + IntPoint boxOrigin = locationIncludingFlipping(); + boxOrigin.move(tx, ty); + IntRect rect(boxOrigin, IntSize(width(), height())); + if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.intersects(result.rectForPoint(x, y))) { + renderer()->updateHitTestResult(result, flipForWritingMode(IntPoint(x - tx, y - ty))); + if (!result.addNodeToRectBasedTestResult(renderer()->node(), x, y, rect)) + return true; + } + return false; +} + +FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, const ShadowData* shadow, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal) +{ + if (!shadow) + return FloatSize(); + + FloatSize extraOffset; + int shadowX = horizontal ? shadow->x() : shadow->y(); + int shadowY = horizontal ? shadow->y() : -shadow->x(); + FloatSize shadowOffset(shadowX, shadowY); + int shadowBlur = shadow->blur(); + const Color& shadowColor = shadow->color(); + + if (shadow->next() || stroked || !opaque) { + FloatRect shadowRect(textRect); + shadowRect.inflate(shadowBlur); + shadowRect.move(shadowOffset); + context->save(); + context->clip(shadowRect); + + extraOffset = FloatSize(0, 2 * textRect.height() + max(0.0f, shadowOffset.height()) + shadowBlur); + shadowOffset -= extraOffset; + } + + context->setShadow(shadowOffset, shadowBlur, shadowColor, context->fillColorSpace()); + return extraOffset; +} + +static void paintTextWithShadows(GraphicsContext* context, const Font& font, const TextRun& textRun, const AtomicString& emphasisMark, int emphasisMarkOffset, int startOffset, int endOffset, int truncationPoint, const IntPoint& textOrigin, + const IntRect& boxRect, const ShadowData* shadow, bool stroked, bool horizontal) +{ + Color fillColor = context->fillColor(); + ColorSpace fillColorSpace = context->fillColorSpace(); + bool opaque = fillColor.alpha() == 255; + if (!opaque) + context->setFillColor(Color::black, fillColorSpace); + + do { + IntSize extraOffset; + if (shadow) + extraOffset = roundedIntSize(InlineTextBox::applyShadowToGraphicsContext(context, shadow, boxRect, stroked, opaque, horizontal)); + else if (!opaque) + context->setFillColor(fillColor, fillColorSpace); + + if (startOffset <= endOffset) { + if (emphasisMark.isEmpty()) + context->drawText(font, textRun, textOrigin + extraOffset, startOffset, endOffset); + else + context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, endOffset); + } else { + if (endOffset > 0) { + if (emphasisMark.isEmpty()) + context->drawText(font, textRun, textOrigin + extraOffset, 0, endOffset); + else + context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), 0, endOffset); + } if (startOffset < truncationPoint) { + if (emphasisMark.isEmpty()) + context->drawText(font, textRun, textOrigin + extraOffset, startOffset, truncationPoint); + else + context->drawEmphasisMarks(font, textRun, emphasisMark, textOrigin + extraOffset + IntSize(0, emphasisMarkOffset), startOffset, truncationPoint); + } + } + + if (!shadow) + break; + + if (shadow->next() || stroked || !opaque) + context->restore(); + else + context->clearShadow(); + + shadow = shadow->next(); + } while (shadow || stroked || !opaque); +} + +bool InlineTextBox::getEmphasisMarkPosition(RenderStyle* style, TextEmphasisPosition& emphasisPosition) const +{ + // This function returns true if there are text emphasis marks and they are suppressed by ruby text. + if (style->textEmphasisMark() == TextEmphasisMarkNone) + return false; + + emphasisPosition = style->textEmphasisPosition(); + if (emphasisPosition == TextEmphasisPositionUnder) + return true; // Ruby text is always over, so it cannot suppress emphasis marks under. + + RenderBlock* containingBlock = renderer()->containingBlock(); + if (!containingBlock->isRubyBase()) + return true; // This text is not inside a ruby base, so it does not have ruby text over it. + + if (!containingBlock->parent()->isRubyRun()) + return true; // Cannot get the ruby text. + + RenderRubyText* rubyText = static_cast(containingBlock->parent())->rubyText(); + + // The emphasis marks over are suppressed only if there is a ruby text box and it not empty. + return !rubyText || !rubyText->firstLineBox(); +} + +void InlineTextBox::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (isLineBreak() || !paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || + m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline || !m_len) + return; + + ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); + + // FIXME: Technically we're potentially incorporating other visual overflow that had nothing to do with us. + // Would it be simpler to just check our own shadow and stroke overflow by hand here? + int logicalLeftOverflow = parent()->logicalLeft() - parent()->logicalLeftVisualOverflow(); + int logicalRightOverflow = parent()->logicalRightVisualOverflow() - (parent()->logicalLeft() + parent()->logicalWidth()); + int logicalStart = logicalLeft() - logicalLeftOverflow + (isHorizontal() ? tx : ty); + int logicalExtent = logicalWidth() + logicalLeftOverflow + logicalRightOverflow; + + int paintEnd = isHorizontal() ? paintInfo.rect.right() : paintInfo.rect.bottom(); + int paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y(); + + if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart) + return; + + bool isPrinting = textRenderer()->document()->printing(); + + // Determine whether or not we're selected. + bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; + if (!haveSelection && paintInfo.phase == PaintPhaseSelection) + // When only painting the selection, don't bother to paint if there is none. + return; + + if (m_truncation != cNoTruncation) { + if (renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection()) { + // Make the visible fragment of text hug the edge closest to the rest of the run by moving the origin + // at which we start drawing text. + // e.g. In the case of LTR text truncated in an RTL Context, the correct behavior is: + // |Hello|CBA| -> |...He|CBA| + // In order to draw the fragment "He" aligned to the right edge of it's box, we need to start drawing + // farther to the right. + // NOTE: WebKit's behavior differs from that of IE which appears to just overlay the ellipsis on top of the + // truncated string i.e. |Hello|CBA| -> |...lo|CBA| + int widthOfVisibleText = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine); + int widthOfHiddenText = m_logicalWidth - widthOfVisibleText; + // FIXME: The hit testing logic also needs to take this translation int account. + if (isHorizontal()) + tx += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText; + else + ty += isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText; + } + } + + GraphicsContext* context = paintInfo.context; + + RenderStyle* styleToUse = renderer()->style(m_firstLine); + + ty -= styleToUse->isHorizontalWritingMode() ? 0 : logicalHeight(); + + IntPoint boxOrigin = locationIncludingFlipping(); + boxOrigin.move(tx, ty); + IntRect boxRect(boxOrigin, IntSize(logicalWidth(), logicalHeight())); + IntPoint textOrigin = IntPoint(boxOrigin.x(), boxOrigin.y() + styleToUse->font().ascent()); + + if (!isHorizontal()) { + context->save(); + context->translate(boxRect.x(), boxRect.bottom()); + context->rotate(static_cast(deg2rad(90.))); + context->translate(-boxRect.x(), -boxRect.bottom()); + } + + + // Determine whether or not we have composition underlines to draw. + bool containsComposition = renderer()->node() && renderer()->frame()->editor()->compositionNode() == renderer()->node(); + bool useCustomUnderlines = containsComposition && renderer()->frame()->editor()->compositionUsesCustomUnderlines(); + + // Set our font. + int d = styleToUse->textDecorationsInEffect(); + const Font& font = styleToUse->font(); + + // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection + // and composition underlines. + if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) { +#if PLATFORM(MAC) + // Custom highlighters go behind everything else. + if (styleToUse->highlight() != nullAtom && !context->paintingDisabled()) + paintCustomHighlight(tx, ty, styleToUse->highlight()); +#endif + + if (containsComposition && !useCustomUnderlines) + paintCompositionBackground(context, boxOrigin, styleToUse, font, + renderer()->frame()->editor()->compositionStart(), + renderer()->frame()->editor()->compositionEnd()); + + paintDocumentMarkers(context, boxOrigin, styleToUse, font, true); + + if (haveSelection && !useCustomUnderlines) + paintSelection(context, boxOrigin, styleToUse, font); + } + + // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). + Color textFillColor; + Color textStrokeColor; + Color emphasisMarkColor; + float textStrokeWidth = styleToUse->textStrokeWidth(); + const ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow(); + + if (paintInfo.forceBlackText) { + textFillColor = Color::black; + textStrokeColor = Color::black; + emphasisMarkColor = Color::black; + } else { + textFillColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextFillColor); + + // Make the text fill color legible against a white background + if (styleToUse->forceBackgroundsToWhite()) + textFillColor = correctedTextColor(textFillColor, Color::white); + + textStrokeColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); + + // Make the text stroke color legible against a white background + if (styleToUse->forceBackgroundsToWhite()) + textStrokeColor = correctedTextColor(textStrokeColor, Color::white); + + emphasisMarkColor = styleToUse->visitedDependentColor(CSSPropertyWebkitTextEmphasisColor); + + // Make the text stroke color legible against a white background + if (styleToUse->forceBackgroundsToWhite()) + emphasisMarkColor = correctedTextColor(emphasisMarkColor, Color::white); + } + + bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); + bool paintSelectedTextSeparately = false; + + Color selectionFillColor = textFillColor; + Color selectionStrokeColor = textStrokeColor; + Color selectionEmphasisMarkColor = emphasisMarkColor; + float selectionStrokeWidth = textStrokeWidth; + const ShadowData* selectionShadow = textShadow; + if (haveSelection) { + // Check foreground color first. + Color foreground = paintInfo.forceBlackText ? Color::black : renderer()->selectionForegroundColor(); + if (foreground.isValid() && foreground != selectionFillColor) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionFillColor = foreground; + } + + Color emphasisMarkForeground = paintInfo.forceBlackText ? Color::black : renderer()->selectionEmphasisMarkColor(); + if (emphasisMarkForeground.isValid() && emphasisMarkForeground != selectionEmphasisMarkColor) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionEmphasisMarkColor = emphasisMarkForeground; + } + + if (RenderStyle* pseudoStyle = renderer()->getCachedPseudoStyle(SELECTION)) { + const ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); + if (shadow != selectionShadow) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionShadow = shadow; + } + + float strokeWidth = pseudoStyle->textStrokeWidth(); + if (strokeWidth != selectionStrokeWidth) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionStrokeWidth = strokeWidth; + } + + Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->visitedDependentColor(CSSPropertyWebkitTextStrokeColor); + if (stroke != selectionStrokeColor) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionStrokeColor = stroke; + } + } + } + + const UChar* characters = textRenderer()->text()->characters() + m_start; + int length = m_len; + BufferForAppendingHyphen charactersWithHyphen; + if (hasHyphen()) + adjustCharactersAndLengthForHyphen(charactersWithHyphen, styleToUse, characters, length); + + TextRun textRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || styleToUse->visuallyOrdered()); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); +#endif + + int sPos = 0; + int ePos = 0; + if (paintSelectedTextOnly || paintSelectedTextSeparately) + selectionStartEnd(sPos, ePos); + + if (m_truncation != cNoTruncation) { + sPos = min(sPos, m_truncation); + ePos = min(ePos, m_truncation); + length = m_truncation; + } + + int emphasisMarkOffset = 0; + TextEmphasisPosition emphasisMarkPosition; + bool hasTextEmphasis = getEmphasisMarkPosition(styleToUse, emphasisMarkPosition); + const AtomicString& emphasisMark = hasTextEmphasis ? styleToUse->textEmphasisMarkString() : nullAtom; + if (!emphasisMark.isEmpty()) + emphasisMarkOffset = emphasisMarkPosition == TextEmphasisPositionOver ? -font.ascent() - font.emphasisMarkDescent(emphasisMark) : font.descent() + font.emphasisMarkAscent(emphasisMark); + + if (!paintSelectedTextOnly) { + // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side + // effect, so only when we know we're stroking, do a save/restore. + if (textStrokeWidth > 0) + context->save(); + + updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + if (!paintSelectedTextSeparately || ePos <= sPos) { + // FIXME: Truncate right-to-left text correctly. + paintTextWithShadows(context, font, textRun, nullAtom, 0, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + } else + paintTextWithShadows(context, font, textRun, nullAtom, 0, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + + if (!emphasisMark.isEmpty()) { + updateGraphicsContext(context, emphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + if (!paintSelectedTextSeparately || ePos <= sPos) { + // FIXME: Truncate right-to-left text correctly. + paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, 0, length, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + } else + paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, ePos, sPos, length, textOrigin, boxRect, textShadow, textStrokeWidth > 0, isHorizontal()); + } + + if (textStrokeWidth > 0) + context->restore(); + } + + if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) { + // paint only the text that is selected + if (selectionStrokeWidth > 0) + context->save(); + + updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth, styleToUse->colorSpace()); + paintTextWithShadows(context, font, textRun, nullAtom, 0, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); + if (!emphasisMark.isEmpty()) { + updateGraphicsContext(context, selectionEmphasisMarkColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + paintTextWithShadows(context, font, textRun, emphasisMark, emphasisMarkOffset, sPos, ePos, length, textOrigin, boxRect, selectionShadow, selectionStrokeWidth > 0, isHorizontal()); + } + if (selectionStrokeWidth > 0) + context->restore(); + } + + // Paint decorations + if (d != TDNONE && paintInfo.phase != PaintPhaseSelection) { + updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth, styleToUse->colorSpace()); + paintDecoration(context, boxOrigin, d, textShadow); + } + + if (paintInfo.phase == PaintPhaseForeground) { + paintDocumentMarkers(context, boxOrigin, styleToUse, font, false); + + if (useCustomUnderlines) { + const Vector& underlines = renderer()->frame()->editor()->customCompositionUnderlines(); + size_t numUnderlines = underlines.size(); + + for (size_t index = 0; index < numUnderlines; ++index) { + const CompositionUnderline& underline = underlines[index]; + + if (underline.endOffset <= start()) + // underline is completely before this run. This might be an underline that sits + // before the first run we draw, or underlines that were within runs we skipped + // due to truncation. + continue; + + if (underline.startOffset <= end()) { + // underline intersects this run. Paint it. + paintCompositionUnderline(context, boxOrigin, underline); + if (underline.endOffset > end() + 1) + // underline also runs into the next run. Bail now, no more marker advancement. + break; + } else + // underline is completely after this run, bail. A later run will paint it. + break; + } + } + } + + if (!isHorizontal()) + context->restore(); +} + +void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) +{ + int startPos, endPos; + if (renderer()->selectionState() == RenderObject::SelectionInside) { + startPos = 0; + endPos = textRenderer()->textLength(); + } else { + textRenderer()->selectionStartEnd(startPos, endPos); + if (renderer()->selectionState() == RenderObject::SelectionStart) + endPos = textRenderer()->textLength(); + else if (renderer()->selectionState() == RenderObject::SelectionEnd) + startPos = 0; + } + + sPos = max(startPos - m_start, 0); + ePos = min(endPos - m_start, (int)m_len); +} + +void InlineTextBox::paintSelection(GraphicsContext* context, const IntPoint& boxOrigin, RenderStyle* style, const Font& font) +{ + // See if we have a selection to paint at all. + int sPos, ePos; + selectionStartEnd(sPos, ePos); + if (sPos >= ePos) + return; + + Color textColor = style->visitedDependentColor(CSSPropertyColor); + Color c = renderer()->selectionBackgroundColor(); + if (!c.isValid() || c.alpha() == 0) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. This should basically never happen, since the selection has transparency. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + context->save(); + updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all! + + // If the text is truncated, let the thing being painted in the truncation + // draw its own highlight. + int length = m_truncation != cNoTruncation ? m_truncation : m_len; + const UChar* characters = textRenderer()->text()->characters() + m_start; + + BufferForAppendingHyphen charactersWithHyphen; + if (ePos == length && hasHyphen()) { + adjustCharactersAndLengthForHyphen(charactersWithHyphen, style, characters, length); + ePos = length; + } + + int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); + int selHeight = selectionHeight(); + IntPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); + context->clip(IntRect(localOrigin, IntSize(m_logicalWidth, selHeight))); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + TextRun textRun = TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); + context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); +#else + context->drawHighlightForText(font, TextRun(characters, length, textRenderer()->allowTabs(), textPos(), m_toAdd, + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), + localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); +#endif + context->restore(); +} + +void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const IntPoint& boxOrigin, RenderStyle* style, const Font& font, int startPos, int endPos) +{ + int offset = m_start; + int sPos = max(startPos - offset, 0); + int ePos = min(endPos - offset, (int)m_len); + + if (sPos >= ePos) + return; + + context->save(); + + Color c = Color(225, 221, 85); + + updateGraphicsContext(context, c, c, 0, style->colorSpace()); // Don't draw text at all! + + int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); + int selHeight = selectionHeight(); + IntPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + TextRun textRun = TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); + context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); +#else + context->drawHighlightForText(font, TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, + !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), + localOrigin, selHeight, c, style->colorSpace(), sPos, ePos); +#endif + context->restore(); +} + +#if PLATFORM(MAC) + +void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type) +{ + Frame* frame = renderer()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + RootInlineBox* r = root(); + FloatRect rootRect(tx + r->x(), ty + selectionTop(), r->logicalWidth(), selectionHeight()); + FloatRect textRect(tx + x(), rootRect.y(), logicalWidth(), rootRect.height()); + + page->chrome()->client()->paintCustomHighlight(renderer()->node(), type, textRect, rootRect, true, false); +} + +#endif + +void InlineTextBox::paintDecoration(GraphicsContext* context, const IntPoint& boxOrigin, int deco, const ShadowData* shadow) +{ + if (m_truncation == cFullTruncation) + return; + + IntPoint localOrigin = boxOrigin; + + int width = m_logicalWidth; + if (m_truncation != cNoTruncation) { + width = toRenderText(renderer())->width(m_start, m_truncation, textPos(), m_firstLine); + if (!isLeftToRightDirection()) + localOrigin.move(m_logicalWidth - width, 0); + } + + // Get the text decoration colors. + Color underline, overline, linethrough; + renderer()->getTextDecorationColors(deco, underline, overline, linethrough, true); + + // Use a special function for underlines to get the positioning exactly right. + bool isPrinting = textRenderer()->document()->printing(); + context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. + + bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255); + + RenderStyle* styleToUse = renderer()->style(m_firstLine); + int baseline = styleToUse->font().ascent(); + + bool setClip = false; + int extraOffset = 0; + if (!linesAreOpaque && shadow && shadow->next()) { + context->save(); + IntRect clipRect(localOrigin, IntSize(width, baseline + 2)); + for (const ShadowData* s = shadow; s; s = s->next()) { + IntRect shadowRect(localOrigin, IntSize(width, baseline + 2)); + shadowRect.inflate(s->blur()); + int shadowX = isHorizontal() ? s->x() : s->y(); + int shadowY = isHorizontal() ? s->y() : -s->x(); + shadowRect.move(shadowX, shadowY); + clipRect.unite(shadowRect); + extraOffset = max(extraOffset, max(0, shadowY) + s->blur()); + } + context->save(); + context->clip(clipRect); + extraOffset += baseline + 2; + localOrigin.move(0, extraOffset); + setClip = true; + } + + ColorSpace colorSpace = renderer()->style()->colorSpace(); + bool setShadow = false; + + do { + if (shadow) { + if (!shadow->next()) { + // The last set of lines paints normally inside the clip. + localOrigin.move(0, -extraOffset); + extraOffset = 0; + } + int shadowX = isHorizontal() ? shadow->x() : shadow->y(); + int shadowY = isHorizontal() ? shadow->y() : -shadow->x(); + context->setShadow(IntSize(shadowX, shadowY - extraOffset), shadow->blur(), shadow->color(), colorSpace); + setShadow = true; + shadow = shadow->next(); + } + + if (deco & UNDERLINE) { + context->setStrokeColor(underline, colorSpace); + context->setStrokeStyle(SolidStroke); + // Leave one pixel of white between the baseline and the underline. + context->drawLineForText(IntPoint(localOrigin.x(), localOrigin.y() + baseline + 1), width, isPrinting); + } + if (deco & OVERLINE) { + context->setStrokeColor(overline, colorSpace); + context->setStrokeStyle(SolidStroke); + context->drawLineForText(localOrigin, width, isPrinting); + } + if (deco & LINE_THROUGH) { + context->setStrokeColor(linethrough, colorSpace); + context->setStrokeStyle(SolidStroke); + context->drawLineForText(IntPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting); + } + } while (shadow); + + if (setClip) + context->restore(); + else if (setShadow) + context->clearShadow(); +} + +static GraphicsContext::TextCheckingLineStyle textCheckingLineStyleForMarkerType(DocumentMarker::MarkerType markerType) +{ + switch (markerType) { + case DocumentMarker::Spelling: + return GraphicsContext::TextCheckingSpellingLineStyle; + case DocumentMarker::Grammar: + return GraphicsContext::TextCheckingGrammarLineStyle; + case DocumentMarker::CorrectionIndicator: + return GraphicsContext::TextCheckingReplacementLineStyle; + default: + ASSERT_NOT_REACHED(); + return GraphicsContext::TextCheckingSpellingLineStyle; + } +} + +void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, const IntPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font, bool grammar) +{ + // Never print spelling/grammar markers (5327887) + if (textRenderer()->document()->printing()) + return; + + if (m_truncation == cFullTruncation) + return; + + int start = 0; // start of line to draw, relative to tx + int width = m_logicalWidth; // how much line to draw + + // Determine whether we need to measure text + bool markerSpansWholeBox = true; + if (m_start <= (int)marker.startOffset) + markerSpansWholeBox = false; + if ((end() + 1) != marker.endOffset) // end points at the last char, not past it + markerSpansWholeBox = false; + if (m_truncation != cNoTruncation) + markerSpansWholeBox = false; + + if (!markerSpansWholeBox || grammar) { + int startPosition = max(marker.startOffset - m_start, 0); + int endPosition = min(marker.endOffset - m_start, m_len); + + if (m_truncation != cNoTruncation) + endPosition = min(endPosition, m_truncation); + + // Calculate start & width + int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); + int selHeight = selectionHeight(); + IntPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + if (m_disableRoundingHacks) + run.disableRoundingHacks(); +#endif + + IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition)); + start = markerRect.x() - startPoint.x(); + width = markerRect.width(); + + // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to + // display a toolTip. We don't do this for misspelling markers. + if (grammar) { + markerRect.move(-boxOrigin.x(), -boxOrigin.y()); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); + renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); + } + } + + // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to + // make sure to fit within those bounds. This means the top pixel(s) of the underline will overlap the + // bottom pixel(s) of the glyphs in smaller font sizes. The alternatives are to increase the line spacing (bad!!) + // or decrease the underline thickness. The overlap is actually the most useful, and matches what AppKit does. + // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so + // we pin to two pixels under the baseline. + int lineThickness = cMisspellingLineThickness; + int baseline = renderer()->style(m_firstLine)->font().ascent(); + int descent = logicalHeight() - baseline; + int underlineOffset; + if (descent <= (2 + lineThickness)) { + // Place the underline at the very bottom of the text in small/medium fonts. + underlineOffset = logicalHeight() - lineThickness; + } else { + // In larger fonts, though, place the underline up near the baseline to prevent a big gap. + underlineOffset = baseline + 2; + } + pt->drawLineForTextChecking(IntPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, textCheckingLineStyleForMarkerType(marker.type)); +} + +void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const IntPoint& boxOrigin, const DocumentMarker& marker, RenderStyle* style, const Font& font) +{ + // Use same y positioning and height as for selection, so that when the selection and this highlight are on + // the same word there are no pieces sticking out. + int deltaY = renderer()->style()->isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop(); + int selHeight = selectionHeight(); + + int sPos = max(marker.startOffset - m_start, (unsigned)0); + int ePos = min(marker.endOffset - m_start, (unsigned)m_len); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + if (m_disableRoundingHacks) + run.disableRoundingHacks(); +#endif + + // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates. + IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(m_x, selectionTop()), selHeight, sPos, ePos)); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); + renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); + + // Optionally highlight the text + if (renderer()->frame()->editor()->markedTextMatchesAreHighlighted()) { + Color color = marker.activeMatch ? + renderer()->theme()->platformActiveTextSearchHighlightColor() : + renderer()->theme()->platformInactiveTextSearchHighlightColor(); + pt->save(); + updateGraphicsContext(pt, color, color, 0, style->colorSpace()); // Don't draw text at all! + pt->clip(IntRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight)); + pt->drawHighlightForText(font, run, IntPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style->colorSpace(), sPos, ePos); + pt->restore(); + } +} + +void InlineTextBox::computeRectForReplacementMarker(const DocumentMarker& marker, RenderStyle* style, const Font& font) +{ + // Replacement markers are not actually drawn, but their rects need to be computed for hit testing. + int y = selectionTop(); + int h = selectionHeight(); + + int sPos = max(marker.startOffset - m_start, (unsigned)0); + int ePos = min(marker.endOffset - m_start, (unsigned)m_len); + TextRun run(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + if (m_disableRoundingHacks) + run.disableRoundingHacks(); +#endif + IntPoint startPoint = IntPoint(m_x, y); + + // Compute and store the rect associated with this marker. + IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos)); + markerRect = renderer()->localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox(); + renderer()->document()->markers()->setRenderedRectForMarker(renderer()->node(), marker, markerRect); +} + +void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const IntPoint& boxOrigin, RenderStyle* style, const Font& font, bool background) +{ + if (!renderer()->node()) + return; + + Vector markers = renderer()->document()->markers()->markersForNode(renderer()->node()); + Vector::iterator markerIt = markers.begin(); + + // Give any document markers that touch this run a chance to draw before the text has been drawn. + // Note end() points at the last char, not one past it like endOffset and ranges do. + for ( ; markerIt != markers.end(); markerIt++) { + const DocumentMarker& marker = *markerIt; + + // Paint either the background markers or the foreground markers, but not both + switch (marker.type) { + case DocumentMarker::Grammar: + case DocumentMarker::Spelling: + case DocumentMarker::Replacement: + case DocumentMarker::CorrectionIndicator: + case DocumentMarker::RejectedCorrection: + if (background) + continue; + break; + case DocumentMarker::TextMatch: + if (!background) + continue; + break; + + default: + ASSERT_NOT_REACHED(); + } + + if (marker.endOffset <= start()) + // marker is completely before this run. This might be a marker that sits before the + // first run we draw, or markers that were within runs we skipped due to truncation. + continue; + + if (marker.startOffset > end()) + // marker is completely after this run, bail. A later run will paint it. + break; + + // marker intersects this run. Paint it. + switch (marker.type) { + case DocumentMarker::Spelling: + paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false); + break; + case DocumentMarker::Grammar: + paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, true); + break; + case DocumentMarker::TextMatch: + paintTextMatchMarker(pt, boxOrigin, marker, style, font); + break; + case DocumentMarker::CorrectionIndicator: + computeRectForReplacementMarker(marker, style, font); + paintSpellingOrGrammarMarker(pt, boxOrigin, marker, style, font, false); + break; + case DocumentMarker::Replacement: + case DocumentMarker::RejectedCorrection: + break; + default: + ASSERT_NOT_REACHED(); + } + + } +} + + +void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const IntPoint& boxOrigin, const CompositionUnderline& underline) +{ + if (m_truncation == cFullTruncation) + return; + + int start = 0; // start of line to draw, relative to tx + int width = m_logicalWidth; // how much line to draw + bool useWholeWidth = true; + unsigned paintStart = m_start; + unsigned paintEnd = end() + 1; // end points at the last char, not past it + if (paintStart <= underline.startOffset) { + paintStart = underline.startOffset; + useWholeWidth = false; + start = toRenderText(renderer())->width(m_start, paintStart - m_start, textPos(), m_firstLine); + } + if (paintEnd != underline.endOffset) { // end points at the last char, not past it + paintEnd = min(paintEnd, (unsigned)underline.endOffset); + useWholeWidth = false; + } + if (m_truncation != cNoTruncation) { + paintEnd = min(paintEnd, (unsigned)m_start + m_truncation); + useWholeWidth = false; + } + if (!useWholeWidth) { + width = toRenderText(renderer())->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine); + } + + // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline. + // All other marked text underlines are 1px thick. + // If there's not enough space the underline will touch or overlap characters. + int lineThickness = 1; + int baseline = renderer()->style(m_firstLine)->font().ascent(); + if (underline.thick && logicalHeight() - baseline >= 2) + lineThickness = 2; + + // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those. + // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too. + start += 1; + width -= 2; + + ctx->setStrokeColor(underline.color, renderer()->style()->colorSpace()); + ctx->setStrokeThickness(lineThickness); + ctx->drawLineForText(IntPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, textRenderer()->document()->printing()); +} + +int InlineTextBox::caretMinOffset() const +{ + return m_start; +} + +int InlineTextBox::caretMaxOffset() const +{ + return m_start + m_len; +} + +unsigned InlineTextBox::caretMaxRenderedOffset() const +{ + return m_start + m_len; +} + +int InlineTextBox::textPos() const +{ + // When computing the width of a text run, RenderBlock::computeInlineDirectionPositionsForLine() doesn't include the actual offset + // from the containing block edge in its measurement. textPos() should be consistent so the text are rendered in the same width. + if (logicalLeft() == 0) + return 0; + return logicalLeft() - root()->logicalLeft(); +} + +int InlineTextBox::offsetForPosition(int lineOffset, bool includePartialGlyphs) const +{ + if (isLineBreak()) + return 0; + + int leftOffset = isLeftToRightDirection() ? 0 : m_len; + int rightOffset = isLeftToRightDirection() ? m_len : 0; + bool blockIsInOppositeDirection = renderer()->containingBlock()->style()->isLeftToRightDirection() != isLeftToRightDirection(); + if (blockIsInOppositeDirection) + swap(leftOffset, rightOffset); + + if (lineOffset - logicalLeft() > logicalWidth()) + return rightOffset; + if (lineOffset - logicalLeft() < 0) + return leftOffset; + + RenderText* text = toRenderText(renderer()); + RenderStyle* style = text->style(m_firstLine); + const Font* f = &style->font(); +<<<<<<< HEAD:WebCore/rendering/InlineTextBox.cpp +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + TextRun textRun = TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()); + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); + return f->offsetForPosition(textRun, lineOffset - logicalLeft(), includePartialGlyphs); +#else + return f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), + lineOffset - logicalLeft(), includePartialGlyphs); +#endif +======= + int offset = f->offsetForPosition(TextRun(textRenderer()->text()->characters() + m_start, m_len, + textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride || style->visuallyOrdered()), + lineOffset - logicalLeft(), includePartialGlyphs); + if (blockIsInOppositeDirection && (!offset || offset == m_len)) + return !offset ? m_len : 0; + return offset; +>>>>>>> webkit.org at r75315:Source/WebCore/rendering/InlineTextBox.cpp +} + +int InlineTextBox::positionForOffset(int offset) const +{ + ASSERT(offset >= m_start); + ASSERT(offset <= m_start + m_len); + + if (isLineBreak()) + return logicalLeft(); + + RenderText* text = toRenderText(renderer()); + const Font& f = text->style(m_firstLine)->font(); + int from = !isLeftToRightDirection() ? offset - m_start : 0; + int to = !isLeftToRightDirection() ? m_len : offset - m_start; + // FIXME: Do we need to add rightBearing here? +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + TextRun textRun = TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride); + if (m_disableRoundingHacks) + textRun.disableRoundingHacks(); + return enclosingIntRect(f.selectionRectForText(textRun, IntPoint(logicalLeft(), 0), 0, from, to)).right(); +#else + return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textRenderer()->allowTabs(), textPos(), m_toAdd, !isLeftToRightDirection(), m_dirOverride), + IntPoint(logicalLeft(), 0), 0, from, to)).right(); +#endif +} + +bool InlineTextBox::containsCaretOffset(int offset) const +{ + // Offsets before the box are never "in". + if (offset < m_start) + return false; + + int pastEnd = m_start + m_len; + + // Offsets inside the box (not at either edge) are always "in". + if (offset < pastEnd) + return true; + + // Offsets outside the box are always "out". + if (offset > pastEnd) + return false; + + // Offsets at the end are "out" for line breaks (they are on the next line). + if (isLineBreak()) + return false; + + // Offsets at the end are "in" for normal boxes (but the caller has to check affinity). + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/InlineTextBox.h b/Source/WebCore/rendering/InlineTextBox.h new file mode 100644 index 0000000..ee75f06 --- /dev/null +++ b/Source/WebCore/rendering/InlineTextBox.h @@ -0,0 +1,178 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef InlineTextBox_h +#define InlineTextBox_h + +#include "InlineBox.h" +#include "RenderText.h" // so textRenderer() can be inline + +namespace WebCore { + +struct CompositionUnderline; +struct DocumentMarker; + +const unsigned short cNoTruncation = USHRT_MAX; +const unsigned short cFullTruncation = USHRT_MAX - 1; + +// Helper functions shared by InlineTextBox / SVGRootInlineBox +void updateGraphicsContext(GraphicsContext*, const Color& fillColor, const Color& strokeColor, float strokeThickness, ColorSpace); +Color correctedTextColor(Color textColor, Color backgroundColor); + +class InlineTextBox : public InlineBox { +public: +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + InlineTextBox(RenderObject* obj, bool disableRoundingHacks = false) +#else + InlineTextBox(RenderObject* obj) +#endif + : InlineBox(obj) + , m_prevTextBox(0) + , m_nextTextBox(0) + , m_start(0) + , m_len(0) + , m_truncation(cNoTruncation) +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + , m_disableRoundingHacks(disableRoundingHacks) +#endif + { + } + + InlineTextBox* prevTextBox() const { return m_prevTextBox; } + InlineTextBox* nextTextBox() const { return m_nextTextBox; } + void setNextTextBox(InlineTextBox* n) { m_nextTextBox = n; } + void setPreviousTextBox(InlineTextBox* p) { m_prevTextBox = p; } + + unsigned start() const { return m_start; } + unsigned end() const { return m_len ? m_start + m_len - 1 : m_start; } + unsigned len() const { return m_len; } + + void setStart(unsigned start) { m_start = start; } + void setLen(unsigned len) { m_len = len; } + + void offsetRun(int d) { m_start += d; } + + unsigned short truncation() { return m_truncation; } + + bool hasHyphen() const { return m_hasEllipsisBoxOrHyphen; } + void setHasHyphen(bool hasHyphen) { m_hasEllipsisBoxOrHyphen = hasHyphen; } + static inline bool compareByStart(const InlineTextBox* first, const InlineTextBox* second) { return first->start() < second->start(); } + + virtual int baselinePosition(FontBaseline) const; + virtual int lineHeight() const; + + bool getEmphasisMarkPosition(RenderStyle*, TextEmphasisPosition&) const; + +private: + int selectionTop(); + int selectionBottom(); + int selectionHeight(); + +public: + virtual IntRect calculateBoundaries() const { return IntRect(x(), y(), logicalWidth(), logicalHeight()); } + + virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); + bool isSelected(int startPos, int endPos) const; + void selectionStartEnd(int& sPos, int& ePos); + +protected: + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + +public: + RenderText* textRenderer() const; + +private: + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + +public: + virtual RenderObject::SelectionState selectionState(); + +private: + virtual void clearTruncation() { m_truncation = cNoTruncation; } + virtual int placeEllipsisBox(bool flowIsLTR, int visibleLeftEdge, int visibleRightEdge, int ellipsisWidth, bool& foundBox); + +public: + virtual bool isLineBreak() const; + + void setSpaceAdd(int add) { m_logicalWidth -= m_toAdd; m_toAdd = add; m_logicalWidth += m_toAdd; } + +private: + virtual bool isInlineTextBox() const { return true; } + +public: + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + +private: + virtual unsigned caretMaxRenderedOffset() const; + + int textPos() const; // returns the x position relative to the left start of the text line. + +public: + virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const; + virtual int positionForOffset(int offset) const; + + bool containsCaretOffset(int offset) const; // false for offset after line break + + // Needs to be public, so the static paintTextWithShadows() function can use it. + static FloatSize applyShadowToGraphicsContext(GraphicsContext*, const ShadowData*, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal); + +private: + InlineTextBox* m_prevTextBox; // The previous box that also uses our RenderObject + InlineTextBox* m_nextTextBox; // The next box that also uses our RenderObject + + int m_start; + unsigned short m_len; + + unsigned short m_truncation; // Where to truncate when text overflow is applied. We use special constants to + // denote no truncation (the whole run paints) and full truncation (nothing paints at all). +#ifdef ANDROID_DISABLE_ROUNDING_HACKS + bool m_disableRoundingHacks; +#endif + +protected: + void paintCompositionBackground(GraphicsContext*, const IntPoint& boxOrigin, RenderStyle*, const Font&, int startPos, int endPos); + void paintDocumentMarkers(GraphicsContext*, const IntPoint& boxOrigin, RenderStyle*, const Font&, bool background); + void paintCompositionUnderline(GraphicsContext*, const IntPoint& boxOrigin, const CompositionUnderline&); +#if PLATFORM(MAC) + void paintCustomHighlight(int tx, int ty, const AtomicString& type); +#endif + +private: + void paintDecoration(GraphicsContext*, const IntPoint& boxOrigin, int decoration, const ShadowData*); + void paintSelection(GraphicsContext*, const IntPoint& boxOrigin, RenderStyle*, const Font&); + void paintSpellingOrGrammarMarker(GraphicsContext*, const IntPoint& boxOrigin, const DocumentMarker&, RenderStyle*, const Font&, bool grammar); + void paintTextMatchMarker(GraphicsContext*, const IntPoint& boxOrigin, const DocumentMarker&, RenderStyle*, const Font&); + void computeRectForReplacementMarker(const DocumentMarker&, RenderStyle*, const Font&); +}; + +inline RenderText* InlineTextBox::textRenderer() const +{ + return toRenderText(renderer()); +} + +} // namespace WebCore + +#endif // InlineTextBox_h diff --git a/Source/WebCore/rendering/LayoutState.cpp b/Source/WebCore/rendering/LayoutState.cpp new file mode 100644 index 0000000..aeba416 --- /dev/null +++ b/Source/WebCore/rendering/LayoutState.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LayoutState.h" + +#include "ColumnInfo.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderView.h" + +namespace WebCore { + +LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& offset, int pageLogicalHeight, bool pageLogicalHeightChanged, ColumnInfo* columnInfo) + : m_columnInfo(columnInfo) + , m_next(prev) +#ifndef NDEBUG + , m_renderer(renderer) +#endif +{ + ASSERT(m_next); + + bool fixed = renderer->isPositioned() && renderer->style()->position() == FixedPosition; + if (fixed) { + // FIXME: This doesn't work correctly with transforms. + FloatPoint fixedOffset = renderer->view()->localToAbsolute(FloatPoint(), true); + m_paintOffset = IntSize(fixedOffset.x(), fixedOffset.y()) + offset; + } else + m_paintOffset = prev->m_paintOffset + offset; + + if (renderer->isPositioned() && !fixed) { + if (RenderObject* container = renderer->container()) { + if (container->isRelPositioned() && container->isRenderInline()) + m_paintOffset += toRenderInline(container)->relativePositionedInlineOffset(renderer); + } + } + + m_layoutOffset = m_paintOffset; + + if (renderer->isRelPositioned() && renderer->hasLayer()) + m_paintOffset += renderer->layer()->relativePositionOffset(); + + m_clipped = !fixed && prev->m_clipped; + if (m_clipped) + m_clipRect = prev->m_clipRect; + + if (renderer->hasOverflowClip()) { + RenderLayer* layer = renderer->layer(); + IntRect clipRect(toPoint(m_paintOffset) + renderer->view()->layoutDelta(), layer->size()); + if (m_clipped) + m_clipRect.intersect(clipRect); + else { + m_clipRect = clipRect; + m_clipped = true; + } + + m_paintOffset -= layer->scrolledContentOffset(); + } + + // If we establish a new page height, then cache the offset to the top of the first page. + // We can compare this later on to figure out what part of the page we're actually on, + if (pageLogicalHeight || m_columnInfo) { + m_pageLogicalHeight = pageLogicalHeight; + m_pageOffset = IntSize(m_layoutOffset.width() + renderer->borderLeft() + renderer->paddingLeft(), + m_layoutOffset.height() + renderer->borderTop() + renderer->paddingTop()); + m_pageLogicalHeightChanged = pageLogicalHeightChanged; + } else { + // If we don't establish a new page height, then propagate the old page height and offset down. + m_pageLogicalHeight = m_next->m_pageLogicalHeight; + m_pageLogicalHeightChanged = m_next->m_pageLogicalHeightChanged; + m_pageOffset = m_next->m_pageOffset; + + // Disable pagination for objects we don't support. For now this includes overflow:scroll/auto and inline blocks. + if (renderer->isReplaced() || renderer->scrollsOverflow()) + m_pageLogicalHeight = 0; + } + + if (!m_columnInfo) + m_columnInfo = m_next->m_columnInfo; + + m_layoutDelta = m_next->m_layoutDelta; + + // FIXME: Apply control clip if present. +} + +LayoutState::LayoutState(RenderObject* root) + : m_clipped(false) + , m_pageLogicalHeight(0) + , m_pageLogicalHeightChanged(false) + , m_columnInfo(0) + , m_next(0) +#ifndef NDEBUG + , m_renderer(root) +#endif +{ + RenderObject* container = root->container(); + FloatPoint absContentPoint = container->localToAbsolute(FloatPoint(), false, true); + m_paintOffset = IntSize(absContentPoint.x(), absContentPoint.y()); + + if (container->hasOverflowClip()) { + RenderLayer* layer = toRenderBoxModelObject(container)->layer(); + m_clipped = true; + m_clipRect = IntRect(toPoint(m_paintOffset), layer->size()); + m_paintOffset -= layer->scrolledContentOffset(); + } +} + +#ifndef NDEBUG +static bool inLayoutStateDestroy; +#endif + +void LayoutState::destroy(RenderArena* renderArena) +{ +#ifndef NDEBUG + inLayoutStateDestroy = true; +#endif + delete this; +#ifndef NDEBUG + inLayoutStateDestroy = false; +#endif + renderArena->free(*(size_t*)this, this); +} + +void* LayoutState::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void LayoutState::operator delete(void* ptr, size_t sz) +{ + ASSERT(inLayoutStateDestroy); + *(size_t*)ptr = sz; +} + +void LayoutState::clearPaginationInformation() +{ + m_pageLogicalHeight = m_next->m_pageLogicalHeight; + m_pageOffset = m_next->m_pageOffset; + m_columnInfo = m_next->m_columnInfo; +} + +int LayoutState::pageLogicalOffset(int childLogicalOffset) const +{ + return m_layoutOffset.height() + childLogicalOffset - m_pageOffset.height(); +} + +void LayoutState::addForcedColumnBreak(int childY) +{ + if (!m_columnInfo || m_columnInfo->columnHeight()) + return; + m_columnInfo->addForcedBreak(pageLogicalOffset(childY)); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/LayoutState.h b/Source/WebCore/rendering/LayoutState.h new file mode 100644 index 0000000..0d06cb1 --- /dev/null +++ b/Source/WebCore/rendering/LayoutState.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LayoutState_h +#define LayoutState_h + +#include "IntRect.h" +#include "IntSize.h" +#include + +namespace WebCore { + +class ColumnInfo; +class RenderArena; +class RenderBox; +class RenderObject; + +class LayoutState : public Noncopyable { +public: + LayoutState() + : m_clipped(false) + , m_pageLogicalHeight(0) + , m_pageLogicalHeightChanged(false) + , m_columnInfo(0) + , m_next(0) +#ifndef NDEBUG + , m_renderer(0) +#endif + { + } + + LayoutState(LayoutState*, RenderBox*, const IntSize& offset, int pageHeight, bool pageHeightChanged, ColumnInfo*); + LayoutState(RenderObject*); + + void destroy(RenderArena*); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + void clearPaginationInformation(); + bool isPaginatingColumns() const { return m_columnInfo; } + bool isPaginated() const { return m_pageLogicalHeight || m_columnInfo; } + + // The page logical offset is the object's offset from the top of the page in the page progression + // direction (so an x-offset in vertical text and a y-offset for horizontal text). + int pageLogicalOffset(int childLogicalOffset) const; + + void addForcedColumnBreak(int childY); + + bool pageLogicalHeight() const { return m_pageLogicalHeight; } + bool pageLogicalHeightChanged() const { return m_pageLogicalHeightChanged; } + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: + bool m_clipped; + IntRect m_clipRect; + IntSize m_paintOffset; // x/y offset from container. Includes relative positioning and scroll offsets. + IntSize m_layoutOffset; // x/y offset from container. Does not include relative positioning or scroll offsets. + IntSize m_layoutDelta; // Transient offset from the final position of the object + // used to ensure that repaints happen in the correct place. + // This is a total delta accumulated from the root. + + int m_pageLogicalHeight; // The current page height for the pagination model that encloses us. + bool m_pageLogicalHeightChanged; // If our page height has changed, this will force all blocks to relayout. + IntSize m_pageOffset; // The offset of the start of the first page in the nearest enclosing pagination model. + ColumnInfo* m_columnInfo; // If the enclosing pagination model is a column model, then this will store column information for easy retrieval/manipulation. + + LayoutState* m_next; +#ifndef NDEBUG + RenderObject* m_renderer; +#endif +}; + +} // namespace WebCore + +#endif // LayoutState_h diff --git a/Source/WebCore/rendering/MediaControlElements.cpp b/Source/WebCore/rendering/MediaControlElements.cpp new file mode 100644 index 0000000..accbf4f --- /dev/null +++ b/Source/WebCore/rendering/MediaControlElements.cpp @@ -0,0 +1,876 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "MediaControlElements.h" + +#include "EventNames.h" +#include "FloatConversion.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderMedia.h" +#include "RenderSlider.h" +#include "RenderTheme.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLMediaElement* toParentMediaElement(RenderObject* o) +{ + Node* node = o->node(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag))) + return 0; + + return static_cast(mediaNode); +} + +// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in. +static const float cSeekRepeatDelay = 0.1f; +static const float cStepTime = 0.07f; +static const float cSeekTime = 0.2f; + +inline MediaControlShadowRootElement::MediaControlShadowRootElement(HTMLMediaElement* mediaElement) + : HTMLDivElement(divTag, mediaElement->document()) +{ + setShadowHost(mediaElement); +} + +PassRefPtr MediaControlShadowRootElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr element = adoptRef(new MediaControlShadowRootElement(mediaElement)); + + RefPtr rootStyle = RenderStyle::create(); + rootStyle->inheritFrom(mediaElement->renderer()->style()); + rootStyle->setDisplay(BLOCK); + rootStyle->setPosition(RelativePosition); + + RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(element.get()); + renderer->setStyle(rootStyle.release()); + + element->setRenderer(renderer); + element->setAttached(); + element->setInDocument(); + + return element.release(); +} + +void MediaControlShadowRootElement::updateStyle() +{ + if (renderer()) { + RenderStyle* timelineContainerStyle = shadowHost()->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_TIMELINE_CONTAINER); + renderer()->setStyle(timelineContainerStyle); + } +} + +void MediaControlShadowRootElement::detach() +{ + HTMLDivElement::detach(); + // FIXME: Remove once shadow DOM uses Element::setShadowRoot(). + setShadowHost(0); +} + +// ---------------------------- + +MediaControlElement::MediaControlElement(HTMLMediaElement* mediaElement, PseudoId pseudo) + : HTMLDivElement(divTag, mediaElement->document()) + , m_mediaElement(mediaElement) + , m_pseudoStyleId(pseudo) +{ + setInDocument(); + switch (pseudo) { + case MEDIA_CONTROLS_CURRENT_TIME_DISPLAY: + m_displayType = MediaCurrentTimeDisplay; + break; + case MEDIA_CONTROLS_TIME_REMAINING_DISPLAY: + m_displayType = MediaTimeRemainingDisplay; + break; + case MEDIA_CONTROLS_TIMELINE_CONTAINER: + m_displayType = MediaTimelineContainer; + break; + case MEDIA_CONTROLS_STATUS_DISPLAY: + m_displayType = MediaStatusDisplay; + break; + case MEDIA_CONTROLS_PANEL: + m_displayType = MediaControlsPanel; + break; + case MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER: + m_displayType = MediaVolumeSliderContainer; + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +PassRefPtr MediaControlElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) +{ + return adoptRef(new MediaControlElement(mediaElement, pseudoStyleId)); +} + +void MediaControlElement::attachToParent(Element* parent) +{ + // FIXME: This code seems very wrong. Why are we magically adding |this| to the DOM here? + // We shouldn't be calling parser API methods outside of the parser! + parent->parserAddChild(this); +} + +void MediaControlElement::update() +{ + if (renderer()) + renderer()->updateFromElement(); + updateStyle(); +} + +PassRefPtr MediaControlElement::styleForElement() +{ + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(m_pseudoStyleId); + if (!style) + return 0; + + // text-decoration can't be overrided from CSS. So we do it here. + // See https://bugs.webkit.org/show_bug.cgi?id=27015 + style->setTextDecoration(TDNONE); + style->setTextDecorationsInEffect(TDNONE); + + return style; +} + +bool MediaControlElement::rendererIsNeeded(RenderStyle* style) +{ + ASSERT(document()->page()); + + return HTMLDivElement::rendererIsNeeded(style) && parentNode() && parentNode()->renderer() + && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), m_mediaElement)); +} + +void MediaControlElement::attach() +{ + RefPtr style = styleForElement(); + if (!style) + return; + bool needsRenderer = rendererIsNeeded(style.get()); + if (!needsRenderer) + return; + RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style.get()); + if (!renderer) + return; + renderer->setStyle(style.get()); + setRenderer(renderer); + if (parentNode() && parentNode()->renderer()) { + // Find next sibling with a renderer to determine where to insert. + Node* sibling = nextSibling(); + while (sibling && !sibling->renderer()) + sibling = sibling->nextSibling(); + parentNode()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0); + } + ContainerNode::attach(); +} + +void MediaControlElement::updateStyle() +{ + if (!m_mediaElement || !m_mediaElement->renderer()) + return; + + RefPtr style = styleForElement(); + if (!style) + return; + + bool needsRenderer = rendererIsNeeded(style.get()) && parentNode() && parentNode()->renderer(); + if (renderer() && !needsRenderer) + detach(); + else if (!renderer() && needsRenderer) + attach(); + else if (renderer()) { + renderer()->setStyle(style.get()); + + // Make sure that if there is any innerText renderer, it is updated as well. + if (firstChild() && firstChild()->renderer()) + firstChild()->renderer()->setStyle(style.get()); + } +} + +// ---------------------------- + +inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(HTMLMediaElement* mediaElement) + : MediaControlElement(mediaElement, MEDIA_CONTROLS_TIMELINE_CONTAINER) +{ +} + +PassRefPtr MediaControlTimelineContainerElement::create(HTMLMediaElement* mediaElement) +{ + return adoptRef(new MediaControlTimelineContainerElement(mediaElement)); +} + +bool MediaControlTimelineContainerElement::rendererIsNeeded(RenderStyle* style) +{ + if (!MediaControlElement::rendererIsNeeded(style)) + return false; + + // This is for MediaControllerThemeClassic: + // If there is no style for MediaControlStatusDisplayElement style, don't hide + // the timeline. + if (!mediaElement()->renderer()->getCachedPseudoStyle(MEDIA_CONTROLS_STATUS_DISPLAY)) + return true; + + float duration = mediaElement()->duration(); + return !isnan(duration) && !isinf(duration); +} + +// ---------------------------- + +inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(HTMLMediaElement* mediaElement) + : MediaControlElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER) + , m_isVisible(false) + , m_x(0) + , m_y(0) +{ +} + +PassRefPtr MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement) +{ + return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement)); +} + +PassRefPtr MediaControlVolumeSliderContainerElement::styleForElement() +{ + RefPtr style = MediaControlElement::styleForElement(); + style->setPosition(AbsolutePosition); + style->setLeft(Length(m_x, Fixed)); + style->setTop(Length(m_y, Fixed)); + style->setDisplay(m_isVisible ? BLOCK : NONE); + return style; +} + +void MediaControlVolumeSliderContainerElement::setVisible(bool visible) +{ + if (visible == m_isVisible) + return; + m_isVisible = visible; +} + +void MediaControlVolumeSliderContainerElement::setPosition(int x, int y) +{ + if (x == m_x && y == m_y) + return; + m_x = x; + m_y = y; +} + +bool MediaControlVolumeSliderContainerElement::hitTest(const IntPoint& absPoint) +{ + if (renderer() && renderer()->style()->hasAppearance()) + return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint); + + return false; +} + +// ---------------------------- + +inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(HTMLMediaElement* mediaElement) + : MediaControlElement(mediaElement, MEDIA_CONTROLS_STATUS_DISPLAY) + , m_stateBeingDisplayed(Nothing) +{ +} + +PassRefPtr MediaControlStatusDisplayElement::create(HTMLMediaElement* mediaElement) +{ + return adoptRef(new MediaControlStatusDisplayElement(mediaElement)); +} + +void MediaControlStatusDisplayElement::update() +{ + MediaControlElement::update(); + + // Get the new state that we'll have to display. + StateBeingDisplayed newStateToDisplay = Nothing; + + if (mediaElement()->readyState() != HTMLMediaElement::HAVE_ENOUGH_DATA && !mediaElement()->currentSrc().isEmpty()) + newStateToDisplay = Loading; + else if (mediaElement()->movieLoadType() == MediaPlayer::LiveStream) + newStateToDisplay = LiveBroadcast; + + // Propagate only if needed. + if (newStateToDisplay == m_stateBeingDisplayed) + return; + m_stateBeingDisplayed = newStateToDisplay; + + ExceptionCode e; + switch (m_stateBeingDisplayed) { + case Nothing: + setInnerText("", e); + break; + case Loading: + setInnerText(mediaElementLoadingStateText(), e); + break; + case LiveBroadcast: + setInnerText(mediaElementLiveBroadcastStateText(), e); + break; + } +} + +bool MediaControlStatusDisplayElement::rendererIsNeeded(RenderStyle* style) +{ + if (!MediaControlElement::rendererIsNeeded(style)) + return false; + float duration = mediaElement()->duration(); + return (isnan(duration) || isinf(duration)); +} + +// ---------------------------- + +MediaControlInputElement::MediaControlInputElement(HTMLMediaElement* mediaElement, PseudoId pseudo) + : HTMLInputElement(inputTag, mediaElement->document()) + , m_mediaElement(mediaElement) + , m_pseudoStyleId(pseudo) +{ + setInDocument(); + + switch (pseudo) { + case MEDIA_CONTROLS_MUTE_BUTTON: + m_displayType = MediaMuteButton; + break; + case MEDIA_CONTROLS_PLAY_BUTTON: + m_displayType = MediaPlayButton; + break; + case MEDIA_CONTROLS_SEEK_FORWARD_BUTTON: + m_displayType = MediaSeekForwardButton; + break; + case MEDIA_CONTROLS_SEEK_BACK_BUTTON: + m_displayType = MediaSeekBackButton; + break; + case MEDIA_CONTROLS_FULLSCREEN_BUTTON: + m_displayType = MediaFullscreenButton; + break; + case MEDIA_CONTROLS_TIMELINE: + m_displayType = MediaSlider; + break; + case MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON: + m_displayType = MediaReturnToRealtimeButton; + break; + case MEDIA_CONTROLS_REWIND_BUTTON: + m_displayType = MediaRewindButton; + break; + case MEDIA_CONTROLS_VOLUME_SLIDER: + m_displayType = MediaVolumeSlider; + break; + case MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON: + m_displayType = MediaVolumeSliderMuteButton; + break; + case MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON: + m_displayType = MediaShowClosedCaptionsButton; + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void MediaControlInputElement::attachToParent(Element* parent) +{ + // FIXME: This code seems very wrong. Why are we magically adding |this| to the DOM here? + // We shouldn't be calling parser API methods outside of the parser! + parent->parserAddChild(this); +} + +void MediaControlInputElement::update() +{ + updateDisplayType(); + if (renderer()) + renderer()->updateFromElement(); + updateStyle(); +} + +PassRefPtr MediaControlInputElement::styleForElement() +{ + return mediaElement()->renderer()->getCachedPseudoStyle(m_pseudoStyleId); +} + +bool MediaControlInputElement::rendererIsNeeded(RenderStyle* style) +{ + ASSERT(document()->page()); + + return HTMLInputElement::rendererIsNeeded(style) && parentNode() && parentNode()->renderer() + && (!style->hasAppearance() || document()->page()->theme()->shouldRenderMediaControlPart(style->appearance(), mediaElement())); +} + +void MediaControlInputElement::attach() +{ + RefPtr style = styleForElement(); + if (!style) + return; + + bool needsRenderer = rendererIsNeeded(style.get()); + if (!needsRenderer) + return; + RenderObject* renderer = createRenderer(mediaElement()->renderer()->renderArena(), style.get()); + if (!renderer) + return; + renderer->setStyle(style.get()); + setRenderer(renderer); + if (parentNode() && parentNode()->renderer()) { + // Find next sibling with a renderer to determine where to insert. + Node* sibling = nextSibling(); + while (sibling && !sibling->renderer()) + sibling = sibling->nextSibling(); + parentNode()->renderer()->addChild(renderer, sibling ? sibling->renderer() : 0); + } + ContainerNode::attach(); +} + +void MediaControlInputElement::updateStyle() +{ + if (!mediaElement() || !mediaElement()->renderer()) + return; + + RefPtr style = styleForElement(); + if (!style) + return; + + bool needsRenderer = rendererIsNeeded(style.get()) && parentNode() && parentNode()->renderer(); + if (renderer() && !needsRenderer) + detach(); + else if (!renderer() && needsRenderer) + attach(); + else if (renderer()) + renderer()->setStyle(style.get()); +} + +bool MediaControlInputElement::hitTest(const IntPoint& absPoint) +{ + if (renderer() && renderer()->style()->hasAppearance()) + return renderer()->theme()->hitTestMediaControlPart(renderer(), absPoint); + + return false; +} + +void MediaControlInputElement::setDisplayType(MediaControlElementType displayType) +{ + if (displayType == m_displayType) + return; + + m_displayType = displayType; + if (RenderObject* object = renderer()) + object->repaint(); +} + +// ---------------------------- + +inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(HTMLMediaElement* mediaElement, ButtonLocation location) + : MediaControlInputElement(mediaElement, location == Controller ? MEDIA_CONTROLS_MUTE_BUTTON : MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON) +{ +} + +PassRefPtr MediaControlMuteButtonElement::create(HTMLMediaElement* mediaElement, ButtonLocation location) +{ + RefPtr button = adoptRef(new MediaControlMuteButtonElement(mediaElement, location)); + button->setType("button"); + return button.release(); +} + +void MediaControlMuteButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaElement()->setMuted(!mediaElement()->muted()); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlMuteButtonElement::updateDisplayType() +{ + setDisplayType(mediaElement()->muted() ? MediaUnMuteButton : MediaMuteButton); +} + +// ---------------------------- + +inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_PLAY_BUTTON) +{ +} + +PassRefPtr MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr button = adoptRef(new MediaControlPlayButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlPlayButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaElement()->togglePlayState(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlPlayButtonElement::updateDisplayType() +{ + setDisplayType(mediaElement()->canPlay() ? MediaPlayButton : MediaPauseButton); +} + +// ---------------------------- + +inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(HTMLMediaElement* mediaElement, PseudoId pseudoId) + : MediaControlInputElement(mediaElement, pseudoId) + , m_seeking(false) + , m_capturing(false) + , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired) +{ +} + +PassRefPtr MediaControlSeekButtonElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) +{ + RefPtr button = adoptRef(new MediaControlSeekButtonElement(mediaElement, pseudoStyleId)); + button->setType("button"); + return button.release(); +} + +inline bool MediaControlSeekButtonElement::isForwardButton() const +{ + return pseudoStyleId() == MEDIA_CONTROLS_SEEK_FORWARD_BUTTON; +} + +void MediaControlSeekButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().mousedownEvent) { + if (Frame* frame = document()->frame()) { + m_capturing = true; + frame->eventHandler()->setCapturingMouseEventsNode(this); + } + mediaElement()->pause(event->fromUserGesture()); + m_seekTimer.startRepeating(cSeekRepeatDelay); + event->setDefaultHandled(); + } else if (event->type() == eventNames().mouseupEvent) { + if (m_capturing) + if (Frame* frame = document()->frame()) { + m_capturing = false; + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + ExceptionCode ec; + if (m_seeking || m_seekTimer.isActive()) { + if (!m_seeking) { + float stepTime = isForwardButton() ? cStepTime : -cStepTime; + mediaElement()->setCurrentTime(mediaElement()->currentTime() + stepTime, ec); + } + m_seekTimer.stop(); + m_seeking = false; + event->setDefaultHandled(); + } + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlSeekButtonElement::seekTimerFired(Timer*) +{ + ExceptionCode ec; + m_seeking = true; + float seekTime = isForwardButton() ? cSeekTime : -cSeekTime; + mediaElement()->setCurrentTime(mediaElement()->currentTime() + seekTime, ec); +} + +void MediaControlSeekButtonElement::detach() +{ + if (m_capturing) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + MediaControlInputElement::detach(); +} + +// ---------------------------- + +inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(HTMLMediaElement* element) + : MediaControlInputElement(element, MEDIA_CONTROLS_REWIND_BUTTON) +{ +} + +PassRefPtr MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr button = adoptRef(new MediaControlRewindButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlRewindButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaElement()->rewind(30); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +// ---------------------------- + +inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON) +{ +} + +PassRefPtr MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr button = adoptRef(new MediaControlReturnToRealtimeButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaElement()->returnToRealtime(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + + +// ---------------------------- + +inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON) +{ +} + +PassRefPtr MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaElement()->setClosedCaptionsVisible(!mediaElement()->closedCaptionsVisible()); + setChecked(mediaElement()->closedCaptionsVisible()); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() +{ + setDisplayType(mediaElement()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); +} + +// ---------------------------- + +MediaControlTimelineElement::MediaControlTimelineElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_TIMELINE) +{ +} + +PassRefPtr MediaControlTimelineElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr timeline = adoptRef(new MediaControlTimelineElement(mediaElement)); + timeline->setType("range"); + return timeline.release(); +} + +void MediaControlTimelineElement::defaultEventHandler(Event* event) +{ + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast(event)->button()) + return; + + if (!attached()) + return; + + if (event->type() == eventNames().mousedownEvent) + mediaElement()->beginScrubbing(); + + MediaControlInputElement::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) + return; + + float time = narrowPrecisionToFloat(value().toDouble()); + if (time != mediaElement()->currentTime()) { + ExceptionCode ec; + mediaElement()->setCurrentTime(time, ec); + } + + RenderSlider* slider = toRenderSlider(renderer()); + if (slider && slider->inDragMode()) { + toRenderMedia(mediaElement()->renderer())->updateTimeDisplay(); +#if PLATFORM(ANDROID) + toRenderMedia(mediaElement()->renderer())->updateLastTouch(); +#endif + } + + if (event->type() == eventNames().mouseupEvent) + mediaElement()->endScrubbing(); +} + +void MediaControlTimelineElement::update(bool updateDuration) +{ + if (updateDuration) { + float duration = mediaElement()->duration(); + setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0)); + } + setValue(String::number(mediaElement()->currentTime())); + MediaControlInputElement::update(); +} + +// ---------------------------- + +inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_VOLUME_SLIDER) +{ +} + +PassRefPtr MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr slider = adoptRef(new MediaControlVolumeSliderElement(mediaElement)); + slider->setType("range"); + return slider.release(); +} + +void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) +{ + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast(event)->button()) + return; + + if (!attached()) + return; + + MediaControlInputElement::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) + return; + + float volume = narrowPrecisionToFloat(value().toDouble()); + if (volume != mediaElement()->volume()) { + ExceptionCode ec = 0; + mediaElement()->setVolume(volume, ec); + ASSERT(!ec); + } +} + +void MediaControlVolumeSliderElement::update() +{ + float volume = mediaElement()->volume(); + if (value().toFloat() != volume) + setValue(String::number(volume)); + MediaControlInputElement::update(); +} + +// ---------------------------- + +inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(HTMLMediaElement* mediaElement) + : MediaControlInputElement(mediaElement, MEDIA_CONTROLS_FULLSCREEN_BUTTON) +{ +} + +PassRefPtr MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement) +{ + RefPtr button = adoptRef(new MediaControlFullscreenButtonElement(mediaElement)); + button->setType("button"); + return button.release(); +} + +void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { +#if ENABLE(FULLSCREEN_API) + // Only use the new full screen API if the fullScreenEnabled setting has + // been explicitly enabled. Otherwise, use the old fullscreen API. This + // allows apps which embed a WebView to retain the existing full screen + // video implementation without requiring them to implement their own full + // screen behavior. + if (document()->settings() && document()->settings()->fullScreenEnabled()) { + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == mediaElement()) + document()->webkitCancelFullScreen(); + else + mediaElement()->webkitRequestFullScreen(0); + } else +#endif + mediaElement()->enterFullscreen(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +// ---------------------------- + +inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(HTMLMediaElement* mediaElement, PseudoId pseudo) + : MediaControlElement(mediaElement, pseudo) + , m_currentValue(0) + , m_isVisible(true) +{ +} + +PassRefPtr MediaControlTimeDisplayElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId) +{ + return adoptRef(new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId)); +} + +PassRefPtr MediaControlTimeDisplayElement::styleForElement() +{ + RefPtr style = MediaControlElement::styleForElement(); + if (!m_isVisible) { + style = RenderStyle::clone(style.get()); + style->setWidth(Length(0, Fixed)); + } + return style; +} + +void MediaControlTimeDisplayElement::setVisible(bool visible) +{ + if (visible == m_isVisible) + return; + m_isVisible = visible; + + // This function is used during the RenderMedia::layout() + // call, where we cannot change the renderer at this time. + if (!renderer() || !renderer()->style()) + return; + + RefPtr style = styleForElement(); + renderer()->setStyle(style.get()); +} + +void MediaControlTimeDisplayElement::setCurrentValue(float time) +{ + m_currentValue = time; +} + +} // namespace WebCore + +#endif // ENABLE(VIDEO) diff --git a/Source/WebCore/rendering/MediaControlElements.h b/Source/WebCore/rendering/MediaControlElements.h new file mode 100644 index 0000000..b2d063d --- /dev/null +++ b/Source/WebCore/rendering/MediaControlElements.h @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControlElements_h +#define MediaControlElements_h + +#if ENABLE(VIDEO) + +#include "HTMLDivElement.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "RenderBlock.h" + +// These are the shadow elements used in RenderMedia + +namespace WebCore { + +class Event; +class Frame; + +// Must match WebKitSystemInterface.h +enum MediaControlElementType { + MediaFullscreenButton = 0, + MediaMuteButton, + MediaPlayButton, + MediaSeekBackButton, + MediaSeekForwardButton, + MediaSlider, + MediaSliderThumb, + MediaRewindButton, + MediaReturnToRealtimeButton, + MediaShowClosedCaptionsButton, + MediaHideClosedCaptionsButton, + MediaUnMuteButton, + MediaPauseButton, + MediaTimelineContainer, + MediaCurrentTimeDisplay, + MediaTimeRemainingDisplay, + MediaStatusDisplay, + MediaControlsPanel, + MediaVolumeSliderContainer, + MediaVolumeSlider, + MediaVolumeSliderThumb, + MediaVolumeSliderMuteButton, +}; + +HTMLMediaElement* toParentMediaElement(RenderObject*); + +class MediaControlShadowRootElement : public HTMLDivElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + void updateStyle(); + virtual void detach(); + +private: + MediaControlShadowRootElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlElement : public HTMLDivElement { +public: + static PassRefPtr create(HTMLMediaElement*, PseudoId); + + virtual void attach(); + void attachToParent(Element*); + void update(); + void updateStyle(); + + MediaControlElementType displayType() const { return m_displayType; } + + HTMLMediaElement* mediaElement() const { return m_mediaElement; } + +protected: + MediaControlElement(HTMLMediaElement*, PseudoId); + + virtual bool rendererIsNeeded(RenderStyle*); + + virtual PassRefPtr styleForElement(); + +private: + virtual bool isMediaControlElement() const { return true; } + + HTMLMediaElement* m_mediaElement; + PseudoId m_pseudoStyleId; + MediaControlElementType m_displayType; // some elements can show multiple types (e.g. play/pause) +}; + +// ---------------------------- + +class MediaControlTimelineContainerElement : public MediaControlElement { +public: + static PassRefPtr create(HTMLMediaElement*); + +private: + MediaControlTimelineContainerElement(HTMLMediaElement*); + virtual bool rendererIsNeeded(RenderStyle*); +}; + +// ---------------------------- + +class MediaControlVolumeSliderContainerElement : public MediaControlElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual PassRefPtr styleForElement(); + void setVisible(bool); + bool isVisible() { return m_isVisible; } + void setPosition(int x, int y); + bool hitTest(const IntPoint& absPoint); + +private: + MediaControlVolumeSliderContainerElement(HTMLMediaElement*); + + bool m_isVisible; + int m_x, m_y; +}; + +// ---------------------------- + +class MediaControlStatusDisplayElement : public MediaControlElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + void update(); + +private: + MediaControlStatusDisplayElement(HTMLMediaElement*); + + virtual bool rendererIsNeeded(RenderStyle*); + + enum StateBeingDisplayed { Nothing, Loading, LiveBroadcast }; + StateBeingDisplayed m_stateBeingDisplayed; +}; + +// ---------------------------- + +class MediaControlInputElement : public HTMLInputElement { +public: + void attachToParent(Element*); + void update(); + void updateStyle(); + + bool hitTest(const IntPoint& absPoint); + + MediaControlElementType displayType() const { return m_displayType; } + + HTMLMediaElement* mediaElement() const { return m_mediaElement; } + +protected: + MediaControlInputElement(HTMLMediaElement*, PseudoId); + + void setDisplayType(MediaControlElementType); + + PseudoId pseudoStyleId() const { return m_pseudoStyleId; } + +private: + virtual void attach(); + virtual bool rendererIsNeeded(RenderStyle*); + + virtual PassRefPtr styleForElement(); + + virtual bool isMediaControlElement() const { return true; } + + virtual void updateDisplayType() { } + + HTMLMediaElement* m_mediaElement; + PseudoId m_pseudoStyleId; + MediaControlElementType m_displayType; +}; + +// ---------------------------- + +class MediaControlMuteButtonElement : public MediaControlInputElement { +public: + enum ButtonLocation { Controller, VolumeSlider }; + static PassRefPtr create(HTMLMediaElement*, ButtonLocation); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlMuteButtonElement(HTMLMediaElement*, ButtonLocation); + + virtual void updateDisplayType(); +}; + +// ---------------------------- + +class MediaControlPlayButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlPlayButtonElement(HTMLMediaElement*); + + virtual void updateDisplayType(); +}; + +// ---------------------------- + +class MediaControlSeekButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*, PseudoId); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlSeekButtonElement(HTMLMediaElement*, PseudoId); + + bool isForwardButton() const; + + virtual void detach(); + void seekTimerFired(Timer*); + + bool m_seeking; + bool m_capturing; + Timer m_seekTimer; +}; + +// ---------------------------- + +class MediaControlRewindButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlRewindButtonElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlReturnToRealtimeButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlReturnToRealtimeButtonElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlToggleClosedCaptionsButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement*); + + virtual void updateDisplayType(); +}; + +// ---------------------------- + +class MediaControlTimelineElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + void update(bool updateDuration = true); + +private: + MediaControlTimelineElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlVolumeSliderElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + void update(); + +private: + MediaControlVolumeSliderElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlFullscreenButtonElement : public MediaControlInputElement { +public: + static PassRefPtr create(HTMLMediaElement*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenButtonElement(HTMLMediaElement*); +}; + +// ---------------------------- + +class MediaControlTimeDisplayElement : public MediaControlElement { +public: + static PassRefPtr create(HTMLMediaElement*, PseudoId); + + void setVisible(bool); + + void setCurrentValue(float); + float currentValue() const { return m_currentValue; } + +private: + MediaControlTimeDisplayElement(HTMLMediaElement*, PseudoId); + + virtual PassRefPtr styleForElement(); + float m_currentValue; + bool m_isVisible; +}; + +// ---------------------------- + +class RenderMediaControlShadowRoot : public RenderBlock { +public: + RenderMediaControlShadowRoot(Element* e) : RenderBlock(e) { } + void setParent(RenderObject* p) { RenderObject::setParent(p); } +}; + +// ---------------------------- + +} //namespace WebCore + +#endif // ENABLE(VIDEO) + +#endif // MediaControlElements_h diff --git a/Source/WebCore/rendering/OverlapTestRequestClient.h b/Source/WebCore/rendering/OverlapTestRequestClient.h new file mode 100644 index 0000000..71400ab --- /dev/null +++ b/Source/WebCore/rendering/OverlapTestRequestClient.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OverlapTestRequestClient_h +#define OverlapTestRequestClient_h + +namespace WebCore { + +class OverlapTestRequestClient { +public: + virtual ~OverlapTestRequestClient() { } + virtual void setOverlapTestResult(bool) = 0; +}; + +} // namespace WebCore + +#endif // OverlapTestRequestClient_h diff --git a/Source/WebCore/rendering/PaintInfo.h b/Source/WebCore/rendering/PaintInfo.h new file mode 100644 index 0000000..3598807 --- /dev/null +++ b/Source/WebCore/rendering/PaintInfo.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PaintInfo_h +#define PaintInfo_h + +#if ENABLE(SVG) +#include "AffineTransform.h" +#endif + +#include "GraphicsContext.h" +#include "IntRect.h" +#include "PaintPhase.h" +#include +#include + +namespace WebCore { + +class OverlapTestRequestClient; +class RenderInline; +class RenderObject; + +typedef HashMap OverlapTestRequestMap; + +/* + * Paint the object and its children, clipped by (x|y|w|h). + * (tx|ty) is the calculated position of the parent + */ +struct PaintInfo { + PaintInfo(GraphicsContext* newContext, const IntRect& newRect, PaintPhase newPhase, bool newForceBlackText, + RenderObject* newPaintingRoot, ListHashSet* newOutlineObjects, + OverlapTestRequestMap* overlapTestRequests = 0) + : context(newContext) + , rect(newRect) + , phase(newPhase) + , forceBlackText(newForceBlackText) + , paintingRoot(newPaintingRoot) + , outlineObjects(newOutlineObjects) + , overlapTestRequests(overlapTestRequests) + { + } + + void updatePaintingRootForChildren(const RenderObject* renderer) + { + if (!paintingRoot) + return; + + // If we're the painting root, kids draw normally, and see root of 0. + if (paintingRoot == renderer) { + paintingRoot = 0; + return; + } + } + + bool shouldPaintWithinRoot(const RenderObject* renderer) const + { + return !paintingRoot || paintingRoot == renderer; + } + +#if ENABLE(SVG) + void applyTransform(const AffineTransform& localToAncestorTransform) + { + if (localToAncestorTransform.isIdentity()) + return; + + context->concatCTM(localToAncestorTransform); + + if (rect == infiniteRect()) + return; + + rect = localToAncestorTransform.inverse().mapRect(rect); + } +#endif + + static IntRect infiniteRect() { return IntRect(INT_MIN / 2, INT_MIN / 2, INT_MAX, INT_MAX); } + + // FIXME: Introduce setters/getters at some point. Requires a lot of changes throughout rendering/. + GraphicsContext* context; + IntRect rect; + PaintPhase phase; + bool forceBlackText; + RenderObject* paintingRoot; // used to draw just one element and its visual kids + ListHashSet* outlineObjects; // used to list outlines that should be painted by a block with inline children + OverlapTestRequestMap* overlapTestRequests; +}; + +} // namespace WebCore + +#endif // PaintInfo_h diff --git a/Source/WebCore/rendering/PaintPhase.h b/Source/WebCore/rendering/PaintPhase.h new file mode 100644 index 0000000..396131f --- /dev/null +++ b/Source/WebCore/rendering/PaintPhase.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef PaintPhase_h +#define PaintPhase_h + +namespace WebCore { + +/* + * The painting of a layer occurs in three distinct phases. Each phase involves + * a recursive descent into the layer's render objects. The first phase is the background phase. + * The backgrounds and borders of all blocks are painted. Inlines are not painted at all. + * Floats must paint above block backgrounds but entirely below inline content that can overlap them. + * In the foreground phase, all inlines are fully painted. Inline replaced elements will get all + * three phases invoked on them during this phase. + */ + +enum PaintPhase { + PaintPhaseBlockBackground, + PaintPhaseChildBlockBackground, + PaintPhaseChildBlockBackgrounds, + PaintPhaseFloat, + PaintPhaseForeground, + PaintPhaseOutline, + PaintPhaseChildOutlines, + PaintPhaseSelfOutline, + PaintPhaseSelection, + PaintPhaseCollapsedTableBorders, + PaintPhaseTextClip, + PaintPhaseMask +}; + +enum PaintBehaviorFlags { + PaintBehaviorNormal = 0, + PaintBehaviorSelectionOnly = 1 << 0, + PaintBehaviorForceBlackText = 1 << 1, + PaintBehaviorFlattenCompositingLayers = 1 << 2 +}; + +typedef unsigned PaintBehavior; + +} // namespace WebCore + +#endif // PaintPhase_h diff --git a/Source/WebCore/rendering/PointerEventsHitRules.cpp b/Source/WebCore/rendering/PointerEventsHitRules.cpp new file mode 100644 index 0000000..cc5aae4 --- /dev/null +++ b/Source/WebCore/rendering/PointerEventsHitRules.cpp @@ -0,0 +1,111 @@ +/* + Copyright (C) 2007 Rob Buis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PointerEventsHitRules.h" + +namespace WebCore { + +PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, const HitTestRequest& request, EPointerEvents pointerEvents) + : requireVisible(false) + , requireFill(false) + , requireStroke(false) + , canHitStroke(false) + , canHitFill(false) +{ + if (request.svgClipContent()) + pointerEvents = PE_FILL; + + if (hitTesting == SVG_PATH_HITTESTING) { + switch (pointerEvents) + { + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireFill = true; + requireStroke = true; + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + requireVisible = true; + canHitFill = true; + break; + case PE_VISIBLE_STROKE: + requireVisible = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + canHitFill = true; + break; + case PE_STROKE: + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; + } + } else { + switch (pointerEvents) + { + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireVisible = true; + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + case PE_VISIBLE_STROKE: + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + case PE_STROKE: + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; + } + } +} + +} + +// vim:ts=4:noet diff --git a/Source/WebCore/rendering/PointerEventsHitRules.h b/Source/WebCore/rendering/PointerEventsHitRules.h new file mode 100644 index 0000000..72fbc45 --- /dev/null +++ b/Source/WebCore/rendering/PointerEventsHitRules.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2007 Rob Buis + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + aint with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef PointerEventsHitRules_h +#define PointerEventsHitRules_h + +#include "HitTestRequest.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +class PointerEventsHitRules { +public: + enum EHitTesting { + SVG_IMAGE_HITTESTING, + SVG_PATH_HITTESTING, + SVG_TEXT_HITTESTING + }; + + PointerEventsHitRules(EHitTesting, const HitTestRequest&, EPointerEvents); + + bool requireVisible; + bool requireFill; + bool requireStroke; + bool canHitStroke; + bool canHitFill; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/Source/WebCore/rendering/RenderApplet.cpp b/Source/WebCore/rendering/RenderApplet.cpp new file mode 100644 index 0000000..eaa3535 --- /dev/null +++ b/Source/WebCore/rendering/RenderApplet.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderApplet.h" + +#include "Frame.h" +#include "HTMLAppletElement.h" +#include "HTMLNames.h" +#include "HTMLParamElement.h" +#include "PluginViewBase.h" +#include "Widget.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderApplet::RenderApplet(HTMLAppletElement* applet, const HashMap& args) + : RenderWidget(applet) + , m_args(args) +{ + setInline(true); +} + +RenderApplet::~RenderApplet() +{ +} + +IntSize RenderApplet::intrinsicSize() const +{ + // FIXME: This doesn't make sense. We can't just start returning + // a different size once we've created the widget and expect + // layout and sizing to be correct. We should remove this and + // pass the appropriate intrinsic size in the constructor. + return widget() ? IntSize(50, 50) : IntSize(150, 150); +} + +void RenderApplet::createWidgetIfNecessary() +{ + HTMLAppletElement* element = static_cast(node()); + if (widget() || !element->isFinishedParsingChildren()) + return; + + // FIXME: Java applets can't be resized (this is a bug in Apple's Java implementation). + // In order to work around this problem and have a correct size from the start, we will + // use fixed widths/heights from the style system when we can, since the widget might + // not have an accurate m_width/m_height. + int contentWidth = style()->width().isFixed() ? style()->width().value() : + width() - borderAndPaddingWidth(); + int contentHeight = style()->height().isFixed() ? style()->height().value() : + height() - borderAndPaddingHeight(); + for (Node* child = element->firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(paramTag)) { + HTMLParamElement* p = static_cast(child); + if (!p->name().isEmpty()) + m_args.set(p->name(), p->value()); + } + } + + Frame* frame = this->frame(); + ASSERT(frame); + setWidget(frame->loader()->subframeLoader()->createJavaAppletWidget(IntSize(contentWidth, contentHeight), element, m_args)); +} + +void RenderApplet::layout() +{ + ASSERT(needsLayout()); + + computeLogicalWidth(); + computeLogicalHeight(); + + // The applet's widget gets created lazily upon first layout. + createWidgetIfNecessary(); + setNeedsLayout(false); +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderApplet::requiresLayer() const +{ + if (RenderWidget::requiresLayer()) + return true; + + return allowsAcceleratedCompositing(); +} + +bool RenderApplet::allowsAcceleratedCompositing() const +{ + return widget() && widget()->isPluginViewBase() && static_cast(widget())->platformLayer(); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderApplet.h b/Source/WebCore/rendering/RenderApplet.h new file mode 100644 index 0000000..007902d --- /dev/null +++ b/Source/WebCore/rendering/RenderApplet.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderApplet_h +#define RenderApplet_h + +#include "RenderWidget.h" +#include + +namespace WebCore { + +class HTMLAppletElement; + +class RenderApplet : public RenderWidget { +public: + RenderApplet(HTMLAppletElement*, const HashMap& args); + virtual ~RenderApplet(); + + void createWidgetIfNecessary(); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool allowsAcceleratedCompositing() const; +#endif + +private: + virtual const char* renderName() const { return "RenderApplet"; } + + virtual bool isApplet() const { return true; } + + virtual void layout(); + virtual IntSize intrinsicSize() const; + +#if USE(ACCELERATED_COMPOSITING) + virtual bool requiresLayer() const; +#endif + + HashMap m_args; +}; + +inline RenderApplet* toRenderApplet(RenderObject* object) +{ + ASSERT(!object || object->isApplet()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderApplet(const RenderApplet*); + +} // namespace WebCore + +#endif // RenderApplet_h diff --git a/Source/WebCore/rendering/RenderArena.cpp b/Source/WebCore/rendering/RenderArena.cpp new file mode 100644 index 0000000..57ed978 --- /dev/null +++ b/Source/WebCore/rendering/RenderArena.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "RenderArena.h" + +#include +#include +#include + +#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) + +namespace WebCore { + +#ifndef NDEBUG + +const int signature = 0xDBA00AEA; +const int signatureDead = 0xDBA00AED; + +typedef struct { + RenderArena* arena; + size_t size; + int signature; +} RenderArenaDebugHeader; + +static const size_t debugHeaderSize = ARENA_ALIGN(sizeof(RenderArenaDebugHeader)); + +#endif + +RenderArena::RenderArena(unsigned arenaSize) +{ + // Initialize the arena pool + INIT_ARENA_POOL(&m_pool, "RenderArena", arenaSize); + + // Zero out the recyclers array + memset(m_recyclers, 0, sizeof(m_recyclers)); +} + +RenderArena::~RenderArena() +{ + FinishArenaPool(&m_pool); +} + +void* RenderArena::allocate(size_t size) +{ +#ifndef NDEBUG + // Use standard malloc so that memory debugging tools work. + ASSERT(this); + void* block = ::malloc(debugHeaderSize + size); + RenderArenaDebugHeader* header = static_cast(block); + header->arena = this; + header->size = size; + header->signature = signature; + return static_cast(block) + debugHeaderSize; +#else + void* result = 0; + + // Ensure we have correct alignment for pointers. Important for Tru64 + size = ROUNDUP(size, sizeof(void*)); + + // Check recyclers first + if (size < gMaxRecycledSize) { + const int index = size >> 2; + + result = m_recyclers[index]; + if (result) { + // Need to move to the next object + void* next = *((void**)result); + m_recyclers[index] = next; + } + } + + if (!result) { + // Allocate a new chunk from the arena + ARENA_ALLOCATE(result, &m_pool, size); + } + + return result; +#endif +} + +void RenderArena::free(size_t size, void* ptr) +{ +#ifndef NDEBUG + // Use standard free so that memory debugging tools work. + void* block = static_cast(ptr) - debugHeaderSize; + RenderArenaDebugHeader* header = static_cast(block); + ASSERT(header->signature == signature); + ASSERT_UNUSED(size, header->size == size); + ASSERT(header->arena == this); + header->signature = signatureDead; + ::free(block); +#else + // Ensure we have correct alignment for pointers. Important for Tru64 + size = ROUNDUP(size, sizeof(void*)); + + // See if it's a size that we recycle + if (size < gMaxRecycledSize) { + const int index = size >> 2; + void* currentTop = m_recyclers[index]; + m_recyclers[index] = ptr; + *((void**)ptr) = currentTop; + } +#endif +} + +#ifdef ANDROID_INSTRUMENT +size_t RenderArena::reportPoolSize() const +{ + return ReportPoolSize(&m_pool); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderArena.h b/Source/WebCore/rendering/RenderArena.h new file mode 100644 index 0000000..edf052a --- /dev/null +++ b/Source/WebCore/rendering/RenderArena.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef RenderArena_h +#define RenderArena_h + +#include "Arena.h" +#include + +namespace WebCore { + +static const size_t gMaxRecycledSize = 400; + +class RenderArena : public Noncopyable { +public: + RenderArena(unsigned arenaSize = 4096); + ~RenderArena(); + + // Memory management functions + void* allocate(size_t); + void free(size_t, void*); + +#ifdef ANDROID_INSTRUMENT + size_t reportPoolSize() const; +#endif + +private: + // Underlying arena pool + ArenaPool m_pool; + + // The recycler array is sparse with the indices being multiples of 4, + // i.e., 0, 4, 8, 12, 16, 20, ... + void* m_recyclers[gMaxRecycledSize >> 2]; +}; + +} // namespace WebCore + +#endif // RenderArena_h diff --git a/Source/WebCore/rendering/RenderBR.cpp b/Source/WebCore/rendering/RenderBR.cpp new file mode 100644 index 0000000..fb136a4 --- /dev/null +++ b/Source/WebCore/rendering/RenderBR.cpp @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderBR.h" + +#include "Document.h" +#include "InlineTextBox.h" +#include "VisiblePosition.h" + +namespace WebCore { + +RenderBR::RenderBR(Node* node) + : RenderText(node, StringImpl::create("\n")) + , m_lineHeight(-1) +{ +} + +RenderBR::~RenderBR() +{ +} + +int RenderBR::lineHeight(bool firstLine) const +{ + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; +} + +void RenderBR::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderText::styleDidChange(diff, oldStyle); + m_lineHeight = -1; +} + +int RenderBR::caretMinOffset() const +{ + return 0; +} + +int RenderBR::caretMaxOffset() const +{ + return 1; +} + +unsigned RenderBR::caretMaxRenderedOffset() const +{ + return 1; +} + +VisiblePosition RenderBR::positionForPoint(const IntPoint&) +{ + return createVisiblePosition(0, DOWNSTREAM); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderBR.h b/Source/WebCore/rendering/RenderBR.h new file mode 100644 index 0000000..7216b5a --- /dev/null +++ b/Source/WebCore/rendering/RenderBR.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderBR_h +#define RenderBR_h + +#include "RenderText.h" + +/* + * The whole class here is a hack to get
working, as long as we don't have support for + * CSS2 :before and :after pseudo elements + */ +namespace WebCore { + +class Position; + +class RenderBR : public RenderText { +public: + RenderBR(Node*); + virtual ~RenderBR(); + + virtual const char* renderName() const { return "RenderBR"; } + + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* /*repaintContainer*/, bool /*clipToVisibleContent*/) { return IntRect(); } + + virtual unsigned width(unsigned /*from*/, unsigned /*len*/, const Font&, int /*xpos*/) const { return 0; } + virtual unsigned width(unsigned /*from*/, unsigned /*len*/, int /*xpos*/, bool /*firstLine = false*/) const { return 0; } + + int lineHeight(bool firstLine) const; + + // overrides + virtual bool isBR() const { return true; } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + virtual VisiblePosition positionForPoint(const IntPoint&); + +protected: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + +private: + mutable int m_lineHeight; +}; + + +inline RenderBR* toRenderBR(RenderObject* object) +{ + ASSERT(!object || object->isBR()); + return static_cast(object); +} + +inline const RenderBR* toRenderBR(const RenderObject* object) +{ + ASSERT(!object || object->isBR()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBR(const RenderBR*); + +} // namespace WebCore + +#endif // RenderBR_h diff --git a/Source/WebCore/rendering/RenderBlock.cpp b/Source/WebCore/rendering/RenderBlock.cpp new file mode 100644 index 0000000..e0fe6a0 --- /dev/null +++ b/Source/WebCore/rendering/RenderBlock.cpp @@ -0,0 +1,6111 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "RenderBlock.h" + +#include "ColumnInfo.h" +#include "Document.h" +#include "Element.h" +#include "FloatQuad.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "InlineTextBox.h" +#include "RenderFlexibleBox.h" +#include "RenderImage.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderMarquee.h" +#include "RenderReplica.h" +#include "RenderTableCell.h" +#include "RenderTextFragment.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "SelectionController.h" +#include "Settings.h" +#include "TransformState.h" +#include + +#ifdef ANDROID_LAYOUT +#include "Settings.h" +#endif + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +using namespace HTMLNames; + +typedef WTF::HashMap ColumnInfoMap; +static ColumnInfoMap* gColumnInfoMap = 0; + +typedef WTF::HashMap*> PercentHeightDescendantsMap; +static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0; + +typedef WTF::HashMap*> PercentHeightContainerMap; +static PercentHeightContainerMap* gPercentHeightContainerMap = 0; + +typedef WTF::HashMap*> ContinuationOutlineTableMap; + +typedef WTF::HashSet DelayedUpdateScrollInfoSet; +static int gDelayUpdateScrollInfo = 0; +static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; + +// Our MarginInfo state used when laying out block children. +RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int beforeBorderPadding, int afterBorderPadding) + : m_atBeforeSideOfBlock(true) + , m_atAfterSideOfBlock(false) + , m_marginBeforeQuirk(false) + , m_marginAfterQuirk(false) + , m_determinedMarginBeforeQuirk(false) +{ + // Whether or not we can collapse our own margins with our children. We don't do this + // if we had any border/padding (obviously), if we're the root or HTML elements, or if + // we're positioned, floating, a table cell. + m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isPositioned() + && !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable() + && !block->isWritingModeRoot(); + + m_canCollapseMarginBeforeWithChildren = m_canCollapseWithChildren && (beforeBorderPadding == 0) && block->style()->marginBeforeCollapse() != MSEPARATE; + + // If any height other than auto is specified in CSS, then we don't collapse our bottom + // margins with our children's margins. To do otherwise would be to risk odd visual + // effects when the children overflow out of the parent block and yet still collapse + // with it. We also don't collapse if we have any bottom border/padding. + m_canCollapseMarginAfterWithChildren = m_canCollapseWithChildren && (afterBorderPadding == 0) && + (block->style()->logicalHeight().isAuto() && block->style()->logicalHeight().value() == 0) && block->style()->marginAfterCollapse() != MSEPARATE; + + m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginBeforeCollapse() == MDISCARD || + block->style()->marginAfterCollapse() == MDISCARD; + + m_positiveMargin = m_canCollapseMarginBeforeWithChildren ? block->maxPositiveMarginBefore() : 0; + m_negativeMargin = m_canCollapseMarginBeforeWithChildren ? block->maxNegativeMarginBefore() : 0; +} + +// ------------------------------------------------------------------------------------------------------- + +RenderBlock::RenderBlock(Node* node) + : RenderBox(node) + , m_floatingObjects(0) + , m_positionedObjects(0) + , m_rareData(0) + , m_lineHeight(-1) +{ + setChildrenInline(true); +} + +RenderBlock::~RenderBlock() +{ + delete m_floatingObjects; + delete m_positionedObjects; + + if (hasColumns()) + delete gColumnInfoMap->take(this); + + if (gPercentHeightDescendantsMap) { + if (HashSet* descendantSet = gPercentHeightDescendantsMap->take(this)) { + HashSet::iterator end = descendantSet->end(); + for (HashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { + HashSet* containerSet = gPercentHeightContainerMap->get(*descendant); + ASSERT(containerSet); + if (!containerSet) + continue; + ASSERT(containerSet->contains(this)); + containerSet->remove(this); + if (containerSet->isEmpty()) { + gPercentHeightContainerMap->remove(*descendant); + delete containerSet; + } + } + delete descendantSet; + } + } +} + +void RenderBlock::destroy() +{ + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + children()->destroyLeftoverChildren(); + + // Destroy our continuation before anything other than anonymous children. + // The reason we don't destroy it before anonymous children is that they may + // have continuations of their own that are anonymous children of our continuation. + RenderBoxModelObject* continuation = this->continuation(); + if (continuation) { + continuation->destroy(); + setContinuation(0); + } + + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // We can't wait for RenderBox::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If we are an anonymous block, then our line boxes might have children + // that will outlast this block. In the non-anonymous block case those + // children will be destroyed by the time we return from this function. + if (isAnonymousBlock()) { + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) { + while (InlineBox* childBox = box->firstChild()) + childBox->remove(); + } + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(renderArena()); + + RenderBox::destroy(); +} + +void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +{ + setReplaced(newStyle->isDisplayReplacedType()); + + if (style() && parent() && diff == StyleDifferenceLayout && style()->position() != newStyle->position()) { + if (newStyle->position() == StaticPosition) + // Clear our positioned objects list. Our absolutely positioned descendants will be + // inserted into our containing block's positioned objects list during layout. + removePositionedObjects(0); + else if (style()->position() == StaticPosition) { + // Remove our absolutely positioned descendants from their current containing block. + // They will be inserted into our positioned objects list during layout. + RenderObject* cb = parent(); + while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { + if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { + cb = cb->containingBlock(); + break; + } + cb = cb->parent(); + } + + if (cb->isRenderBlock()) + toRenderBlock(cb)->removePositionedObjects(this); + } + } + + RenderBox::styleWillChange(diff, newStyle); +} + +void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBox::styleDidChange(diff, oldStyle); + + if (!isAnonymousBlock()) { + // Ensure that all of our continuation blocks pick up the new style. + for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { + RenderBoxModelObject* nextCont = currCont->continuation(); + currCont->setContinuation(0); + currCont->setStyle(style()); + currCont->setContinuation(nextCont); + } + } + + // FIXME: We could save this call when the change only affected non-inherited properties + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isAnonymousBlock()) { + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + if (style()->specifiesColumns()) { + if (child->style()->specifiesColumns()) + newStyle->inheritColumnPropertiesFrom(style()); + if (child->style()->columnSpan()) + newStyle->setColumnSpan(true); + } + newStyle->setDisplay(BLOCK); + child->setStyle(newStyle.release()); + } + } + + m_lineHeight = -1; + + // Update pseudos for :before and :after now. + if (!isAnonymous() && document()->usesBeforeAfterRules() && canHaveChildren()) { + updateBeforeAfterContent(BEFORE); + updateBeforeAfterContent(AFTER); + } +} + +void RenderBlock::updateBeforeAfterContent(PseudoId pseudoId) +{ + // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. + if (parent() && parent()->createsAnonymousWrapper()) + return; + return children()->updateBeforeAfterContent(this, pseudoId); +} + +RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) +{ + if (beforeChild && beforeChild->parent() == this) + return this; + + RenderBlock* curr = toRenderBlock(continuation()); + RenderBlock* nextToLast = this; + RenderBlock* last = this; + while (curr) { + if (beforeChild && beforeChild->parent() == curr) { + if (curr->firstChild() == beforeChild) + return last; + return curr; + } + + nextToLast = last; + last = curr; + curr = toRenderBlock(curr->continuation()); + } + + if (!beforeChild && !last->firstChild()) + return nextToLast; + return last; +} + +void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + RenderBlock* flow = continuationBefore(beforeChild); + ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); + RenderBoxModelObject* beforeChildParent = 0; + if (beforeChild) + beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); + else { + RenderBoxModelObject* cont = flow->continuation(); + if (cont) + beforeChildParent = cont; + else + beforeChildParent = flow; + } + + if (newChild->isFloatingOrPositioned()) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + + // A continuation always consists of two potential candidates: a block or an anonymous + // column span box holding column span children. + bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan(); + bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan(); + bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan(); + + if (flow == beforeChildParent) + return flow->addChildIgnoringContinuation(newChild, beforeChild); + + // The goal here is to match up if we can, so that we can coalesce and create the + // minimal # of continuations needed for the inline. + if (childIsNormal == bcpIsNormal) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + if (flowIsNormal == childIsNormal) + return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); +} + + +void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) +{ + ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block. + + // The goal is to locate a suitable box in which to place our child. + RenderBlock* beforeChildParent = toRenderBlock(beforeChild && beforeChild->parent()->isRenderBlock() ? beforeChild->parent() : lastChild()); + + // If the new child is floating or positioned it can just go in that block. + if (newChild->isFloatingOrPositioned()) + return beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); + + // See if the child can be placed in the box. + bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline(); + bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock(); + + if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) + return beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); + + if (!beforeChild) { + // Create a new block of the correct type. + RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); + children()->appendChildNode(this, newBox); + newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); + return; + } + + RenderObject* immediateChild = beforeChild; + bool isPreviousBlockViable = true; + while (immediateChild->parent() != this) { + if (isPreviousBlockViable) + isPreviousBlockViable = !immediateChild->previousSibling(); + immediateChild = immediateChild->parent(); + } + if (isPreviousBlockViable && immediateChild->previousSibling()) + return toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append. + + // Split our anonymous blocks. + RenderObject* newBeforeChild = splitAnonymousBlocksAroundChild(beforeChild); + + // Create a new anonymous box of the appropriate type. + RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); + children()->insertChildNode(this, newBox, newBeforeChild); + newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); + return; +} + +RenderBlock* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock) +{ + for (RenderObject* curr = this; curr; curr = curr->parent()) { + if (!curr->isRenderBlock() || curr->isFloatingOrPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip() + || curr->isInlineBlockOrInlineTable()) + return 0; + + RenderBlock* currBlock = toRenderBlock(curr); + if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock())) + return currBlock; + + if (currBlock->isAnonymousColumnSpanBlock()) + return 0; + } + return 0; +} + +RenderBlock* RenderBlock::clone() const +{ + RenderBlock* cloneBlock; + if (isAnonymousBlock()) + cloneBlock = createAnonymousBlock(); + else { + cloneBlock = new (renderArena()) RenderBlock(node()); + cloneBlock->setStyle(style()); + } + cloneBlock->setChildrenInline(childrenInline()); + return cloneBlock; +} + +void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, + RenderBlock* middleBlock, + RenderObject* beforeChild, RenderBoxModelObject* oldCont) +{ + // Create a clone of this inline. + RenderBlock* cloneBlock = clone(); + if (!isAnonymousBlock()) + cloneBlock->setContinuation(oldCont); + + // Now take all of the children from beforeChild to the end and remove + // them from |this| and place them in the clone. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + moveChildrenTo(cloneBlock, beforeChild, 0); + + // Hook |clone| up as the continuation of the middle block. + if (!cloneBlock->isAnonymousBlock()) + middleBlock->setContinuation(cloneBlock); + + // We have been reparented and are now under the fromBlock. We need + // to walk up our block parent chain until we hit the containing anonymous columns block. + // Once we hit the anonymous columns block we're done. + RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); + RenderBoxModelObject* currChild = this; + + while (curr && curr != fromBlock) { + ASSERT(curr->isRenderBlock()); + + RenderBlock* blockCurr = toRenderBlock(curr); + + // Create a new clone. + RenderBlock* cloneChild = cloneBlock; + cloneBlock = blockCurr->clone(); + + // Insert our child clone as the first child. + cloneBlock->children()->appendChildNode(cloneBlock, cloneChild); + + // Hook the clone up as a continuation of |curr|. Note we do encounter + // anonymous blocks possibly as we walk up the block chain. When we split an + // anonymous block, there's no need to do any continuation hookup, since we haven't + // actually split a real element. + if (!blockCurr->isAnonymousBlock()) { + oldCont = blockCurr->continuation(); + blockCurr->setContinuation(cloneBlock); + cloneBlock->setContinuation(oldCont); + } + + // Someone may have indirectly caused a to split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after + // content gets properly destroyed. + if (document()->usesBeforeAfterRules()) + blockCurr->children()->updateBeforeAfterContent(blockCurr, AFTER); + + // Now we need to take all of the children starting from the first child + // *after* currChild and append them all to the clone. + RenderObject* afterContent = isAfterContent(cloneBlock->lastChild()) ? cloneBlock->lastChild() : 0; + blockCurr->moveChildrenTo(cloneBlock, currChild->nextSibling(), 0, afterContent); + + // Keep walking up the chain. + currChild = curr; + curr = toRenderBoxModelObject(curr->parent()); + } + + // Now we are at the columns block level. We need to put the clone into the toBlock. + toBlock->children()->appendChildNode(toBlock, cloneBlock); + + // Now take all the children after currChild and remove them from the fromBlock + // and put them in the toBlock. + fromBlock->moveChildrenTo(toBlock, currChild->nextSibling(), 0); +} + +void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderBoxModelObject* oldCont) +{ + RenderBlock* pre = 0; + RenderBlock* block = containingColumnsBlock(); + + // Delete our line boxes before we do the inline split into continuations. + block->deleteLineBoxTree(); + + bool madeNewBeforeBlock = false; + if (block->isAnonymousColumnsBlock()) { + // We can reuse this block and make it the preBlock of the next continuation. + pre = block; + pre->removePositionedObjects(0); + block = toRenderBlock(block->parent()); + } else { + // No anonymous block available for use. Make one. + pre = block->createAnonymousColumnsBlock(); + pre->setChildrenInline(false); + madeNewBeforeBlock = true; + } + + RenderBlock* post = block->createAnonymousColumnsBlock(); + post->setChildrenInline(false); + + RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); + if (madeNewBeforeBlock) + block->children()->insertChildNode(block, pre, boxFirst); + block->children()->insertChildNode(block, newBlockBox, boxFirst); + block->children()->insertChildNode(block, post, boxFirst); + block->setChildrenInline(false); + + if (madeNewBeforeBlock) + block->moveChildrenTo(pre, boxFirst, 0); + + splitBlocks(pre, post, newBlockBox, beforeChild, oldCont); + + // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting + // time in makeChildrenNonInline by just setting this explicitly up front. + newBlockBox->setChildrenInline(false); + + // We delayed adding the newChild until now so that the |newBlockBox| would be fully + // connected, thus allowing newChild access to a renderArena should it need + // to wrap itself in additional boxes (e.g., table construction). + newBlockBox->addChild(newChild); + + // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) + // get deleted properly. Because objects moves from the pre block into the post block, we want to + // make new line boxes instead of leaving the old line boxes around. + pre->setNeedsLayoutAndPrefWidthsRecalc(); + block->setNeedsLayoutAndPrefWidthsRecalc(); + post->setNeedsLayoutAndPrefWidthsRecalc(); +} + +RenderObject* RenderBlock::splitAnonymousBlocksAroundChild(RenderObject* beforeChild) +{ + while (beforeChild->parent() != this) { + RenderBlock* blockToSplit = toRenderBlock(beforeChild->parent()); + if (blockToSplit->firstChild() != beforeChild) { + // We have to split the parentBlock into two blocks. + RenderBlock* post = createAnonymousBlockWithSameTypeAs(blockToSplit); + post->setChildrenInline(blockToSplit->childrenInline()); + RenderBlock* parentBlock = toRenderBlock(blockToSplit->parent()); + parentBlock->children()->insertChildNode(parentBlock, post, blockToSplit->nextSibling()); + blockToSplit->moveChildrenTo(post, beforeChild, 0, blockToSplit->hasLayer()); + post->setNeedsLayoutAndPrefWidthsRecalc(); + blockToSplit->setNeedsLayoutAndPrefWidthsRecalc(); + beforeChild = post; + } else + beforeChild = blockToSplit; + } + return beforeChild; +} + +void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild) +{ + RenderBlock* pre = 0; + RenderBlock* post = 0; + RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable + // so that we don't have to patch all of the rest of the code later on. + + // Delete the block's line boxes before we do the split. + block->deleteLineBoxTree(); + + if (beforeChild && beforeChild->parent() != this) + beforeChild = splitAnonymousBlocksAroundChild(beforeChild); + + if (beforeChild != firstChild()) { + pre = block->createAnonymousColumnsBlock(); + pre->setChildrenInline(block->childrenInline()); + } + + if (beforeChild) { + post = block->createAnonymousColumnsBlock(); + post->setChildrenInline(block->childrenInline()); + } + + RenderObject* boxFirst = block->firstChild(); + if (pre) + block->children()->insertChildNode(block, pre, boxFirst); + block->children()->insertChildNode(block, newBlockBox, boxFirst); + if (post) + block->children()->insertChildNode(block, post, boxFirst); + block->setChildrenInline(false); + + // The pre/post blocks always have layers, so we know to always do a full insert/remove (so we pass true as the last argument). + block->moveChildrenTo(pre, boxFirst, beforeChild, true); + block->moveChildrenTo(post, beforeChild, 0, true); + + // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting + // time in makeChildrenNonInline by just setting this explicitly up front. + newBlockBox->setChildrenInline(false); + + // We delayed adding the newChild until now so that the |newBlockBox| would be fully + // connected, thus allowing newChild access to a renderArena should it need + // to wrap itself in additional boxes (e.g., table construction). + newBlockBox->addChild(newChild); + + // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) + // get deleted properly. Because objects moved from the pre block into the post block, we want to + // make new line boxes instead of leaving the old line boxes around. + if (pre) + pre->setNeedsLayoutAndPrefWidthsRecalc(); + block->setNeedsLayoutAndPrefWidthsRecalc(); + if (post) + post->setNeedsLayoutAndPrefWidthsRecalc(); +} + +RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) +{ + // FIXME: This function is the gateway for the addition of column-span support. It will + // be added to in three stages: + // (1) Immediate children of a multi-column block can span. + // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span. + // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we + // cross the streams and have to cope with both types of continuations mixed together). + // This function currently supports (1) and (2). + RenderBlock* columnsBlockAncestor = 0; + if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isFloatingOrPositioned() + && !newChild->isInline() && !isAnonymousColumnSpanBlock()) { + if (style()->specifiesColumns()) + columnsBlockAncestor = this; + else if (parent() && parent()->isRenderBlock()) + columnsBlockAncestor = toRenderBlock(parent())->containingColumnsBlock(false); + } + return columnsBlockAncestor; +} + +void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild) { + RenderObject* lastRenderer = lastChild(); + if (isAfterContent(lastRenderer)) + beforeChild = lastRenderer; + else if (lastRenderer && lastRenderer->isAnonymousBlock() && isAfterContent(lastRenderer->lastChild())) + beforeChild = lastRenderer->lastChild(); + } + + // If the requested beforeChild is not one of our children, then this is because + // there is an anonymous container within this object that contains the beforeChild. + if (beforeChild && beforeChild->parent() != this) { + RenderObject* anonymousChild = beforeChild->parent(); + ASSERT(anonymousChild); + + while (anonymousChild->parent() != this) + anonymousChild = anonymousChild->parent(); + + ASSERT(anonymousChild->isAnonymous()); + + if (anonymousChild->isAnonymousBlock()) { + // Insert the child into the anonymous block box instead of here. + if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) + beforeChild->parent()->addChild(newChild, beforeChild); + else + addChild(newChild, beforeChild->parent()); + return; + } + + ASSERT(anonymousChild->isTable()); + if ((newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) + || (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) + || newChild->isTableSection() + || newChild->isTableRow() + || newChild->isTableCell()) { + // Insert into the anonymous table. + anonymousChild->addChild(newChild, beforeChild); + return; + } + + // Go on to insert before the anonymous table. + beforeChild = anonymousChild; + } + + // Check for a spanning element in columns. + RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); + if (columnsBlockAncestor) { + // We are placing a column-span element inside a block. + RenderBlock* newBox = createAnonymousColumnSpanBlock(); + + if (columnsBlockAncestor != this) { + // We are nested inside a multi-column element and are being split by the span. We have to break up + // our block into continuations. + RenderBoxModelObject* oldContinuation = continuation(); + setContinuation(newBox); + + // Someone may have put a

inside a , causing a split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after + // content gets properly destroyed. + bool isLastChild = (beforeChild == lastChild()); + if (document()->usesBeforeAfterRules()) + children()->updateBeforeAfterContent(this, AFTER); + if (isLastChild && beforeChild != lastChild()) + beforeChild = 0; // We destroyed the last child, so now we need to update our insertion + // point to be 0. It's just a straight append now. + + splitFlow(beforeChild, newBox, newChild, oldContinuation); + return; + } + + // We have to perform a split of this block's children. This involves creating an anonymous block box to hold + // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into + // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. + makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); + return; + } + + bool madeBoxesNonInline = false; + + // A block has to either have all of its children inline, or all of its children as blocks. + // So, if our children are currently inline and a block child has to be inserted, we move all our + // inline children into anonymous block boxes. + if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { + // This is a block with inline content. Wrap the inline content in anonymous blocks. + makeChildrenNonInline(beforeChild); + madeBoxesNonInline = true; + + if (beforeChild && beforeChild->parent() != this) { + beforeChild = beforeChild->parent(); + ASSERT(beforeChild->isAnonymousBlock()); + ASSERT(beforeChild->parent() == this); + } + } else if (!childrenInline() && (newChild->isFloatingOrPositioned() || newChild->isInline())) { + // If we're inserting an inline child but all of our children are blocks, then we have to make sure + // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise + // a new one is created and inserted into our list of children in the appropriate position. + RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); + + if (afterChild && afterChild->isAnonymousBlock()) { + afterChild->addChild(newChild); + return; + } + + if (newChild->isInline()) { + // No suitable existing anonymous box - create a new one. + RenderBlock* newBox = createAnonymousBlock(); + RenderBox::addChild(newBox, beforeChild); + newBox->addChild(newChild); + return; + } + } + + RenderBox::addChild(newChild, beforeChild); + + if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + // this object may be dead here +} + +void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (continuation() && !isAnonymousBlock()) + return addChildToContinuation(newChild, beforeChild); + return addChildIgnoringContinuation(newChild, beforeChild); +} + +void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) + return addChildToAnonymousColumnBlocks(newChild, beforeChild); + return addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); +} + +static void getInlineRun(RenderObject* start, RenderObject* boundary, + RenderObject*& inlineRunStart, + RenderObject*& inlineRunEnd) +{ + // Beginning at |start| we find the largest contiguous run of inlines that + // we can. We denote the run with start and end points, |inlineRunStart| + // and |inlineRunEnd|. Note that these two values may be the same if + // we encounter only one inline. + // + // We skip any non-inlines we encounter as long as we haven't found any + // inlines yet. + // + // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary| + // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered + // a non-inline. + + // Start by skipping as many non-inlines as we can. + RenderObject * curr = start; + bool sawInline; + do { + while (curr && !(curr->isInline() || curr->isFloatingOrPositioned())) + curr = curr->nextSibling(); + + inlineRunStart = inlineRunEnd = curr; + + if (!curr) + return; // No more inline children to be found. + + sawInline = curr->isInline(); + + curr = curr->nextSibling(); + while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != boundary)) { + inlineRunEnd = curr; + if (curr->isInline()) + sawInline = true; + curr = curr->nextSibling(); + } + } while (!sawInline); +} + +void RenderBlock::deleteLineBoxTree() +{ + m_lineBoxes.deleteLineBoxTree(renderArena()); +} + +RootInlineBox* RenderBlock::createRootInlineBox() +{ + return new (renderArena()) RootInlineBox(this); +} + +RootInlineBox* RenderBlock::createAndAppendRootInlineBox() +{ + RootInlineBox* rootBox = createRootInlineBox(); + m_lineBoxes.appendLineBox(rootBox); + return rootBox; +} + +void RenderBlock::moveChildTo(RenderBlock* to, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert) +{ + ASSERT(this == child->parent()); + ASSERT(!beforeChild || to == beforeChild->parent()); + to->children()->insertChildNode(to, children()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert); +} + +void RenderBlock::moveChildrenTo(RenderBlock* to, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert) +{ + ASSERT(!beforeChild || to == beforeChild->parent()); + RenderObject* nextChild = startChild; + while (nextChild && nextChild != endChild) { + RenderObject* child = nextChild; + nextChild = child->nextSibling(); + to->children()->insertChildNode(to, children()->removeChildNode(this, child, fullRemoveInsert), beforeChild, fullRemoveInsert); + if (child == endChild) + return; + } +} + +void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) +{ + // makeChildrenNonInline takes a block whose children are *all* inline and it + // makes sure that inline children are coalesced under anonymous + // blocks. If |insertionPoint| is defined, then it represents the insertion point for + // the new block child that is causing us to have to wrap all the inlines. This + // means that we cannot coalesce inlines before |insertionPoint| with inlines following + // |insertionPoint|, because the new child is going to be inserted in between the inlines, + // splitting them. + ASSERT(isInlineBlockOrInlineTable() || !isInline()); + ASSERT(!insertionPoint || insertionPoint->parent() == this); + + setChildrenInline(false); + + RenderObject *child = firstChild(); + if (!child) + return; + + deleteLineBoxTree(); + + while (child) { + RenderObject *inlineRunStart, *inlineRunEnd; + getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); + + if (!inlineRunStart) + break; + + child = inlineRunEnd->nextSibling(); + + RenderBlock* block = createAnonymousBlock(); + children()->insertChildNode(this, block, inlineRunStart); + moveChildrenTo(block, inlineRunStart, child); + } + +#ifndef NDEBUG + for (RenderObject *c = firstChild(); c; c = c->nextSibling()) + ASSERT(!c->isInline()); +#endif + + repaint(); +} + +void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) +{ + ASSERT(child->isAnonymousBlock()); + ASSERT(!child->childrenInline()); + + if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock()))) + return; + + RenderObject* firstAnChild = child->m_children.firstChild(); + RenderObject* lastAnChild = child->m_children.lastChild(); + if (firstAnChild) { + RenderObject* o = firstAnChild; + while (o) { + o->setParent(this); + o = o->nextSibling(); + } + firstAnChild->setPreviousSibling(child->previousSibling()); + lastAnChild->setNextSibling(child->nextSibling()); + if (child->previousSibling()) + child->previousSibling()->setNextSibling(firstAnChild); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(lastAnChild); + + if (child == m_children.firstChild()) + m_children.setFirstChild(firstAnChild); + if (child == m_children.lastChild()) + m_children.setLastChild(lastAnChild); + } else { + if (child == m_children.firstChild()) + m_children.setFirstChild(child->nextSibling()); + if (child == m_children.lastChild()) + m_children.setLastChild(child->previousSibling()); + + if (child->previousSibling()) + child->previousSibling()->setNextSibling(child->nextSibling()); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(child->previousSibling()); + } + child->setParent(0); + child->setPreviousSibling(0); + child->setNextSibling(0); + + child->children()->setFirstChild(0); + child->m_next = 0; + + child->destroy(); +} + +static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next) +{ + if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation()) + return false; + + if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation())) + || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation()))) + return false; + + // FIXME: This check isn't required when inline run-ins can't be split into continuations. + if (prev && prev->firstChild() && prev->firstChild()->isInline() && prev->firstChild()->isRunIn()) + return false; + + if ((prev && (prev->isRubyRun() || prev->isRubyBase())) + || (next && (next->isRubyRun() || next->isRubyBase()))) + return false; + + if (!prev || !next) + return true; + + // Make sure the types of the anonymous blocks match up. + return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock() + && prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock(); +} + +void RenderBlock::removeChild(RenderObject* oldChild) +{ + // If this child is a block, and if our previous and next siblings are + // both anonymous blocks with inline content, then we can go ahead and + // fold the inline content back together. + RenderObject* prev = oldChild->previousSibling(); + RenderObject* next = oldChild->nextSibling(); + bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); + if (canMergeAnonymousBlocks && prev && next) { + prev->setNeedsLayoutAndPrefWidthsRecalc(); + RenderBlock* nextBlock = toRenderBlock(next); + RenderBlock* prevBlock = toRenderBlock(prev); + + if (prev->childrenInline() != next->childrenInline()) { + RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; + RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; + + // Place the inline children block inside of the block children block instead of deleting it. + // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure + // to clear out inherited column properties by just making a new style, and to also clear the + // column span flag if it is set. + ASSERT(!inlineChildrenBlock->continuation()); + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlock->hasLayer()); + inlineChildrenBlock->setStyle(newStyle); + + // Now just put the inlineChildrenBlock inside the blockChildrenBlock. + blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0, + inlineChildrenBlock->hasLayer() || blockChildrenBlock->hasLayer()); + next->setNeedsLayoutAndPrefWidthsRecalc(); + + // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child + // of "this". we null out prev or next so that is not used later in the function. + if (inlineChildrenBlock == prevBlock) + prev = 0; + else + next = 0; + } else { + // Take all the children out of the |next| block and put them in + // the |prev| block. + nextBlock->moveAllChildrenTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); + + // FIXME: When we destroy nextBlock, it might happen that nextBlock's next sibling block and + // oldChild can get merged. Since oldChild is getting removed, we do not want to move + // nextBlock's next sibling block's children into it. By setting a fake continuation, + // we prevent this from happening. This is not the best approach, we should replace this + // something better later to automatically detect that oldChild is getting removed. + RenderBlock* oldChildBlock = 0; + if (oldChild->isAnonymous() && oldChild->isRenderBlock() && !toRenderBlock(oldChild)->continuation()) { + oldChildBlock = toRenderBlock(oldChild); + oldChildBlock->setContinuation(oldChildBlock); + } + + // Delete the now-empty block's lines and nuke it. + nextBlock->deleteLineBoxTree(); + nextBlock->destroy(); + next = 0; + + // FIXME: Revert the continuation change done above. + if (oldChildBlock) + oldChildBlock->setContinuation(0); + } + } + + RenderBox::removeChild(oldChild); + + RenderObject* child = prev ? prev : next; + if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) { + // The removal has knocked us down to containing only a single anonymous + // box. We can go ahead and pull the content right back up into our + // box. + setNeedsLayoutAndPrefWidthsRecalc(); + setChildrenInline(child->childrenInline()); + RenderBlock* anonBlock = toRenderBlock(children()->removeChildNode(this, child, child->hasLayer())); + anonBlock->moveAllChildrenTo(this, child->hasLayer()); + // Delete the now-empty block's lines and nuke it. + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } + + if (!firstChild() && !documentBeingDestroyed()) { + // If this was our last child be sure to clear out our line boxes. + if (childrenInline()) + lineBoxes()->deleteLineBoxes(renderArena()); + } +} + +bool RenderBlock::isSelfCollapsingBlock() const +{ + // We are not self-collapsing if we + // (a) have a non-zero height according to layout (an optimization to avoid wasting time) + // (b) are a table, + // (c) have border/padding, + // (d) have a min-height + // (e) have specified that one of our margins can't collapse using a CSS extension + if (logicalHeight() > 0 + || isTable() || borderAndPaddingLogicalHeight() + || style()->logicalMinHeight().isPositive() + || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE) + return false; + + Length logicalHeightLength = style()->logicalHeight(); + bool hasAutoHeight = logicalHeightLength.isAuto(); + if (logicalHeightLength.isPercent() && !document()->inQuirksMode()) { + hasAutoHeight = true; + for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { + if (cb->style()->logicalHeight().isFixed() || cb->isTableCell()) + hasAutoHeight = false; + } + } + + // If the height is 0 or auto, then whether or not we are a self-collapsing block depends + // on whether we have content that is all self-collapsing or not. + if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { + // If the block has inline children, see if we generated any line boxes. If we have any + // line boxes, then we can't be self-collapsing, since we have content. + if (childrenInline()) + return !firstLineBox(); + + // Whether or not we collapse is dependent on whether all our normal flow children + // are also self-collapsing. + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->isFloatingOrPositioned()) + continue; + if (!child->isSelfCollapsingBlock()) + return false; + } + return true; + } + return false; +} + +void RenderBlock::startDelayUpdateScrollInfo() +{ + if (gDelayUpdateScrollInfo == 0) { + ASSERT(!gDelayedUpdateScrollInfoSet); + gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet; + } + ASSERT(gDelayedUpdateScrollInfoSet); + ++gDelayUpdateScrollInfo; +} + +void RenderBlock::finishDelayUpdateScrollInfo() +{ + --gDelayUpdateScrollInfo; + ASSERT(gDelayUpdateScrollInfo >= 0); + if (gDelayUpdateScrollInfo == 0) { + ASSERT(gDelayedUpdateScrollInfoSet); + + OwnPtr infoSet(gDelayedUpdateScrollInfoSet); + gDelayedUpdateScrollInfoSet = 0; + + for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { + RenderBlock* block = *it; + if (block->hasOverflowClip()) { + block->layer()->updateScrollInfoAfterLayout(); + } + } + } +} + +void RenderBlock::updateScrollInfoAfterLayout() +{ + if (hasOverflowClip()) { + if (gDelayUpdateScrollInfo) + gDelayedUpdateScrollInfoSet->add(this); + else + layer()->updateScrollInfoAfterLayout(); + } +} + +void RenderBlock::layout() +{ + // Update our first letter info now. + updateFirstLetter(); + + // Table cells call layoutBlock directly, so don't add any logic here. Put code into + // layoutBlock(). + layoutBlock(false); + + // It's safe to check for control clip here, since controls can never be table cells. + // If we have a lightweight clip, there can never be any overflow from children. + if (hasControlClip() && m_overflow) + clearLayoutOverflow(); +} + +void RenderBlock::layoutBlock(bool relayoutChildren, int pageLogicalHeight) +{ + ASSERT(needsLayout()); + + if (isInline() && !isInlineBlockOrInlineTable()) // Inline

s inside various table elements can + return; // cause us to come in here. Just bail. + + if (!relayoutChildren && layoutOnlyPositionedObjects()) + return; + + LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout()); + + int oldWidth = logicalWidth(); + int oldColumnWidth = desiredColumnWidth(); + + computeLogicalWidth(); + calcColumnWidth(); + + m_overflow.clear(); + + if (oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth()) + relayoutChildren = true; + +#ifdef ANDROID_LAYOUT + checkAndSetRelayoutChildren(&relayoutChildren); +#endif + + clearFloats(); + + int previousHeight = logicalHeight(); + setLogicalHeight(0); + bool hasSpecifiedPageLogicalHeight = false; + bool pageLogicalHeightChanged = false; + ColumnInfo* colInfo = columnInfo(); + if (hasColumns()) { + if (!pageLogicalHeight) { + // We need to go ahead and set our explicit page height if one exists, so that we can + // avoid doing two layout passes. + computeLogicalHeight(); + int columnHeight = contentLogicalHeight(); + if (columnHeight > 0) { + pageLogicalHeight = columnHeight; + hasSpecifiedPageLogicalHeight = true; + } + setLogicalHeight(0); + } + if (colInfo->columnHeight() != pageLogicalHeight && m_everHadLayout) { + colInfo->setColumnHeight(pageLogicalHeight); + pageLogicalHeightChanged = true; + } + + if (!hasSpecifiedPageLogicalHeight && !pageLogicalHeight) + colInfo->clearForcedBreaks(); + } + + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode(), pageLogicalHeight, pageLogicalHeightChanged, colInfo); + + // We use four values, maxTopPos, maxTopNeg, maxBottomPos, and maxBottomNeg, to track + // our current maximal positive and negative margins. These values are used when we + // are collapsed with adjacent blocks, so for example, if you have block A and B + // collapsing together, then you'd take the maximal positive margin from both A and B + // and subtract it from the maximal negative margin from both A and B to get the + // true collapsed margin. This algorithm is recursive, so when we finish layout() + // our block knows its current maximal positive/negative values. + // + // Start out by setting our margin values to our current margins. Table cells have + // no margins, so we don't fill in the values for table cells. + bool isCell = isTableCell(); + if (!isCell) { + initMaxMarginValues(); + + setMarginBeforeQuirk(style()->marginBefore().quirk()); + setMarginAfterQuirk(style()->marginAfter().quirk()); + + Node* n = node(); + if (n && n->hasTagName(formTag) && static_cast(n)->isMalformed()) { + // See if this form is malformed (i.e., unclosed). If so, don't give the form + // a bottom margin. + setMaxMarginAfterValues(0, 0); + } + + setPaginationStrut(0); + } + + // For overflow:scroll blocks, ensure we have both scrollbars in place always. + if (scrollsOverflow()) { + if (style()->overflowX() == OSCROLL) + layer()->setHasHorizontalScrollbar(true); + if (style()->overflowY() == OSCROLL) + layer()->setHasVerticalScrollbar(true); + } + + int repaintLogicalTop = 0; + int repaintLogicalBottom = 0; + int maxFloatLogicalBottom = 0; + if (!firstChild() && !isAnonymousBlock()) + setChildrenInline(true); + if (childrenInline()) + layoutInlineChildren(relayoutChildren, repaintLogicalTop, repaintLogicalBottom); + else + layoutBlockChildren(relayoutChildren, maxFloatLogicalBottom); + + // Expand our intrinsic height to encompass floats. + int toAdd = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + if (lowestFloatLogicalBottom() > (logicalHeight() - toAdd) && expandsToEncloseOverhangingFloats()) + setLogicalHeight(lowestFloatLogicalBottom() + toAdd); + + if (layoutColumns(hasSpecifiedPageLogicalHeight, pageLogicalHeight, statePusher)) + return; + + // Calculate our new height. + int oldHeight = logicalHeight(); + int oldClientAfterEdge = clientLogicalBottom(); + computeLogicalHeight(); + int newHeight = logicalHeight(); + if (oldHeight != newHeight) { + if (oldHeight > newHeight && maxFloatLogicalBottom > newHeight && !childrenInline()) { + // One of our children's floats may have become an overhanging float for us. We need to look for it. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isBlockFlow() && !child->isFloatingOrPositioned()) { + RenderBlock* block = toRenderBlock(child); + if (block->lowestFloatLogicalBottom() + block->logicalTop() > newHeight) + addOverhangingFloats(block, -block->logicalLeft(), -block->logicalTop(), false); + } + } + } + } + + if (previousHeight != newHeight) + relayoutChildren = true; + + layoutPositionedObjects(relayoutChildren || isRoot()); + + // Add overflow from children (unless we're multi-column, since in that case all our child overflow is clipped anyway). + computeOverflow(oldClientAfterEdge); + + statePusher.pop(); + + if (view()->layoutState()->m_pageLogicalHeight) + setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(y())); + + updateLayerTransform(); + + // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if + // we overflow or not. + updateScrollInfoAfterLayout(); + + // Repaint with our new bounds if they are different from our old bounds. + bool didFullRepaint = repainter.repaintAfterLayout(); + if (!didFullRepaint && repaintLogicalTop != repaintLogicalBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { + // FIXME: We could tighten up the left and right invalidation points if we let layoutInlineChildren fill them in based off the particular lines + // it had to lay out. We wouldn't need the hasOverflowClip() hack in that case either. + int repaintLogicalLeft = logicalLeftVisualOverflow(); + int repaintLogicalRight = logicalRightVisualOverflow(); + if (hasOverflowClip()) { + // If we have clipped overflow, we should use layout overflow as well, since visual overflow from lines didn't propagate to our block's overflow. + // Note the old code did this as well but even for overflow:visible. The addition of hasOverflowClip() at least tightens up the hack a bit. + // layoutInlineChildren should be patched to compute the entire repaint rect. + repaintLogicalLeft = min(repaintLogicalLeft, logicalLeftLayoutOverflow()); + repaintLogicalRight = max(repaintLogicalRight, logicalRightLayoutOverflow()); + } + + IntRect repaintRect; + if (style()->isHorizontalWritingMode()) + repaintRect = IntRect(repaintLogicalLeft, repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop); + else + repaintRect = IntRect(repaintLogicalTop, repaintLogicalLeft, repaintLogicalBottom - repaintLogicalTop, repaintLogicalRight - repaintLogicalLeft); + + // The repaint rect may be split across columns, in which case adjustRectForColumns() will return the union. + adjustRectForColumns(repaintRect); + + repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline)); + + if (hasOverflowClip()) { + // Adjust repaint rect for scroll offset + repaintRect.move(-layer()->scrolledContentOffset()); + + // Don't allow this rect to spill out of our overflow box. + repaintRect.intersect(IntRect(0, 0, width(), height())); + } + + // Make sure the rect is still non-empty after intersecting for overflow above + if (!repaintRect.isEmpty()) { + repaintRectangle(repaintRect); // We need to do a partial repaint of our content. + if (hasReflection()) + repaintRectangle(reflectedRect(repaintRect)); + } + } + setNeedsLayout(false); +} + +void RenderBlock::addOverflowFromChildren() +{ + if (!hasColumns()) { + if (childrenInline()) + addOverflowFromInlineChildren(); + else + addOverflowFromBlockChildren(); + } else { + ColumnInfo* colInfo = columnInfo(); + if (columnCount(colInfo)) { + IntRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); + int overflowLeft = !style()->isLeftToRightDirection() ? min(0, lastRect.x()) : 0; + int overflowRight = style()->isLeftToRightDirection() ? max(width(), lastRect.x() + lastRect.width()) : 0; + int overflowHeight = borderTop() + paddingTop() + colInfo->columnHeight(); + addLayoutOverflow(IntRect(overflowLeft, 0, overflowRight - overflowLeft, overflowHeight)); + } + } +} + +void RenderBlock::computeOverflow(int oldClientAfterEdge, bool recomputeFloats) +{ + // Add overflow from children. + addOverflowFromChildren(); + + if (!hasColumns() && (recomputeFloats || isRoot() || expandsToEncloseOverhangingFloats() || hasSelfPaintingLayer())) + addOverflowFromFloats(); + + // Add in the overflow from positioned objects. + addOverflowFromPositionedObjects(); + + if (hasOverflowClip()) { + // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins + // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always + // be considered reachable. + IntRect clientRect(clientBoxRect()); + IntRect rectToApply; + if (style()->isHorizontalWritingMode()) + rectToApply = IntRect(clientRect.x(), clientRect.y(), 1, max(0, oldClientAfterEdge - clientRect.y())); + else + rectToApply = IntRect(clientRect.x(), clientRect.y(), max(0, oldClientAfterEdge - clientRect.x()), 1); + addLayoutOverflow(rectToApply); + } + + // Add visual overflow from box-shadow and reflections. + addShadowOverflow(); +} + +void RenderBlock::addOverflowFromBlockChildren() +{ + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (!child->isFloatingOrPositioned()) + addOverflowFromChild(child); + } +} + +void RenderBlock::addOverflowFromFloats() +{ + IntRect result; + if (!m_floatingObjects) + return; + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + if (r->m_isDescendant) + addOverflowFromChild(r->m_renderer, IntSize(r->left() + r->m_renderer->marginLeft(), r->top() + r->m_renderer->marginTop())); + } + return; +} + +void RenderBlock::addOverflowFromPositionedObjects() +{ + if (!m_positionedObjects) + return; + + RenderBox* positionedObject; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + positionedObject = *it; + + // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. + if (positionedObject->style()->position() != FixedPosition) + addOverflowFromChild(positionedObject); + } +} + +bool RenderBlock::expandsToEncloseOverhangingFloats() const +{ + return isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBox()) + || hasColumns() || isTableCell() || isFieldset() || isWritingModeRoot(); +} + +void RenderBlock::adjustPositionedBlock(RenderBox* child, const MarginInfo& marginInfo) +{ + if (child->style()->hasStaticX()) { + if (style()->isLeftToRightDirection()) + child->layer()->setStaticX(borderLeft() + paddingLeft()); + else + child->layer()->setStaticX(borderRight() + paddingRight()); + } + + if (child->style()->hasStaticY()) { + int y = height(); + if (!marginInfo.canCollapseWithMarginBefore()) { + child->computeBlockDirectionMargins(this); + int marginTop = child->marginTop(); + int collapsedTopPos = marginInfo.positiveMargin(); + int collapsedTopNeg = marginInfo.negativeMargin(); + if (marginTop > 0) { + if (marginTop > collapsedTopPos) + collapsedTopPos = marginTop; + } else { + if (-marginTop > collapsedTopNeg) + collapsedTopNeg = -marginTop; + } + y += (collapsedTopPos - collapsedTopNeg) - marginTop; + } + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != y) { + child->layer()->setStaticY(y); + child->setChildNeedsLayout(true, false); + } + } +} + +void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) +{ + // The float should be positioned taking into account the bottom margin + // of the previous flow. We add that margin into the height, get the + // float positioned properly, and then subtract the margin out of the + // height again. In the case of self-collapsing blocks, we always just + // use the top margins, since the self-collapsing block collapsed its + // own bottom margin into its top margin. + // + // Note also that the previous flow may collapse its margin into the top of + // our block. If this is the case, then we do not add the margin in to our + // height when computing the position of the float. This condition can be tested + // for by simply calling canCollapseWithMarginBefore. See + // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for + // an example of this scenario. + int marginOffset = marginInfo.canCollapseWithMarginBefore() ? 0 : marginInfo.margin(); + setLogicalHeight(logicalHeight() + marginOffset); + positionNewFloats(); + setLogicalHeight(logicalHeight() - marginOffset); +} + +bool RenderBlock::handleSpecialChild(RenderBox* child, const MarginInfo& marginInfo) +{ + // Handle in the given order + return handlePositionedChild(child, marginInfo) + || handleFloatingChild(child, marginInfo) + || handleRunInChild(child); +} + + +bool RenderBlock::handlePositionedChild(RenderBox* child, const MarginInfo& marginInfo) +{ + if (child->isPositioned()) { + child->containingBlock()->insertPositionedObject(child); + adjustPositionedBlock(child, marginInfo); + return true; + } + return false; +} + +bool RenderBlock::handleFloatingChild(RenderBox* child, const MarginInfo& marginInfo) +{ + if (child->isFloating()) { + insertFloatingObject(child); + adjustFloatingBlock(marginInfo); + return true; + } + return false; +} + +bool RenderBlock::handleRunInChild(RenderBox* child) +{ + // See if we have a run-in element with inline children. If the + // children aren't inline, then just treat the run-in as a normal + // block. + if (!child->isRunIn() || !child->childrenInline()) + return false; + // FIXME: We don't handle non-block elements with run-in for now. + if (!child->isRenderBlock()) + return false; + + // Get the next non-positioned/non-floating RenderBlock. + RenderBlock* blockRunIn = toRenderBlock(child); + RenderObject* curr = blockRunIn->nextSibling(); + while (curr && curr->isFloatingOrPositioned()) + curr = curr->nextSibling(); + + if (!curr || !curr->isRenderBlock() || !curr->childrenInline() || curr->isRunIn() || curr->isAnonymous()) + return false; + + RenderBlock* currBlock = toRenderBlock(curr); + + // Remove the old child. + children()->removeChildNode(this, blockRunIn); + + // Create an inline. + Node* runInNode = blockRunIn->node(); + RenderInline* inlineRunIn = new (renderArena()) RenderInline(runInNode ? runInNode : document()); + inlineRunIn->setStyle(blockRunIn->style()); + + bool runInIsGenerated = child->style()->styleType() == BEFORE || child->style()->styleType() == AFTER; + + // Move the nodes from the old child to the new child, but skip any :before/:after content. It has already + // been regenerated by the new inline. + for (RenderObject* runInChild = blockRunIn->firstChild(); runInChild;) { + RenderObject* nextSibling = runInChild->nextSibling(); + if (runInIsGenerated || (runInChild->style()->styleType() != BEFORE && runInChild->style()->styleType() != AFTER)) { + blockRunIn->children()->removeChildNode(blockRunIn, runInChild, false); + inlineRunIn->addChild(runInChild); // Use addChild instead of appendChildNode since it handles correct placement of the children relative to :after-generated content. + } + runInChild = nextSibling; + } + + // Now insert the new child under |currBlock|. + currBlock->children()->insertChildNode(currBlock, inlineRunIn, currBlock->firstChild()); + + // If the run-in had an element, we need to set the new renderer. + if (runInNode) + runInNode->setRenderer(inlineRunIn); + + // Destroy the block run-in, which includes deleting its line box tree. + blockRunIn->deleteLineBoxTree(); + blockRunIn->destroy(); + + // The block acts like an inline, so just null out its + // position. + + return true; +} + +int RenderBlock::collapseMargins(RenderBox* child, MarginInfo& marginInfo) +{ + // Get the four margin values for the child and cache them. + const MarginValues childMargins = marginValuesForChild(child); + + // Get our max pos and neg top margins. + int posTop = childMargins.positiveMarginBefore(); + int negTop = childMargins.negativeMarginBefore(); + + // For self-collapsing blocks, collapse our bottom margins into our + // top to get new posTop and negTop values. + if (child->isSelfCollapsingBlock()) { + posTop = max(posTop, childMargins.positiveMarginAfter()); + negTop = max(negTop, childMargins.negativeMarginAfter()); + } + + // See if the top margin is quirky. We only care if this child has + // margins that will collapse with us. + bool topQuirk = child->isMarginBeforeQuirk() || style()->marginBeforeCollapse() == MDISCARD; + + if (marginInfo.canCollapseWithMarginBefore()) { + // This child is collapsing with the top of the + // block. If it has larger margin values, then we need to update + // our own maximal values. + if (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !topQuirk) + setMaxMarginBeforeValues(max(posTop, maxPositiveMarginBefore()), max(negTop, maxNegativeMarginBefore())); + + // The minute any of the margins involved isn't a quirk, don't + // collapse it away, even if the margin is smaller (www.webreference.com + // has an example of this, a
with 0.8em author-specified inside + // a
inside a . + if (!marginInfo.determinedMarginBeforeQuirk() && !topQuirk && (posTop - negTop)) { + setMarginBeforeQuirk(false); + marginInfo.setDeterminedMarginBeforeQuirk(true); + } + + if (!marginInfo.determinedMarginBeforeQuirk() && topQuirk && !marginBefore()) + // We have no top margin and our top child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the

case. + // Don't do this for a block that split two inlines though. You do + // still apply margins in this case. + setMarginBeforeQuirk(true); + } + + if (marginInfo.quirkContainer() && marginInfo.atBeforeSideOfBlock() && (posTop - negTop)) + marginInfo.setMarginBeforeQuirk(topQuirk); + + int beforeCollapseLogicalTop = logicalHeight(); + int logicalTop = beforeCollapseLogicalTop; + if (child->isSelfCollapsingBlock()) { + // This child has no height. We need to compute our + // position before we collapse the child's margins together, + // so that we can get an accurate position for the zero-height block. + int collapsedBeforePos = max(marginInfo.positiveMargin(), childMargins.positiveMarginBefore()); + int collapsedBeforeNeg = max(marginInfo.negativeMargin(), childMargins.negativeMarginBefore()); + marginInfo.setMargin(collapsedBeforePos, collapsedBeforeNeg); + + // Now collapse the child's margins together, which means examining our + // bottom margin values as well. + marginInfo.setPositiveMarginIfLarger(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMarginIfLarger(childMargins.negativeMarginAfter()); + + if (!marginInfo.canCollapseWithMarginBefore()) + // We need to make sure that the position of the self-collapsing block + // is correct, since it could have overflowing content + // that needs to be positioned correctly (e.g., a block that + // had a specified height of 0 but that actually had subcontent). + logicalTop = logicalHeight() + collapsedBeforePos - collapsedBeforeNeg; + } + else { + if (child->style()->marginBeforeCollapse() == MSEPARATE) { + setLogicalHeight(logicalHeight() + marginInfo.margin() + marginBeforeForChild(child)); + logicalTop = logicalHeight(); + } + else if (!marginInfo.atBeforeSideOfBlock() || + (!marginInfo.canCollapseMarginBeforeWithChildren() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginBeforeQuirk()))) { + // We're collapsing with a previous sibling's margins and not + // with the top of the block. + setLogicalHeight(logicalHeight() + max(marginInfo.positiveMargin(), posTop) - max(marginInfo.negativeMargin(), negTop)); + logicalTop = logicalHeight(); + } + + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + + if (marginInfo.margin()) + marginInfo.setMarginAfterQuirk(child->isMarginAfterQuirk() || style()->marginAfterCollapse() == MDISCARD); + } + + // If margins would pull us past the top of the next page, then we need to pull back and pretend like the margins + // collapsed into the page edge. + bool paginated = view()->layoutState()->isPaginated(); + if (paginated && logicalTop > beforeCollapseLogicalTop) { + int oldLogicalTop = logicalTop; + logicalTop = min(logicalTop, nextPageTop(beforeCollapseLogicalTop)); + setLogicalHeight(logicalHeight() + (logicalTop - oldLogicalTop)); + } + return logicalTop; +} + +int RenderBlock::clearFloatsIfNeeded(RenderBox* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos) +{ + int heightIncrease = getClearDelta(child, yPos); + if (!heightIncrease) + return yPos; + + if (child->isSelfCollapsingBlock()) { + // For self-collapsing blocks that clear, they can still collapse their + // margins with following siblings. Reset the current margins to represent + // the self-collapsing block's margins only. + // CSS2.1 states: + // "An element that has had clearance applied to it never collapses its top margin with its parent block's bottom margin. + // Therefore if we are at the bottom of the block, let's go ahead and reset margins to only include the + // self-collapsing block's bottom margin. + bool atBottomOfBlock = true; + for (RenderBox* curr = child->nextSiblingBox(); curr && atBottomOfBlock; curr = curr->nextSiblingBox()) { + if (!curr->isFloatingOrPositioned()) + atBottomOfBlock = false; + } + + MarginValues childMargins = marginValuesForChild(child); + if (atBottomOfBlock) { + marginInfo.setPositiveMargin(childMargins.positiveMarginAfter()); + marginInfo.setNegativeMargin(childMargins.negativeMarginAfter()); + } else { + marginInfo.setPositiveMargin(max(childMargins.positiveMarginBefore(), childMargins.positiveMarginAfter())); + marginInfo.setNegativeMargin(max(childMargins.negativeMarginBefore(), childMargins.negativeMarginAfter())); + } + + // Adjust our height such that we are ready to be collapsed with subsequent siblings (or the bottom + // of the parent block). + setLogicalHeight(child->y() - max(0, marginInfo.margin())); + } else + // Increase our height by the amount we had to clear. + setLogicalHeight(height() + heightIncrease); + + if (marginInfo.canCollapseWithMarginBefore()) { + // We can no longer collapse with the top of the block since a clear + // occurred. The empty blocks collapse into the cleared block. + // FIXME: This isn't quite correct. Need clarification for what to do + // if the height the cleared block is offset by is smaller than the + // margins involved. + setMaxMarginBeforeValues(oldTopPosMargin, oldTopNegMargin); + marginInfo.setAtBeforeSideOfBlock(false); + } + + return yPos + heightIncrease; +} + +int RenderBlock::estimateLogicalTopPosition(RenderBox* child, const MarginInfo& marginInfo) +{ + // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological + // relayout if there are intruding floats. + int logicalTopEstimate = logicalHeight(); + if (!marginInfo.canCollapseWithMarginBefore()) { + int childMarginBefore = child->selfNeedsLayout() ? marginBeforeForChild(child) : collapsedMarginBeforeForChild(child); + logicalTopEstimate += max(marginInfo.margin(), childMarginBefore); + } + + bool paginated = view()->layoutState()->isPaginated(); + + // Adjust logicalTopEstimate down to the next page if the margins are so large that we don't fit on the current + // page. + if (paginated && logicalTopEstimate > logicalHeight()) + logicalTopEstimate = min(logicalTopEstimate, nextPageTop(logicalHeight())); + + logicalTopEstimate += getClearDelta(child, logicalTopEstimate); + + if (paginated) { + // If the object has a page or column break value of "before", then we should shift to the top of the next page. + logicalTopEstimate = applyBeforeBreak(child, logicalTopEstimate); + + // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. + logicalTopEstimate = adjustForUnsplittableChild(child, logicalTopEstimate); + + if (!child->selfNeedsLayout() && child->isRenderBlock()) + logicalTopEstimate += toRenderBlock(child)->paginationStrut(); + } + + return logicalTopEstimate; +} + +void RenderBlock::determineLogicalLeftPositionForChild(RenderBox* child) +{ + int startPosition = borderStart() + paddingStart(); + int totalAvailableLogicalWidth = borderAndPaddingLogicalWidth() + availableLogicalWidth(); + + // Add in our start margin. + int childMarginStart = marginStartForChild(child); + int newPosition = startPosition + childMarginStart; + + // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need + // to shift over as necessary to dodge any floats that might get in the way. + if (child->avoidsFloats()) { + int startOff = style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(logicalHeight(), false) : totalAvailableLogicalWidth - logicalRightOffsetForLine(logicalHeight(), false); + if (style()->textAlign() != WEBKIT_CENTER && !child->style()->marginStartUsing(style()).isAuto()) { + if (childMarginStart < 0) + startOff += childMarginStart; + newPosition = max(newPosition, startOff); // Let the float sit in the child's margin if it can fit. + } else if (startOff != startPosition) { + // The object is shifting to the "end" side of the block. The object might be centered, so we need to + // recalculate our inline direction margins. Note that the containing block content + // width computation will take into account the delta between |startOff| and |startPosition| + // so that we can just pass the content width in directly to the |computeMarginsInContainingBlockInlineDirection| + // function. + child->computeInlineDirectionMargins(this, availableLogicalWidthForLine(logicalTopForChild(child), false), logicalWidthForChild(child)); + newPosition = startOff + marginStartForChild(child); + } + } + + setLogicalLeftForChild(child, style()->isLeftToRightDirection() ? newPosition : totalAvailableLogicalWidth - newPosition - logicalWidthForChild(child), ApplyLayoutDelta); +} + +void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) +{ + if (marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore()) { + // Update our max pos/neg bottom margins, since we collapsed our bottom margins + // with our children. + setMaxMarginAfterValues(max(maxPositiveMarginAfter(), marginInfo.positiveMargin()), max(maxNegativeMarginAfter(), marginInfo.negativeMargin())); + + if (!marginInfo.marginAfterQuirk()) + setMarginAfterQuirk(false); + + if (marginInfo.marginAfterQuirk() && marginAfter() == 0) + // We have no bottom margin and our last child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the

case. + setMarginAfterQuirk(true); + } +} + +void RenderBlock::handleAfterSideOfBlock(int beforeSide, int afterSide, MarginInfo& marginInfo) +{ + marginInfo.setAtAfterSideOfBlock(true); + + // If we can't collapse with children then go ahead and add in the bottom margin. + if (!marginInfo.canCollapseWithMarginAfter() && !marginInfo.canCollapseWithMarginBefore() + && (!document()->inQuirksMode() || !marginInfo.quirkContainer() || !marginInfo.marginAfterQuirk())) + setLogicalHeight(logicalHeight() + marginInfo.margin()); + + // Now add in our bottom border/padding. + setLogicalHeight(logicalHeight() + afterSide); + + // Negative margins can cause our height to shrink below our minimal height (border/padding). + // If this happens, ensure that the computed height is increased to the minimal height. + setLogicalHeight(max(logicalHeight(), beforeSide + afterSide)); + + // Update our bottom collapsed margin info. + setCollapsedBottomMargin(marginInfo); +} + +void RenderBlock::setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode applyDelta) +{ + if (style()->isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(child->x() - logicalLeft, 0)); + child->setX(logicalLeft); + } else { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(0, child->y() - logicalLeft)); + child->setY(logicalLeft); + } +} + +void RenderBlock::setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode applyDelta) +{ + if (style()->isHorizontalWritingMode()) { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(0, child->y() - logicalTop)); + child->setY(logicalTop); + } else { + if (applyDelta == ApplyLayoutDelta) + view()->addLayoutDelta(IntSize(child->x() - logicalTop, 0)); + child->setX(logicalTop); + } +} + +void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatLogicalBottom) +{ + if (gPercentHeightDescendantsMap) { + if (HashSet* descendants = gPercentHeightDescendantsMap->get(this)) { + HashSet::iterator end = descendants->end(); + for (HashSet::iterator it = descendants->begin(); it != end; ++it) { + RenderBox* box = *it; + while (box != this) { + if (box->normalChildNeedsLayout()) + break; + box->setChildNeedsLayout(true, false); + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; + } + } + } + } + + int beforeEdge = borderBefore() + paddingBefore(); + int afterEdge = borderAfter() + paddingAfter() + scrollbarLogicalHeight(); + + setLogicalHeight(beforeEdge); + + // The margin struct caches all our current margin collapsing state. The compact struct caches state when we encounter compacts, + MarginInfo marginInfo(this, beforeEdge, afterEdge); + + // Fieldsets need to find their legend and position it inside the border of the object. + // The legend then gets skipped during normal layout. The same is true for ruby text. + // It doesn't get included in the normal layout process but is instead skipped. + RenderObject* childToExclude = layoutSpecialExcludedChild(relayoutChildren); + + int previousFloatLogicalBottom = 0; + maxFloatLogicalBottom = 0; + + RenderBox* next = firstChildBox(); + + while (next) { + RenderBox* child = next; + next = child->nextSiblingBox(); + + if (childToExclude == child) + continue; // Skip this child, since it will be positioned by the specialized subclass (fieldsets and ruby runs). + + // Make sure we layout children if they need it. + // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into + // an auto value. Add a method to determine this, so that we can avoid the relayout. + if (relayoutChildren || ((child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent()) && !isRenderView())) + child->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and the child has percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (child->style()->paddingStart().isPercent() || child->style()->paddingEnd().isPercent())) + child->setPreferredLogicalWidthsDirty(true, false); + + // Handle the four types of special elements first. These include positioned content, floating content, compacts and + // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks. + if (handleSpecialChild(child, marginInfo)) + continue; + + // Lay out the child. + layoutBlockChild(child, marginInfo, previousFloatLogicalBottom, maxFloatLogicalBottom); + } + + // Now do the handling of the bottom of the block, adding in our bottom border/padding and + // determining the correct collapsed bottom margin information. + handleAfterSideOfBlock(beforeEdge, afterEdge, marginInfo); +} + +void RenderBlock::layoutBlockChild(RenderBox* child, MarginInfo& marginInfo, int& previousFloatLogicalBottom, int& maxFloatLogicalBottom) +{ + int oldPosMarginBefore = maxPositiveMarginBefore(); + int oldNegMarginBefore = maxNegativeMarginBefore(); + + // The child is a normal flow object. Compute the margins we will use for collapsing now. + child->computeBlockDirectionMargins(this); + + // Do not allow a collapse if the margin-before-collapse style is set to SEPARATE. + if (child->style()->marginBeforeCollapse() == MSEPARATE) { + marginInfo.setAtBeforeSideOfBlock(false); + marginInfo.clearMargin(); + } + + // Try to guess our correct logical top position. In most cases this guess will + // be correct. Only if we're wrong (when we compute the real logical top position) + // will we have to potentially relayout. + int logicalTopEstimate = estimateLogicalTopPosition(child, marginInfo); + + // Cache our old rect so that we can dirty the proper repaint rects if the child moves. + IntRect oldRect(child->x(), child->y() , child->width(), child->height()); + int oldLogicalTop = logicalTopForChild(child); + +#ifndef NDEBUG + IntSize oldLayoutDelta = view()->layoutDelta(); +#endif + // Go ahead and position the child as though it didn't collapse with the top. + setLogicalTopForChild(child, logicalTopEstimate, ApplyLayoutDelta); + + RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; + bool markDescendantsWithFloats = false; + if (logicalTopEstimate != oldLogicalTop && !child->avoidsFloats() && childRenderBlock && childRenderBlock->containsFloats()) + markDescendantsWithFloats = true; + else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { + // If an element might be affected by the presence of floats, then always mark it for + // layout. + int fb = max(previousFloatLogicalBottom, lowestFloatLogicalBottom()); + if (fb > logicalTopEstimate) + markDescendantsWithFloats = true; + } + + if (childRenderBlock) { + if (markDescendantsWithFloats) + childRenderBlock->markAllDescendantsWithFloatsForLayout(); + if (!child->isWritingModeRoot()) + previousFloatLogicalBottom = max(previousFloatLogicalBottom, oldLogicalTop + childRenderBlock->lowestFloatLogicalBottom()); + } + + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); + + bool childHadLayout = child->m_everHadLayout; + bool childNeededLayout = child->needsLayout(); + if (childNeededLayout) + child->layout(); + + // Cache if we are at the top of the block right now. + bool atBeforeSideOfBlock = marginInfo.atBeforeSideOfBlock(); + + // Now determine the correct ypos based off examination of collapsing margin + // values. + int logicalTopBeforeClear = collapseMargins(child, marginInfo); + + // Now check for clear. + int logicalTopAfterClear = clearFloatsIfNeeded(child, marginInfo, oldPosMarginBefore, oldNegMarginBefore, logicalTopBeforeClear); + + bool paginated = view()->layoutState()->isPaginated(); + if (paginated) { + int oldTop = logicalTopAfterClear; + + // If the object has a page or column break value of "before", then we should shift to the top of the next page. + logicalTopAfterClear = applyBeforeBreak(child, logicalTopAfterClear); + + // For replaced elements and scrolled elements, we want to shift them to the next page if they don't fit on the current one. + int logicalTopBeforeUnsplittableAdjustment = logicalTopAfterClear; + int logicalTopAfterUnsplittableAdjustment = adjustForUnsplittableChild(child, logicalTopAfterClear); + + int paginationStrut = 0; + int unsplittableAdjustmentDelta = logicalTopAfterUnsplittableAdjustment - logicalTopBeforeUnsplittableAdjustment; + if (unsplittableAdjustmentDelta) + paginationStrut = unsplittableAdjustmentDelta; + else if (childRenderBlock && childRenderBlock->paginationStrut()) + paginationStrut = childRenderBlock->paginationStrut(); + + if (paginationStrut) { + // We are willing to propagate out to our parent block as long as we were at the top of the block prior + // to collapsing our margins, and as long as we didn't clear or move as a result of other pagination. + if (atBeforeSideOfBlock && oldTop == logicalTopBeforeClear && !isPositioned() && !isTableCell()) { + // FIXME: Should really check if we're exceeding the page height before propagating the strut, but we don't + // have all the information to do so (the strut only has the remaining amount to push). Gecko gets this wrong too + // and pushes to the next page anyway, so not too concerned about it. + setPaginationStrut(logicalTopAfterClear + paginationStrut); + if (childRenderBlock) + childRenderBlock->setPaginationStrut(0); + } else + logicalTopAfterClear += paginationStrut; + } + + // Similar to how we apply clearance. Go ahead and boost height() to be the place where we're going to position the child. + setLogicalHeight(logicalHeight() + (logicalTopAfterClear - oldTop)); + } + + setLogicalTopForChild(child, logicalTopAfterClear, ApplyLayoutDelta); + + // Now we have a final top position. See if it really does end up being different from our estimate. + if (logicalTopAfterClear != logicalTopEstimate) { + if (child->shrinkToAvoidFloats()) { + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. + child->setChildNeedsLayout(true, false); + } + if (childRenderBlock) { + if (!child->avoidsFloats() && childRenderBlock->containsFloats()) + childRenderBlock->markAllDescendantsWithFloatsForLayout(); + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); + } + + // Our guess was wrong. Make the child lay itself out again. + child->layoutIfNeeded(); + } + + // We are no longer at the top of the block if we encounter a non-empty child. + // This has to be done after checking for clear, so that margins can be reset if a clear occurred. + if (marginInfo.atBeforeSideOfBlock() && !child->isSelfCollapsingBlock()) + marginInfo.setAtBeforeSideOfBlock(false); + + // Now place the child in the correct left position + determineLogicalLeftPositionForChild(child); + + // Update our height now that the child has been placed in the correct position. + setLogicalHeight(logicalHeight() + logicalHeightForChild(child)); + if (child->style()->marginAfterCollapse() == MSEPARATE) { + setLogicalHeight(logicalHeight() + marginAfterForChild(child)); + marginInfo.clearMargin(); + } + // If the child has overhanging floats that intrude into following siblings (or possibly out + // of this block), then the parent gets notified of the floats now. + if (childRenderBlock && childRenderBlock->containsFloats()) + maxFloatLogicalBottom = max(maxFloatLogicalBottom, addOverhangingFloats(toRenderBlock(child), -child->logicalLeft(), -child->logicalTop(), !childNeededLayout)); + + IntSize childOffset(child->x() - oldRect.x(), child->y() - oldRect.y()); + if (childOffset.width() || childOffset.height()) { + view()->addLayoutDelta(childOffset); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (childHadLayout && !selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); + } + + if (!childHadLayout && child->checkForRepaintDuringLayout()) { + child->repaint(); + child->repaintOverhangingFloats(true); + } + + if (paginated) { + // Check for an after page/column break. + int newHeight = applyAfterBreak(child, height(), marginInfo); + if (newHeight != height()) + setLogicalHeight(newHeight); + } + + ASSERT(oldLayoutDelta == view()->layoutDelta()); +} + +bool RenderBlock::layoutOnlyPositionedObjects() +{ + if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) + return false; + + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasColumns() || hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); + + if (needsPositionedMovementLayout()) { + tryLayoutDoingPositionedMovementOnly(); + if (needsLayout()) + return false; + } + + // All we have to is lay out our positioned objects. + layoutPositionedObjects(false); + + // Recompute our overflow information. + // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only + // updating our overflow if we either used to have overflow or if the new temporary object has overflow. + // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and + // lowestPosition on every relayout so it's not a regression. + m_overflow.clear(); + computeOverflow(clientLogicalBottom(), true); + + statePusher.pop(); + + updateLayerTransform(); + + updateScrollInfoAfterLayout(); + +#ifdef ANDROID_FIX + // iframe flatten will call FrameView::layout() which calls performPostLayoutTasks, + // which may make us need to layout again + if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) + return false; +#endif + + setNeedsLayout(false); + return true; +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren) +{ + if (!m_positionedObjects) + return; + + if (hasColumns()) + view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns. + + RenderBox* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the + // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned + // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is + // positioned explicitly) this should not incur a performance penalty. + if (relayoutChildren || (r->style()->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) + r->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (r->style()->paddingStart().isPercent() || r->style()->paddingEnd().isPercent())) + r->setPreferredLogicalWidthsDirty(true, false); + + if (!r->needsLayout()) + r->markForPaginationRelayoutIfNeeded(); + + // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width + // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout. + if (r->needsPositionedMovementLayoutOnly()) + r->tryLayoutDoingPositionedMovementOnly(); + r->layoutIfNeeded(); + } + + if (hasColumns()) + view()->layoutState()->m_columnInfo = columnInfo(); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work. +} + +void RenderBlock::markPositionedObjectsForLayout() +{ + if (m_positionedObjects) { + RenderBox* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + r->setChildNeedsLayout(true); + } + } +} + +void RenderBlock::markForPaginationRelayoutIfNeeded() +{ + ASSERT(!needsLayout()); + if (needsLayout()) + return; + + if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(y()) != pageLogicalOffset())) + setChildNeedsLayout(true, false); +} + +void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) +{ + // Repaint any overhanging floats (if we know we're the one to paint them). + if (hasOverhangingFloats()) { + // We think that we must be in a bad state if m_floatingObjects is nil at this point, so + // we assert on Debug builds and nil-check Release builds. + ASSERT(m_floatingObjects); + if (!m_floatingObjects) + return; + + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + + // FIXME: Avoid disabling LayoutState. At the very least, don't disable it for floats originating + // in this block. Better yet would be to push extra state for the containers of other floats. + view()->disableLayoutState(); + for ( ; (r = it.current()); ++it) { + // Only repaint the object if it is overhanging, is not in its own layer, and + // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter + // condition is replaced with being a descendant of us. + if (logicalBottomForFloat(r) > logicalHeight() && ((paintAllDescendants && r->m_renderer->isDescendantOf(this)) || r->m_shouldPaint) && !r->m_renderer->hasSelfPaintingLayer()) { + r->m_renderer->repaint(); + r->m_renderer->repaintOverhangingFloats(); + } + } + view()->enableLayoutState(); + } +} + +void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) +{ + tx += x(); + ty += y(); + + PaintPhase phase = paintInfo.phase; + + // Check if we need to do anything at all. + // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // paints the root's background. + if (!isRoot()) { + IntRect overflowBox = visualOverflowRect(); + overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); + overflowBox.move(tx, ty); + if (!overflowBox.intersects(paintInfo.rect)) + return; + } + + bool pushedClip = pushContentsClip(paintInfo, tx, ty); + paintObject(paintInfo, tx, ty); + if (pushedClip) + popContentsClip(paintInfo, phase, tx, ty); + + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this)) + layer()->paintOverflowControls(paintInfo.context, tx, ty, paintInfo.rect); +} + +void RenderBlock::paintColumnRules(PaintInfo& paintInfo, int tx, int ty) +{ + const Color& ruleColor = style()->visitedDependentColor(CSSPropertyWebkitColumnRuleColor); + bool ruleTransparent = style()->columnRuleIsTransparent(); + EBorderStyle ruleStyle = style()->columnRuleStyle(); + int ruleWidth = style()->columnRuleWidth(); + int colGap = columnGap(); + bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; + if (!renderRule) + return; + + // We need to do multiple passes, breaking up our child painting into strips. + ColumnInfo* colInfo = columnInfo(); + unsigned colCount = columnCount(colInfo); + int currXOffset = style()->isLeftToRightDirection() ? 0 : contentWidth(); + int ruleAdd = borderLeft() + paddingLeft(); + int ruleX = style()->isLeftToRightDirection() ? 0 : contentWidth(); + for (unsigned i = 0; i < colCount; i++) { + IntRect colRect = columnRectAt(colInfo, i); + + // Move to the next position. + if (style()->isLeftToRightDirection()) { + ruleX += colRect.width() + colGap / 2; + currXOffset += colRect.width() + colGap; + } else { + ruleX -= (colRect.width() + colGap / 2); + currXOffset -= (colRect.width() + colGap); + } + + // Now paint the column rule. + if (i < colCount - 1) { + int ruleStart = tx + ruleX - ruleWidth / 2 + ruleAdd; + int ruleEnd = ruleStart + ruleWidth; + int ruleTop = ty + borderTop() + paddingTop(); + int ruleBottom = ruleTop + contentHeight(); + drawLineForBoxSide(paintInfo.context, ruleStart, ruleTop, ruleEnd, ruleBottom, + style()->isLeftToRightDirection() ? BSLeft : BSRight, ruleColor, ruleStyle, 0, 0); + } + + ruleX = currXOffset; + } +} + +void RenderBlock::paintColumnContents(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) +{ + // We need to do multiple passes, breaking up our child painting into strips. + GraphicsContext* context = paintInfo.context; + int colGap = columnGap(); + ColumnInfo* colInfo = columnInfo(); + unsigned colCount = columnCount(colInfo); + if (!colCount) + return; + int currXOffset = style()->isLeftToRightDirection() ? 0 : contentWidth() - columnRectAt(colInfo, 0).width(); + int currYOffset = 0; + for (unsigned i = 0; i < colCount; i++) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = columnRectAt(colInfo, i); + colRect.move(tx, ty); + PaintInfo info(paintInfo); + info.rect.intersect(colRect); + + if (!info.rect.isEmpty()) { + context->save(); + + // Each strip pushes a clip, since column boxes are specified as being + // like overflow:hidden. + context->clip(colRect); + + // Adjust our x and y when painting. + int finalX = tx + currXOffset; + int finalY = ty + currYOffset; + if (paintingFloats) + paintFloats(info, finalX, finalY, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); + else + paintContents(info, finalX, finalY); + + context->restore(); + } + + // Move to the next position. + if (style()->isLeftToRightDirection()) + currXOffset += colRect.width() + colGap; + else + currXOffset -= (colRect.width() + colGap); + + currYOffset -= colRect.height(); + } +} + +void RenderBlock::paintContents(PaintInfo& paintInfo, int tx, int ty) +{ + // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. + // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document + // will do a full repaint(). + if (document()->mayCauseFlashOfUnstyledContent() && !isRenderView()) + return; + + if (childrenInline()) + m_lineBoxes.paint(this, paintInfo, tx, ty); + else + paintChildren(paintInfo, tx, ty); +} + +void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty) +{ + PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; + newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; + + // We don't paint our own background, but we do let the kids paint their backgrounds. + PaintInfo info(paintInfo); + info.phase = newPhase; + info.updatePaintingRootForChildren(this); + + // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit + // NSViews. Do not add any more code for this. + RenderView* renderView = view(); + bool usePrintRect = !renderView->printRect().isEmpty(); + + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // Check for page-break-before: always, and if it's set, break and bail. + bool checkBeforeAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakBefore() == PBALWAYS); + if (checkBeforeAlways + && (ty + child->y()) > paintInfo.rect.y() + && (ty + child->y()) < paintInfo.rect.bottom()) { + view()->setBestTruncatedAt(ty + child->y(), this, true); + return; + } + + if (!child->isFloating() && child->isReplaced() && usePrintRect && child->height() <= renderView->printRect().height()) { + // Paginate block-level replaced elements. + if (ty + child->y() + child->height() > renderView->printRect().bottom()) { + if (ty + child->y() < renderView->truncatedAt()) + renderView->setBestTruncatedAt(ty + child->y(), child); + // If we were able to truncate, don't paint. + if (ty + child->y() >= renderView->truncatedAt()) + break; + } + } + + IntPoint childPoint = flipForWritingMode(child, IntPoint(tx, ty), ParentToChildFlippingAdjustment); + if (!child->hasSelfPaintingLayer() && !child->isFloating()) + child->paint(info, childPoint.x(), childPoint.y()); + + // Check for page-break-after: always, and if it's set, break and bail. + bool checkAfterAlways = !childrenInline() && (usePrintRect && child->style()->pageBreakAfter() == PBALWAYS); + if (checkAfterAlways + && (ty + child->y() + child->height()) > paintInfo.rect.y() + && (ty + child->y() + child->height()) < paintInfo.rect.bottom()) { + view()->setBestTruncatedAt(ty + child->y() + child->height() + max(0, child->collapsedMarginAfter()), this, true); + return; + } + } +} + +void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType type) +{ + SelectionController* selection = type == CursorCaret ? frame()->selection() : frame()->page()->dragCaretController(); + + // Paint the caret if the SelectionController says so or if caret browsing is enabled + bool caretBrowsing = frame()->settings() && frame()->settings()->caretBrowsingEnabled(); + RenderObject* caretPainter = selection->caretRenderer(); + if (caretPainter == this && (selection->isContentEditable() || caretBrowsing)) { + // Convert the painting offset into the local coordinate system of this renderer, + // to match the localCaretRect computed by the SelectionController + offsetForContents(tx, ty); + + if (type == CursorCaret) + frame()->selection()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); + else + frame()->selection()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect); + } +} + +void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + PaintPhase paintPhase = paintInfo.phase; + + // 1. paint background, borders etc + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { + if (hasBoxDecorations()) + paintBoxDecorations(paintInfo, tx, ty); + if (hasColumns()) + paintColumnRules(paintInfo, tx, ty); + } + + if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) { + paintMask(paintInfo, tx, ty); + return; + } + + // We're done. We don't bother painting any children. + if (paintPhase == PaintPhaseBlockBackground) + return; + + // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). + int scrolledX = tx; + int scrolledY = ty; + if (hasOverflowClip()) { + IntSize offset = layer()->scrolledContentOffset(); + scrolledX -= offset.width(); + scrolledY -= offset.height(); + } + + // 2. paint contents + if (paintPhase != PaintPhaseSelfOutline) { + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY); + else + paintContents(paintInfo, scrolledX, scrolledY); + } + + // 3. paint selection + // FIXME: Make this work with multi column layouts. For now don't fill gaps. + bool isPrinting = document()->printing(); + if (!isPrinting && !hasColumns()) + paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks. + + // 4. paint floats. + if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { + if (hasColumns()) + paintColumnContents(paintInfo, scrolledX, scrolledY, true); + else + paintFloats(paintInfo, scrolledX, scrolledY, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); + } + + // 5. paint outline. + if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) + paintOutline(paintInfo.context, tx, ty, width(), height()); + + // 6. paint continuation outlines. + if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { + RenderInline* inlineCont = inlineElementContinuation(); + if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { + RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer()); + RenderBlock* cb = containingBlock(); + + bool inlineEnclosedInSelfPaintingLayer = false; + for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) { + if (box->hasSelfPaintingLayer()) { + inlineEnclosedInSelfPaintingLayer = true; + break; + } + } + + if (!inlineEnclosedInSelfPaintingLayer) + cb->addContinuationWithOutline(inlineRenderer); + else if (!inlineRenderer->firstLineBox()) + inlineRenderer->paintOutline(paintInfo.context, tx - x() + inlineRenderer->containingBlock()->x(), + ty - y() + inlineRenderer->containingBlock()->y()); + } + paintContinuationOutlines(paintInfo, tx, ty); + } + + // 7. paint caret. + // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, + // then paint the caret. + if (paintPhase == PaintPhaseForeground) { + paintCaret(paintInfo, scrolledX, scrolledY, CursorCaret); + paintCaret(paintInfo, scrolledX, scrolledY, DragCaret); + } +} + +void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preservePhase) +{ + if (!m_floatingObjects) + return; + + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + // Only paint the object if our m_shouldPaint flag is set. + if (r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer()) { + PaintInfo currentPaintInfo(paintInfo); + currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; + IntPoint childPoint = flipForWritingMode(r->m_renderer, IntPoint(tx + r->left() + r->m_renderer->marginLeft() - r->m_renderer->x(), ty + r->top() + r->m_renderer->marginTop() - r->m_renderer->y()), ParentToChildFlippingAdjustment); + r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y()); + if (!preservePhase) { + currentPaintInfo.phase = PaintPhaseChildBlockBackgrounds; + r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y()); + currentPaintInfo.phase = PaintPhaseFloat; + r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y()); + currentPaintInfo.phase = PaintPhaseForeground; + r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y()); + currentPaintInfo.phase = PaintPhaseOutline; + r->m_renderer->paint(currentPaintInfo, childPoint.x(), childPoint.y()); + } + } + } +} + +void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) +{ + if (!paintInfo.shouldPaintWithinRoot(this) || !firstLineBox()) + return; + + if (style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) { + // We can check the first box and last box and avoid painting if we don't + // intersect. + int yPos = ty + firstLineBox()->y(); + int h = lastLineBox()->y() + lastLineBox()->logicalHeight() - firstLineBox()->y(); + if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) + return; + + // See if our boxes intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + yPos = ty + curr->y(); + h = curr->logicalHeight(); + if (curr->ellipsisBox() && yPos < paintInfo.rect.bottom() && yPos + h > paintInfo.rect.y()) + curr->paintEllipsisBox(paintInfo, tx, ty); + } + } +} + +RenderInline* RenderBlock::inlineElementContinuation() const +{ + RenderBoxModelObject* continuation = this->continuation(); + return continuation && continuation->isInline() ? toRenderInline(continuation) : 0; +} + +RenderBlock* RenderBlock::blockElementContinuation() const +{ + RenderBoxModelObject* currentContinuation = continuation(); + if (!currentContinuation || currentContinuation->isInline()) + return 0; + RenderBlock* nextContinuation = toRenderBlock(currentContinuation); + if (nextContinuation->isAnonymousBlock()) + return nextContinuation->blockElementContinuation(); + return nextContinuation; +} + +static ContinuationOutlineTableMap* continuationOutlineTable() +{ + DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ()); + return &table; +} + +void RenderBlock::addContinuationWithOutline(RenderInline* flow) +{ + // We can't make this work if the inline is in a layer. We'll just rely on the broken + // way of painting. + ASSERT(!flow->layer() && !flow->isInlineElementContinuation()); + + ContinuationOutlineTableMap* table = continuationOutlineTable(); + ListHashSet* continuations = table->get(this); + if (!continuations) { + continuations = new ListHashSet; + table->set(this, continuations); + } + + continuations->add(flow); +} + +void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty) +{ + ContinuationOutlineTableMap* table = continuationOutlineTable(); + if (table->isEmpty()) + return; + + ListHashSet* continuations = table->get(this); + if (!continuations) + return; + + // Paint each continuation outline. + ListHashSet::iterator end = continuations->end(); + for (ListHashSet::iterator it = continuations->begin(); it != end; ++it) { + // Need to add in the coordinates of the intervening blocks. + RenderInline* flow = *it; + RenderBlock* block = flow->containingBlock(); + for ( ; block && block != this; block = block->containingBlock()) { + tx += block->x(); + ty += block->y(); + } + ASSERT(block); + flow->paintOutline(info.context, tx, ty); + } + + // Delete + delete continuations; + table->remove(this); +} + +bool RenderBlock::shouldPaintSelectionGaps() const +{ + return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); +} + +bool RenderBlock::isSelectionRoot() const +{ + if (!node()) + return false; + + // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. + if (isTable()) + return false; + + if (isBody() || isRoot() || hasOverflowClip() || isRelPositioned() || + isFloatingOrPositioned() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform() || + hasReflection() || hasMask() || isWritingModeRoot()) + return true; + + if (view() && view()->selectionStart()) { + Node* startElement = view()->selectionStart()->node(); + if (startElement && startElement->rootEditableElement() == node()) + return true; + } + + return false; +} + +GapRects RenderBlock::selectionGapRectsForRepaint(RenderBoxModelObject* repaintContainer) +{ + ASSERT(!needsLayout()); + + if (!shouldPaintSelectionGaps()) + return GapRects(); + + // FIXME: this is broken with transforms + TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); + mapLocalToContainer(repaintContainer, false, false, transformState); + IntPoint offsetFromRepaintContainer = roundedIntPoint(transformState.mappedPoint()); + + if (hasOverflowClip()) + offsetFromRepaintContainer -= layer()->scrolledContentOffset(); + + int lastTop = 0; + int lastLeft = logicalLeftSelectionOffset(this, lastTop); + int lastRight = logicalRightSelectionOffset(this, lastTop); + + return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight); +} + +void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty) +{ + if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { + int lastTop = 0; + int lastLeft = logicalLeftSelectionOffset(this, lastTop); + int lastRight = logicalRightSelectionOffset(this, lastTop); + paintInfo.context->save(); + IntRect gapRectsBounds = selectionGaps(this, IntPoint(tx, ty), IntSize(), lastTop, lastLeft, lastRight, &paintInfo); + if (!gapRectsBounds.isEmpty()) { + if (RenderLayer* layer = enclosingLayer()) { + gapRectsBounds.move(IntSize(-tx, -ty)); + if (!hasLayer()) { + IntRect localBounds(gapRectsBounds); + flipForWritingMode(localBounds); + gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox(); + gapRectsBounds.move(layer->scrolledContentOffset()); + } + layer->addBlockSelectionGapsBounds(gapRectsBounds); + } + } + paintInfo.context->restore(); + } +} + +static void clipOutPositionedObjects(const PaintInfo* paintInfo, const IntPoint& offset, RenderBlock::PositionedObjectsListHashSet* positionedObjects) +{ + if (!positionedObjects) + return; + + RenderBlock::PositionedObjectsListHashSet::const_iterator end = positionedObjects->end(); + for (RenderBlock::PositionedObjectsListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { + RenderBox* r = *it; + paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); + } +} + +static int blockDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock) +{ + return rootBlock->style()->isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width(); +} + +static int inlineDirectionOffset(RenderBlock* rootBlock, const IntSize& offsetFromRootBlock) +{ + return rootBlock->style()->isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height(); +} + +IntRect RenderBlock::logicalRectToPhysicalRect(const IntPoint& rootBlockPhysicalPosition, const IntRect& logicalRect) +{ + IntRect result; + if (style()->isHorizontalWritingMode()) + result = logicalRect; + else + result = IntRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width()); + flipForWritingMode(result); + result.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y()); + return result; +} + +GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo) +{ + // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. + // Clip out floating and positioned objects when painting selection gaps. + if (paintInfo) { + // Note that we don't clip out overflow for positioned objects. We just stick to the border box. + IntRect flippedBlockRect = IntRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height()); + rootBlock->flipForWritingMode(flippedBlockRect); + flippedBlockRect.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y()); + clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), m_positionedObjects); + if (isBody() || isRoot()) // The must make sure to examine its containingBlock's positioned objects. + for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) + clipOutPositionedObjects(paintInfo, IntPoint(cb->x(), cb->y()), cb->m_positionedObjects); // FIXME: Not right for flipped writing modes. + if (m_floatingObjects) { + for (DeprecatedPtrListIterator it(*m_floatingObjects); it.current(); ++it) { + FloatingObject* r = it.current(); + IntRect floatBox = IntRect(offsetFromRootBlock.width() + r->left() + r->m_renderer->marginLeft(), + offsetFromRootBlock.height() + r->top() + r->m_renderer->marginTop(), + r->m_renderer->width(), r->m_renderer->height()); + rootBlock->flipForWritingMode(floatBox); + floatBox.move(rootBlockPhysicalPosition.x(), rootBlockPhysicalPosition.y()); + paintInfo->context->clipOut(floatBox); + } + } + } + + // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is + // fixed). + GapRects result; + if (!isBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. + return result; + + if (hasColumns() || hasTransform() || style()->columnSpan()) { + // FIXME: We should learn how to gap fill multiple columns and transforms eventually. + lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + return result; + } + + if (childrenInline()) + result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + else + result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); + + // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. + if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, + logicalHeight(), paintInfo)); + return result; +} + +GapRects RenderBlock::inlineSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo) +{ + GapRects result; + + bool containsStart = selectionState() == SelectionStart || selectionState() == SelectionBoth; + + if (!firstLineBox()) { + if (containsStart) { + // Go ahead and update our lastLogicalTop to be the bottom of the block.


s or empty blocks with height can trip this + // case. + lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); + } + return result; + } + + RootInlineBox* lastSelectedLine = 0; + RootInlineBox* curr; + for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox()) { } + + // Now paint the gaps for the lines. + for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) { + int selTop = curr->selectionTop(); + int selHeight = curr->selectionHeight(); + + if (!containsStart && !lastSelectedLine && + selectionState() != SelectionStart && selectionState() != SelectionBoth) + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, + selTop, paintInfo)); + + IntRect logicalRect(curr->logicalLeft(), selTop, curr->logicalWidth(), selTop + selHeight); + logicalRect.move(style()->isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width())); + IntRect physicalRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect); + if (!paintInfo || (style()->isHorizontalWritingMode() && physicalRect.y() < paintInfo->rect.bottom() && physicalRect.bottom() > paintInfo->rect.y()) + || (!style()->isHorizontalWritingMode() && physicalRect.x() < paintInfo->rect.right() && physicalRect.right() > paintInfo->rect.x())) + result.unite(curr->lineSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, selTop, selHeight, paintInfo)); + + lastSelectedLine = curr; + } + + if (containsStart && !lastSelectedLine) + // VisibleSelection must start just after our last line. + lastSelectedLine = lastRootBox(); + + if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { + // Go ahead and update our lastY to be the bottom of the last selected line. + lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + lastSelectedLine->selectionBottom(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, lastSelectedLine->selectionBottom()); + } + return result; +} + +GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* paintInfo) +{ + GapRects result; + + // Go ahead and jump right to the first block child that contains some selected objects. + RenderBox* curr; + for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } + + for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { + SelectionState childState = curr->selectionState(); + if (childState == SelectionBoth || childState == SelectionEnd) + sawSelectionEnd = true; + + if (curr->isFloatingOrPositioned()) + continue; // We must be a normal flow object in order to even be considered. + + if (curr->isRelPositioned() && curr->hasLayer()) { + // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element. + // Just disregard it completely. + IntSize relOffset = curr->layer()->relativePositionOffset(); + if (relOffset.width() || relOffset.height()) + continue; + } + + bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this. + bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); + if (fillBlockGaps) { + // We need to fill the vertical gap above this object. + if (childState == SelectionEnd || childState == SelectionInside) + // Fill the gap above the object. + result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, + curr->logicalTop(), paintInfo)); + + // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* + // our object. We know this if the selection did not end inside our object. + if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd)) + childState = SelectionNone; + + // Fill side gaps on this object based off its state. + bool leftGap, rightGap; + getSelectionGapInfo(childState, leftGap, rightGap); + + if (leftGap) + result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + if (rightGap) + result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); + + // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as + // they can without bumping into floating or positioned objects. Ideally they will go right up + // to the border of the root selection block. + lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom(); + lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); + lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); + } else if (childState != SelectionNone) + // We must be a block that has some selected object inside it. Go ahead and recur. + result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, IntSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), + lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); + } + return result; +} + +IntRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int lastLogicalTop, int lastLogicalLeft, int lastLogicalRight, int logicalBottom, const PaintInfo* paintInfo) +{ + int logicalTop = lastLogicalTop; + int logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop; + if (logicalHeight <= 0) + return IntRect(); + + // Get the selection offsets for the bottom of the gap + int logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); + int logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); + int logicalWidth = logicalRight - logicalLeft; + if (logicalWidth <= 0) + return IntRect(); + + IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selectionBackgroundColor(), style()->colorSpace()); + return gapRect; +} + +IntRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + RenderObject* selObj, int logicalLeft, int logicalTop, int logicalHeight, const PaintInfo* paintInfo) +{ + int rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; + int rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); + int rootBlockLogicalRight = min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft, min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); + int rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; + if (rootBlockLogicalWidth <= 0) + return IntRect(); + + IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace()); + return gapRect; +} + +IntRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + RenderObject* selObj, int logicalRight, int logicalTop, int logicalHeight, const PaintInfo* paintInfo) +{ + int rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; + int rootBlockLogicalLeft = max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight, max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); + int rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); + int rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; + if (rootBlockLogicalWidth <= 0) + return IntRect(); + + IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, IntRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor(), selObj->style()->colorSpace()); + return gapRect; +} + +void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) +{ + bool ltr = style()->isLeftToRightDirection(); + leftGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionEnd && ltr) || + (state == RenderObject::SelectionStart && !ltr); + rightGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionStart && ltr) || + (state == RenderObject::SelectionEnd && !ltr); +} + +int RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, int position) +{ + int logicalLeft = logicalLeftOffsetForLine(position, false); + if (logicalLeft == logicalLeftOffsetForContent()) { + if (rootBlock != this) + // The border can potentially be further extended by our containingBlock(). + return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); + return logicalLeft; + } else { + RenderBlock* cb = this; + while (cb != rootBlock) { + logicalLeft += cb->logicalLeft(); + cb = cb->containingBlock(); + } + } + return logicalLeft; +} + +int RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, int position) +{ + int logicalRight = logicalRightOffsetForLine(position, false); + if (logicalRight == logicalRightOffsetForContent()) { + if (rootBlock != this) + // The border can potentially be further extended by our containingBlock(). + return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); + return logicalRight; + } else { + RenderBlock* cb = this; + while (cb != rootBlock) { + logicalRight += cb->logicalLeft(); + cb = cb->containingBlock(); + } + } + return logicalRight; +} + +void RenderBlock::insertPositionedObject(RenderBox* o) +{ + // Create the list of special objects if we don't aleady have one + if (!m_positionedObjects) + m_positionedObjects = new PositionedObjectsListHashSet; + + m_positionedObjects->add(o); +} + +void RenderBlock::removePositionedObject(RenderBox* o) +{ + if (m_positionedObjects) + m_positionedObjects->remove(o); +} + +void RenderBlock::removePositionedObjects(RenderBlock* o) +{ + if (!m_positionedObjects) + return; + + RenderBox* r; + + Iterator end = m_positionedObjects->end(); + + Vector deadObjects; + + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + if (!o || r->isDescendantOf(o)) { + if (o) + r->setChildNeedsLayout(true, false); + + // It is parent blocks job to add positioned child to positioned objects list of its containing block + // Parent layout needs to be invalidated to ensure this happens. + RenderObject* p = r->parent(); + while (p && !p->isRenderBlock()) + p = p->parent(); + if (p) + p->setChildNeedsLayout(true); + + deadObjects.append(r); + } + } + + for (unsigned i = 0; i < deadObjects.size(); i++) + m_positionedObjects->remove(deadObjects.at(i)); +} + +RenderBlock::FloatingObject* RenderBlock::insertFloatingObject(RenderBox* o) +{ + ASSERT(o->isFloating()); + + // Create the list of special objects if we don't aleady have one + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList; + m_floatingObjects->setAutoDelete(true); + } else { + // Don't insert the object again if it's already in the list + DeprecatedPtrListIterator it(*m_floatingObjects); + FloatingObject* f; + while ( (f = it.current()) ) { + if (f->m_renderer == o) + return f; + ++it; + } + } + + // Create the special object entry & append it to the list + + FloatingObject* newObj = new FloatingObject(o->style()->floating() == FLEFT ? FloatingObject::FloatLeft : FloatingObject::FloatRight); + + // Our location is irrelevant if we're unsplittable or no pagination is in effect. + // Just go ahead and lay out the float. + bool isChildRenderBlock = o->isRenderBlock(); + if (isChildRenderBlock && !o->needsLayout() && view()->layoutState()->pageLogicalHeightChanged()) + o->setChildNeedsLayout(true, false); + + bool affectedByPagination = isChildRenderBlock && view()->layoutState()->m_pageLogicalHeight; + if (!affectedByPagination || isWritingModeRoot()) // We are unsplittable if we're a block flow root. + o->layoutIfNeeded(); + else { + o->computeLogicalWidth(); + o->computeBlockDirectionMargins(this); + } + setLogicalWidthForFloat(newObj, logicalWidthForChild(o) + marginStartForChild(o) + marginEndForChild(o)); + + newObj->m_shouldPaint = !o->hasSelfPaintingLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. + newObj->m_isDescendant = true; + newObj->m_renderer = o; + + m_floatingObjects->append(newObj); + + return newObj; +} + +void RenderBlock::removeFloatingObject(RenderBox* o) +{ + if (m_floatingObjects) { + DeprecatedPtrListIterator it(*m_floatingObjects); + while (it.current()) { + if (it.current()->m_renderer == o) { + if (childrenInline()) { + int logicalTop = logicalTopForFloat(it.current()); + int logicalBottom = logicalBottomForFloat(it.current()); + + // Special-case zero- and less-than-zero-height floats: those don't touch + // the line that they're on, but it still needs to be dirtied. This is + // accomplished by pretending they have a height of 1. + logicalBottom = max(logicalBottom, logicalTop + 1); + markLinesDirtyInBlockRange(0, logicalBottom); + } + m_floatingObjects->removeRef(it.current()); + } + ++it; + } + } +} + +void RenderBlock::removeFloatingObjectsBelow(FloatingObject* lastFloat, int y) +{ + if (!m_floatingObjects) + return; + + FloatingObject* curr = m_floatingObjects->last(); + while (curr != lastFloat && (!curr->isPlaced() || curr->top() >= y)) { + m_floatingObjects->removeLast(); + curr = m_floatingObjects->last(); + } +} + +bool RenderBlock::positionNewFloats() +{ + if (!m_floatingObjects) + return false; + + FloatingObject* floatingObject = m_floatingObjects->last(); + + // If all floats have already been positioned, then we have no work to do. + if (!floatingObject || floatingObject->isPlaced()) + return false; + + // Move backwards through our floating object list until we find a float that has + // already been positioned. Then we'll be able to move forward, positioning all of + // the new floats that need it. + FloatingObject* lastFloat = m_floatingObjects->getPrev(); + while (lastFloat && !lastFloat->isPlaced()) { + floatingObject = m_floatingObjects->prev(); + lastFloat = m_floatingObjects->getPrev(); + } + + int logicalTop = logicalHeight(); + + // The float cannot start above the top position of the last positioned float. + if (lastFloat) + logicalTop = max(logicalTopForFloat(lastFloat), logicalTop); + + // Now walk through the set of unpositioned floats and place them. + while (floatingObject) { + // The containing block is responsible for positioning floats, so if we have floats in our + // list that come from somewhere else, do not attempt to position them. + if (floatingObject->renderer()->containingBlock() != this) { + floatingObject = m_floatingObjects->next(); + continue; + } + + RenderBox* childBox = floatingObject->renderer(); + int childLogicalLeftMargin = style()->isLeftToRightDirection() ? marginStartForChild(childBox) : marginEndForChild(childBox); + + int rightOffset = logicalRightOffsetForContent(); // Constant part of right offset. + int leftOffset = logicalLeftOffsetForContent(); // Constant part of left offset. + int floatLogicalWidth = logicalWidthForFloat(floatingObject); // The width we look for. + if (rightOffset - leftOffset < floatLogicalWidth) + floatLogicalWidth = rightOffset - leftOffset; // Never look for more than what will be available. + + IntRect oldRect(childBox->x(), childBox->y() , childBox->width(), childBox->height()); + + if (childBox->style()->clear() & CLEFT) + logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatLeft), logicalTop); + if (childBox->style()->clear() & CRIGHT) + logicalTop = max(lowestFloatLogicalBottom(FloatingObject::FloatRight), logicalTop); + + int floatLogicalLeft; + if (childBox->style()->floating() == FLEFT) { + int heightRemainingLeft = 1; + int heightRemainingRight = 1; + floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft); + while (logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight) - floatLogicalLeft < floatLogicalWidth) { + logicalTop += min(heightRemainingLeft, heightRemainingRight); + floatLogicalLeft = logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft); + } + floatLogicalLeft = max(0, floatLogicalLeft); + } else { + int heightRemainingLeft = 1; + int heightRemainingRight = 1; + floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight); + while (floatLogicalLeft - logicalLeftOffsetForLine(logicalTop, leftOffset, false, &heightRemainingLeft) < floatLogicalWidth) { + logicalTop += min(heightRemainingLeft, heightRemainingRight); + floatLogicalLeft = logicalRightOffsetForLine(logicalTop, rightOffset, false, &heightRemainingRight); + } + floatLogicalLeft -= logicalWidthForFloat(floatingObject); // Use the original width of the float here, since the local variable + // |floatLogicalWidth| was capped to the available line width. + // See fast/block/float/clamped-right-float.html. + } + + setLogicalLeftForFloat(floatingObject, floatLogicalLeft); + setLogicalLeftForChild(childBox, floatLogicalLeft + childLogicalLeftMargin); + setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox)); + + if (view()->layoutState()->isPaginated()) { + RenderBlock* childBlock = childBox->isRenderBlock() ? toRenderBlock(childBox) : 0; + + if (!childBox->needsLayout()) + childBox->markForPaginationRelayoutIfNeeded();; + childBox->layoutIfNeeded(); + + // If we are unsplittable and don't fit, then we need to move down. + // We include our margins as part of the unsplittable area. + int newLogicalTop = adjustForUnsplittableChild(childBox, logicalTop, true); + + // See if we have a pagination strut that is making us move down further. + // Note that an unsplittable child can't also have a pagination strut, so this is + // exclusive with the case above. + if (childBlock && childBlock->paginationStrut()) { + newLogicalTop += childBlock->paginationStrut(); + childBlock->setPaginationStrut(0); + } + + if (newLogicalTop != logicalTop) { + floatingObject->m_paginationStrut = newLogicalTop - logicalTop; + logicalTop = newLogicalTop; + setLogicalTopForChild(childBox, logicalTop + marginBeforeForChild(childBox)); + if (childBlock) + childBlock->setChildNeedsLayout(true, false); + childBox->layoutIfNeeded(); + } + } + + setLogicalTopForFloat(floatingObject, logicalTop); + setLogicalHeightForFloat(floatingObject, logicalHeightForChild(childBox) + marginBeforeForChild(childBox) + marginAfterForChild(childBox)); + + floatingObject->setIsPlaced(); + + // If the child moved, we have to repaint it. + if (childBox->checkForRepaintDuringLayout()) + childBox->repaintDuringLayoutIfMoved(oldRect); + + floatingObject = m_floatingObjects->next(); + } + return true; +} + +bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine) +{ + bool didPosition = positionNewFloats(); + if (!didPosition || !newFloat->m_paginationStrut) + return didPosition; + + int floatLogicalTop = logicalTopForFloat(newFloat); + int paginationStrut = newFloat->m_paginationStrut; + FloatingObject* f = m_floatingObjects->last(); + + ASSERT(f == newFloat); + + if (floatLogicalTop - paginationStrut != logicalHeight()) + return didPosition; + + for (f = m_floatingObjects->prev(); f && f != lastFloatFromPreviousLine; f = m_floatingObjects->prev()) { + if (logicalTopForFloat(f) == logicalHeight()) { + ASSERT(!f->m_paginationStrut); + f->m_paginationStrut = paginationStrut; + RenderBox* o = f->m_renderer; + setLogicalTopForChild(o, logicalTopForChild(o) + marginBeforeForChild(o) + paginationStrut); + if (o->isRenderBlock()) + toRenderBlock(o)->setChildNeedsLayout(true, false); + o->layoutIfNeeded(); + setLogicalTopForFloat(f, logicalTopForFloat(f) + f->m_paginationStrut); + } + } + + setLogicalHeight(logicalHeight() + paginationStrut); + + return didPosition; +} + +void RenderBlock::newLine(EClear clear) +{ + positionNewFloats(); + // set y position + int newY = 0; + switch (clear) + { + case CLEFT: + newY = lowestFloatLogicalBottom(FloatingObject::FloatLeft); + break; + case CRIGHT: + newY = lowestFloatLogicalBottom(FloatingObject::FloatRight); + break; + case CBOTH: + newY = lowestFloatLogicalBottom(); + default: + break; + } + if (height() < newY) + setLogicalHeight(newY); +} + +void RenderBlock::addPercentHeightDescendant(RenderBox* descendant) +{ + if (!gPercentHeightDescendantsMap) { + gPercentHeightDescendantsMap = new PercentHeightDescendantsMap; + gPercentHeightContainerMap = new PercentHeightContainerMap; + } + + HashSet* descendantSet = gPercentHeightDescendantsMap->get(this); + if (!descendantSet) { + descendantSet = new HashSet; + gPercentHeightDescendantsMap->set(this, descendantSet); + } + bool added = descendantSet->add(descendant).second; + if (!added) { + ASSERT(gPercentHeightContainerMap->get(descendant)); + ASSERT(gPercentHeightContainerMap->get(descendant)->contains(this)); + return; + } + + HashSet* containerSet = gPercentHeightContainerMap->get(descendant); + if (!containerSet) { + containerSet = new HashSet; + gPercentHeightContainerMap->set(descendant, containerSet); + } + ASSERT(!containerSet->contains(this)); + containerSet->add(this); +} + +void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) +{ + if (!gPercentHeightContainerMap) + return; + + HashSet* containerSet = gPercentHeightContainerMap->take(descendant); + if (!containerSet) + return; + + HashSet::iterator end = containerSet->end(); + for (HashSet::iterator it = containerSet->begin(); it != end; ++it) { + RenderBlock* container = *it; + HashSet* descendantSet = gPercentHeightDescendantsMap->get(container); + ASSERT(descendantSet); + if (!descendantSet) + continue; + ASSERT(descendantSet->contains(descendant)); + descendantSet->remove(descendant); + if (descendantSet->isEmpty()) { + gPercentHeightDescendantsMap->remove(container); + delete descendantSet; + } + } + + delete containerSet; +} + +HashSet* RenderBlock::percentHeightDescendants() const +{ + return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; +} + +int RenderBlock::logicalLeftOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const +{ + int left = fixedOffset; + if (m_floatingObjects) { + if (heightRemaining) + *heightRemaining = 1; + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop + && r->type() == FloatingObject::FloatLeft + && logicalRightForFloat(r) > left) { + left = logicalRightForFloat(r); + if (heightRemaining) + *heightRemaining = logicalBottomForFloat(r) - logicalTop; + } + } + } + + if (applyTextIndent && style()->isLeftToRightDirection()) { + int cw = 0; + if (style()->textIndent().isPercent()) + cw = containingBlock()->availableLogicalWidth(); + left += style()->textIndent().calcMinValue(cw); + } + + return left; +} + +int RenderBlock::logicalRightOffsetForLine(int logicalTop, int fixedOffset, bool applyTextIndent, int* heightRemaining) const +{ + int right = fixedOffset; + + if (m_floatingObjects) { + if (heightRemaining) + *heightRemaining = 1; + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && logicalTopForFloat(r) <= logicalTop && logicalBottomForFloat(r) > logicalTop + && r->type() == FloatingObject::FloatRight + && logicalLeftForFloat(r) < right) { + right = logicalLeftForFloat(r); + if (heightRemaining) + *heightRemaining = logicalBottomForFloat(r) - logicalTop; + } + } + } + + if (applyTextIndent && !style()->isLeftToRightDirection()) { + int cw = 0; + if (style()->textIndent().isPercent()) + cw = containingBlock()->availableLogicalWidth(); + right -= style()->textIndent().calcMinValue(cw); + } + + return right; +} + +int +RenderBlock::availableLogicalWidthForLine(int position, bool firstLine) const +{ + int result = logicalRightOffsetForLine(position, firstLine) - logicalLeftOffsetForLine(position, firstLine); + return (result < 0) ? 0 : result; +} + +int RenderBlock::nextFloatLogicalBottomBelow(int logicalHeight) const +{ + if (!m_floatingObjects) + return 0; + + int bottom = INT_MAX; + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + int floatBottom = logicalBottomForFloat(r); + if (floatBottom > logicalHeight) + bottom = min(floatBottom, bottom); + } + + return bottom == INT_MAX ? 0 : bottom; +} + +int RenderBlock::lowestFloatLogicalBottom(FloatingObject::Type floatType) const +{ + if (!m_floatingObjects) + return 0; + int lowestFloatBottom = 0; + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + if (r->isPlaced() && r->type() & floatType) + lowestFloatBottom = max(lowestFloatBottom, logicalBottomForFloat(r)); + } + return lowestFloatBottom; +} + +void RenderBlock::markLinesDirtyInBlockRange(int logicalTop, int logicalBottom, RootInlineBox* highest) +{ + if (logicalTop >= logicalBottom) + return; + + RootInlineBox* lowestDirtyLine = lastRootBox(); + RootInlineBox* afterLowest = lowestDirtyLine; + while (lowestDirtyLine && lowestDirtyLine->blockLogicalHeight() >= logicalBottom) { + afterLowest = lowestDirtyLine; + lowestDirtyLine = lowestDirtyLine->prevRootBox(); + } + + while (afterLowest && afterLowest != highest && afterLowest->blockLogicalHeight() >= logicalTop) { + afterLowest->markDirty(); + afterLowest = afterLowest->prevRootBox(); + } +} + +void RenderBlock::clearFloats() +{ + // Inline blocks are covered by the isReplaced() check in the avoidFloats method. + if (avoidsFloats() || isRoot() || isRenderView() || isFloatingOrPositioned() || isTableCell()) { + if (m_floatingObjects) + m_floatingObjects->clear(); + return; + } + + typedef HashMap RendererToFloatInfoMap; + RendererToFloatInfoMap floatMap; + + if (m_floatingObjects) { + if (childrenInline()) { + m_floatingObjects->first(); + while (FloatingObject* f = m_floatingObjects->take()) + floatMap.add(f->m_renderer, f); + } else + m_floatingObjects->clear(); + } + + // We should not process floats if the parent node is not a RenderBlock. Otherwise, we will add + // floats in an invalid context. This will cause a crash arising from a bad cast on the parent. + // See , where float property is applied on a text node in a SVG. + if (!parent() || !parent()->isRenderBlock()) + return; + + // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are + // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted + // to avoid floats. + bool parentHasFloats = false; + RenderBlock* parentBlock = toRenderBlock(parent()); + RenderObject* prev = previousSibling(); + while (prev && (prev->isFloatingOrPositioned() || !prev->isBox() || !prev->isRenderBlock() || toRenderBlock(prev)->avoidsFloats())) { + if (prev->isFloating()) + parentHasFloats = true; + prev = prev->previousSibling(); + } + + // First add in floats from the parent. + int logicalTopOffset = logicalTop(); + if (parentHasFloats) + addIntrudingFloats(parentBlock, parentBlock->logicalLeftOffsetForContent(), logicalTopOffset); + + int logicalLeftOffset = 0; + if (prev) + logicalTopOffset -= toRenderBox(prev)->logicalTop(); + else { + prev = parentBlock; + logicalLeftOffset += parentBlock->logicalLeftOffsetForContent(); + } + + // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space. + if (!prev || !prev->isRenderBlock()) + return; + + RenderBlock* block = toRenderBlock(prev); + if (block->m_floatingObjects && block->lowestFloatLogicalBottom() > logicalTopOffset) + addIntrudingFloats(block, logicalLeftOffset, logicalTopOffset); + + if (childrenInline()) { + int changeLogicalTop = numeric_limits::max(); + int changeLogicalBottom = numeric_limits::min(); + if (m_floatingObjects) { + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + FloatingObject* oldFloatingObject = floatMap.get(f->m_renderer); + int logicalBottom = logicalBottomForFloat(f); + if (oldFloatingObject) { + int oldLogicalBottom = logicalBottomForFloat(oldFloatingObject); + if (logicalWidthForFloat(f) != logicalWidthForFloat(oldFloatingObject) || logicalLeftForFloat(f) != logicalLeftForFloat(oldFloatingObject)) { + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom)); + } else if (logicalBottom != oldLogicalBottom) { + changeLogicalTop = min(changeLogicalTop, min(logicalBottom, oldLogicalBottom)); + changeLogicalBottom = max(changeLogicalBottom, max(logicalBottom, oldLogicalBottom)); + } + + floatMap.remove(f->m_renderer); + delete oldFloatingObject; + } else { + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, logicalBottom); + } + } + } + + RendererToFloatInfoMap::iterator end = floatMap.end(); + for (RendererToFloatInfoMap::iterator it = floatMap.begin(); it != end; ++it) { + FloatingObject* floatingObject = (*it).second; + if (!floatingObject->m_isDescendant) { + changeLogicalTop = 0; + changeLogicalBottom = max(changeLogicalBottom, logicalBottomForFloat(floatingObject)); + } + } + deleteAllValues(floatMap); + + markLinesDirtyInBlockRange(changeLogicalTop, changeLogicalBottom); + } +} + +int RenderBlock::addOverhangingFloats(RenderBlock* child, int logicalLeftOffset, int logicalTopOffset, bool makeChildPaintOtherFloats) +{ + // Prevent floats from being added to the canvas by the root element, e.g., . + if (child->hasOverflowClip() || !child->containsFloats() || child->isRoot() || child->hasColumns() || child->isWritingModeRoot()) + return 0; + + int lowestFloatLogicalBottom = 0; + + // Floats that will remain the child's responsibility to paint should factor into its + // overflow. + DeprecatedPtrListIterator it(*child->m_floatingObjects); + for (FloatingObject* r; (r = it.current()); ++it) { + int logicalBottom = child->logicalTop() + logicalBottomForFloat(r); + lowestFloatLogicalBottom = max(lowestFloatLogicalBottom, logicalBottom); + + if (logicalBottom > logicalHeight()) { + // If the object is not in the list, we add it now. + if (!containsFloat(r->m_renderer)) { + int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->left() - leftOffset, r->top() - topOffset, r->width(), r->height())); + floatingObj->m_renderer = r->m_renderer; + + // The nearest enclosing layer always paints the float (so that zindex and stacking + // behaves properly). We always want to propagate the desire to paint the float as + // far out as we can, to the outermost block that overlaps the float, stopping only + // if we hit a self-painting layer boundary. + if (r->m_renderer->enclosingFloatPaintingLayer() == enclosingFloatPaintingLayer()) + r->m_shouldPaint = false; + else + floatingObj->m_shouldPaint = false; + + floatingObj->m_isDescendant = true; + + // We create the floating object list lazily. + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList; + m_floatingObjects->setAutoDelete(true); + } + m_floatingObjects->append(floatingObj); + } + } else { + if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasSelfPaintingLayer() && + r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingFloatPaintingLayer() == child->enclosingFloatPaintingLayer()) { + // The float is not overhanging from this block, so if it is a descendant of the child, the child should + // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing + // layer. + // If makeChildPaintOtherFloats is false, it means that the child must already know about all the floats + // it should paint. + r->m_shouldPaint = true; + } + + // Since the float doesn't overhang, it didn't get put into our list. We need to go ahead and add its overflow in to the + // child now. + if (r->m_isDescendant) + child->addOverflowFromChild(r->m_renderer, IntSize(r->left() + r->m_renderer->marginLeft(), r->top() + r->m_renderer->marginTop())); + } + } + return lowestFloatLogicalBottom; +} + +void RenderBlock::addIntrudingFloats(RenderBlock* prev, int logicalLeftOffset, int logicalTopOffset) +{ + // If the parent or previous sibling doesn't have any floats to add, don't bother. + if (!prev->m_floatingObjects) + return; + + logicalLeftOffset += (style()->isHorizontalWritingMode() ? marginLeft() : marginTop()); + + DeprecatedPtrListIterator it(*prev->m_floatingObjects); + for (FloatingObject *r; (r = it.current()); ++it) { + if (logicalBottomForFloat(r) > logicalTopOffset) { + // The object may already be in our list. Check for it up front to avoid + // creating duplicate entries. + FloatingObject* f = 0; + if (m_floatingObjects) { + DeprecatedPtrListIterator it(*m_floatingObjects); + while ((f = it.current())) { + if (f->m_renderer == r->m_renderer) + break; + ++it; + } + } + if (!f) { + int leftOffset = style()->isHorizontalWritingMode() ? logicalLeftOffset : logicalTopOffset; + int topOffset = style()->isHorizontalWritingMode() ? logicalTopOffset : logicalLeftOffset; + + FloatingObject* floatingObj = new FloatingObject(r->type(), IntRect(r->left() - leftOffset, r->top() - topOffset, r->width(), r->height())); + + // Applying the child's margin makes no sense in the case where the child was passed in. + // since this margin was added already through the modification of the |logicalLeftOffset| variable + // above. |logicalLeftOffset| will equal the margin in this case, so it's already been taken + // into account. Only apply this code if prev is the parent, since otherwise the left margin + // will get applied twice. + if (prev != parent()) { + if (style()->isHorizontalWritingMode()) + floatingObj->setLeft(floatingObj->left() + prev->marginLeft()); + else + floatingObj->setTop(floatingObj->top() + prev->marginTop()); + } + + floatingObj->m_shouldPaint = false; // We are not in the direct inheritance chain for this float. We will never paint it. + floatingObj->m_renderer = r->m_renderer; + + // We create the floating object list lazily. + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList; + m_floatingObjects->setAutoDelete(true); + } + m_floatingObjects->append(floatingObj); + } + } + } +} + +bool RenderBlock::avoidsFloats() const +{ + // Floats can't intrude into our box if we have a non-auto column count or width. + return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); +} + +bool RenderBlock::containsFloat(RenderObject* o) +{ + if (m_floatingObjects) { + DeprecatedPtrListIterator it(*m_floatingObjects); + while (it.current()) { + if (it.current()->m_renderer == o) + return true; + ++it; + } + } + return false; +} + +void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove, bool inLayout) +{ + if (!m_everHadLayout) + return; + + setChildNeedsLayout(true, !inLayout); + + if (floatToRemove) + removeFloatingObject(floatToRemove); + + // Iterate over our children and mark them as needed. + if (!childrenInline()) { + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if ((!floatToRemove && child->isFloatingOrPositioned()) || !child->isRenderBlock()) + continue; + RenderBlock* childBlock = toRenderBlock(child); + if ((floatToRemove ? childBlock->containsFloat(floatToRemove) : childBlock->containsFloats()) || childBlock->shrinkToAvoidFloats()) + childBlock->markAllDescendantsWithFloatsForLayout(floatToRemove, inLayout); + } + } +} + +int RenderBlock::getClearDelta(RenderBox* child, int yPos) +{ + // There is no need to compute clearance if we have no floats. + if (!containsFloats()) + return 0; + + // At least one float is present. We need to perform the clearance computation. + bool clearSet = child->style()->clear() != CNONE; + int bottom = 0; + switch (child->style()->clear()) { + case CNONE: + break; + case CLEFT: + bottom = lowestFloatLogicalBottom(FloatingObject::FloatLeft); + break; + case CRIGHT: + bottom = lowestFloatLogicalBottom(FloatingObject::FloatRight); + break; + case CBOTH: + bottom = lowestFloatLogicalBottom(); + break; + } + + // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default). + int result = clearSet ? max(0, bottom - yPos) : 0; + if (!result && child->avoidsFloats()) { + int y = yPos; + while (true) { + int widthAtY = availableLogicalWidthForLine(y, false); + if (widthAtY == availableLogicalWidth()) + return y - yPos; + + int oldChildY = child->y(); + int oldChildWidth = child->width(); + child->setY(y); + child->computeLogicalWidth(); + int childWidthAtY = child->width(); + child->setY(oldChildY); + child->setWidth(oldChildWidth); + + if (childWidthAtY <= widthAtY) + return y - yPos; + + y = nextFloatLogicalBottomBelow(y); + ASSERT(y >= yPos); + if (y < yPos) + break; + } + ASSERT_NOT_REACHED(); + } + return result; +} + +bool RenderBlock::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) +{ + if (!scrollsOverflow()) + return false; + + return layer()->hitTestOverflowControls(result, IntPoint(_x - _tx, _y - _ty)); +} + +bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + int tx = _tx + x(); + int ty = _ty + y(); + + if (!isRenderView()) { + // Check if we need to do anything at all. + IntRect overflowBox = visualOverflowRect(); + overflowBox.move(tx, ty); + if (!overflowBox.intersects(result.rectForPoint(_x, _y))) + return false; + } + + if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, _x, _y, tx, ty)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet. + if (!result.addNodeToRectBasedTestResult(node(), _x, _y)) + return true; + } + + // If we have clipping, then we can't have any spillout. + bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); + bool useClip = (hasControlClip() || useOverflowClip); + IntRect hitTestArea(result.rectForPoint(_x, _y)); + bool checkChildren = !useClip || (hasControlClip() ? controlClipRect(tx, ty).intersects(hitTestArea) : overflowClipRect(tx, ty).intersects(hitTestArea)); + if (checkChildren) { + // Hit test descendants first. + int scrolledX = tx; + int scrolledY = ty; + if (hasOverflowClip()) { + IntSize offset = layer()->scrolledContentOffset(); + scrolledX -= offset.width(); + scrolledY -= offset.height(); + } + + // Hit test contents if we don't have columns. + if (!hasColumns()) { + if (hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + return true; + } + if (hitTestAction == HitTestFloat && hitTestFloats(request, result, _x, _y, scrolledX, scrolledY)) + return true; + } else if (hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + return true; + } + } + + // Now hit test our background + if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { + IntRect boundsRect(tx, ty, width(), height()); + if (visibleToHitTesting() && boundsRect.intersects(result.rectForPoint(_x, _y))) { + updateHitTestResult(result, flipForWritingMode(IntPoint(_x - tx, _y - ty))); + if (!result.addNodeToRectBasedTestResult(node(), _x, _y, boundsRect)) + return true; + } + } + + return false; +} + +bool RenderBlock::hitTestFloats(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + if (!m_floatingObjects) + return false; + + if (isRenderView()) { + tx += toRenderView(this)->frameView()->scrollX(); + ty += toRenderView(this)->frameView()->scrollY(); + } + + FloatingObject* floatingObject; + DeprecatedPtrListIterator it(*m_floatingObjects); + for (it.toLast(); (floatingObject = it.current()); --it) { + if (floatingObject->m_shouldPaint && !floatingObject->m_renderer->hasSelfPaintingLayer()) { + int xOffset = floatingObject->left() + floatingObject->m_renderer->marginLeft() - floatingObject->m_renderer->x(); + int yOffset = floatingObject->top() + floatingObject->m_renderer->marginTop() - floatingObject->m_renderer->y(); + IntPoint childPoint = flipForWritingMode(floatingObject->m_renderer, IntPoint(tx + xOffset, ty + yOffset), ParentToChildFlippingAdjustment); + if (floatingObject->m_renderer->hitTest(request, result, IntPoint(x, y), childPoint.x(), childPoint.y())) { + updateHitTestResult(result, IntPoint(x - childPoint.x(), y - childPoint.y())); + return true; + } + } + } + + return false; +} + +bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + // We need to do multiple passes, breaking up our hit testing into strips. + ColumnInfo* colInfo = columnInfo(); + int colCount = columnCount(colInfo); + if (!colCount) + return false; + int left = borderLeft() + paddingLeft(); + int currYOffset = 0; + int i; + for (i = 0; i < colCount; i++) + currYOffset -= columnRectAt(colInfo, i).height(); + for (i = colCount - 1; i >= 0; i--) { + IntRect colRect = columnRectAt(colInfo, i); + int currXOffset = colRect.x() - left; + currYOffset += colRect.height(); + colRect.move(tx, ty); + + if (colRect.intersects(result.rectForPoint(x, y))) { + // The point is inside this column. + // Adjust tx and ty to change where we hit test. + + int finalX = tx + currXOffset; + int finalY = ty + currYOffset; + if (result.isRectBasedTest() && !colRect.contains(result.rectForPoint(x, y))) + hitTestContents(request, result, x, y, finalX, finalY, hitTestAction); + else + return hitTestContents(request, result, x, y, finalX, finalY, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, x, y, finalX, finalY)); + } + } + + return false; +} + +bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (childrenInline() && !isTable()) { + // We have to hit-test our line boxes. + if (m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction)) + return true; + } else { + // Hit test our children. + HitTestAction childHitTest = hitTestAction; + if (hitTestAction == HitTestChildBlockBackgrounds) + childHitTest = HitTestChildBlockBackground; + for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) { + IntPoint childPoint = flipForWritingMode(child, IntPoint(tx, ty), ParentToChildFlippingAdjustment); + if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, x, y, childPoint.x(), childPoint.y(), childHitTest)) + return true; + } + } + + return false; +} + +Position RenderBlock::positionForBox(InlineBox *box, bool start) const +{ + if (!box) + return Position(); + + if (!box->renderer()->node()) + return Position(node(), start ? caretMinOffset() : caretMaxOffset()); + + if (!box->isInlineTextBox()) + return Position(box->renderer()->node(), start ? box->renderer()->caretMinOffset() : box->renderer()->caretMaxOffset()); + + InlineTextBox *textBox = static_cast(box); + return Position(box->renderer()->node(), start ? textBox->start() : textBox->start() + textBox->len()); +} + +Position RenderBlock::positionForRenderer(RenderObject* renderer, bool start) const +{ + if (!renderer) + return Position(node(), 0); + + Node* n = renderer->node() ? renderer->node() : node(); + if (!n) + return Position(); + + ASSERT(renderer == n->renderer()); + + int offset = start ? renderer->caretMinOffset() : renderer->caretMaxOffset(); + + // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. + ASSERT(!n->isCharacterDataNode() || renderer->isText()); + + return Position(n, offset); +} + +// FIXME: This function should go on RenderObject as an instance method. Then +// all cases in which positionForPoint recurs could call this instead to +// prevent crossing editable boundaries. This would require many tests. +static VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock* parent, RenderBox* child, const IntPoint& pointInParentCoordinates) +{ + // FIXME: This is wrong if the child's writing-mode is different from the parent's. + IntPoint pointInChildCoordinates(pointInParentCoordinates - child->location()); + + // If this is an anonymous renderer, we just recur normally + Node* childNode = child->node(); + if (!childNode) + return child->positionForPoint(pointInChildCoordinates); + + // Otherwise, first make sure that the editability of the parent and child agree. + // If they don't agree, then we return a visible position just before or after the child + RenderObject* ancestor = parent; + while (ancestor && !ancestor->node()) + ancestor = ancestor->parent(); + + // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal + if (!ancestor || ancestor->node()->isContentEditable() == childNode->isContentEditable()) + return child->positionForPoint(pointInChildCoordinates); + + // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child + int childMiddle = parent->logicalWidthForChild(child) / 2; + int logicalLeft = parent->style()->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); + if (logicalLeft < childMiddle) + return ancestor->createVisiblePosition(childNode->nodeIndex(), DOWNSTREAM); + return ancestor->createVisiblePosition(childNode->nodeIndex() + 1, UPSTREAM); +} + +VisiblePosition RenderBlock::positionForPointWithInlineChildren(const IntPoint& pointInLogicalContents) +{ + ASSERT(childrenInline()); + + if (!firstRootBox()) + return createVisiblePosition(0, DOWNSTREAM); + + // look for the closest line box in the root box which is at the passed-in y coordinate + InlineBox* closestBox = 0; + RootInlineBox* firstRootBoxWithChildren = 0; + RootInlineBox* lastRootBoxWithChildren = 0; + for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { + if (!root->firstLeafChild()) + continue; + if (!firstRootBoxWithChildren) + firstRootBoxWithChildren = root; + lastRootBoxWithChildren = root; + + // check if this root line box is located at this y coordinate + if (pointInLogicalContents.y() < root->selectionBottom()) { + closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); + if (closestBox) + break; + } + } + + bool moveCaretToBoundary = document()->frame()->editor()->behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); + + if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) { + // y coordinate is below last root line box, pretend we hit it + closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); + } + + if (closestBox) { + if (moveCaretToBoundary && pointInLogicalContents.y() < firstRootBoxWithChildren->selectionTop()) { + // y coordinate is above first root line box, so return the start of the first + return VisiblePosition(positionForBox(firstRootBoxWithChildren->firstLeafChild(), true), DOWNSTREAM); + } + + // pass the box a top position that is inside it + IntPoint point(pointInLogicalContents.x(), closestBox->logicalTop()); + if (!style()->isHorizontalWritingMode()) + point = point.transposedPoint(); + if (closestBox->renderer()->isReplaced()) + return positionForPointRespectingEditingBoundaries(this, toRenderBox(closestBox->renderer()), point); + return closestBox->renderer()->positionForPoint(point); + } + + if (lastRootBoxWithChildren) { + // We hit this case for Mac behavior when the Y coordinate is below the last box. + ASSERT(moveCaretToBoundary); + return VisiblePosition(positionForBox(lastRootBoxWithChildren->lastLeafChild(), false), DOWNSTREAM); + } + + // Can't reach this. We have a root line box, but it has no kids. + // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text + // seems to hit this code path. + return createVisiblePosition(0, DOWNSTREAM); +} + +static inline bool isChildHitTestCandidate(RenderBox* box) +{ + return box->height() && box->style()->visibility() == VISIBLE && !box->isFloatingOrPositioned(); +} + +VisiblePosition RenderBlock::positionForPoint(const IntPoint& point) +{ + if (isTable()) + return RenderBox::positionForPoint(point); + + if (isReplaced()) { + // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. + int pointLogicalLeft = style()->isHorizontalWritingMode() ? point.x() : point.y(); + int pointLogicalTop = style()->isHorizontalWritingMode() ? point.y() : point.x(); + + if (pointLogicalTop < 0 || (pointLogicalTop < logicalHeight() && pointLogicalLeft < 0)) + return createVisiblePosition(caretMinOffset(), DOWNSTREAM); + if (pointLogicalTop >= logicalHeight() || (pointLogicalTop >= 0 && pointLogicalLeft >= logicalWidth())) + return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); + } + + int contentsX = point.x(); + int contentsY = point.y(); + offsetForContents(contentsX, contentsY); + IntPoint pointInContents(contentsX, contentsY); + IntPoint pointInLogicalContents(pointInContents); + if (!style()->isHorizontalWritingMode()) + pointInLogicalContents = pointInLogicalContents.transposedPoint(); + + if (childrenInline()) + return positionForPointWithInlineChildren(pointInLogicalContents); + + if (lastChildBox() && pointInContents.y() > lastChildBox()->logicalTop()) { + for (RenderBox* childBox = lastChildBox(); childBox; childBox = childBox->previousSiblingBox()) { + if (isChildHitTestCandidate(childBox)) + return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents); + } + } else { + for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { + // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3). + if (isChildHitTestCandidate(childBox) && pointInContents.y() < childBox->logicalBottom()) + return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents); + } + } + + // We only get here if there are no hit test candidate children below the click. + return RenderBox::positionForPoint(point); +} + +void RenderBlock::offsetForContents(int& tx, int& ty) const +{ + IntPoint contentsPoint(tx, ty); + + if (hasOverflowClip()) + contentsPoint += layer()->scrolledContentOffset(); + + if (hasColumns()) + adjustPointToColumnContents(contentsPoint); + + tx = contentsPoint.x(); + ty = contentsPoint.y(); +} + +int RenderBlock::availableLogicalWidth() const +{ + // If we have multiple columns, then the available logical width is reduced to our column width. + if (hasColumns()) + return desiredColumnWidth(); + return RenderBox::availableLogicalWidth(); +} + +int RenderBlock::columnGap() const +{ + if (style()->hasNormalColumnGap()) + return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches

margins. + return static_cast(style()->columnGap()); +} + +void RenderBlock::calcColumnWidth() +{ + // Calculate our column width and column count. + unsigned desiredColumnCount = 1; + int desiredColumnWidth = contentWidth(); + + // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. + if (document()->paginated() || (style()->hasAutoColumnCount() && style()->hasAutoColumnWidth())) { + setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); + return; + } + + int availWidth = desiredColumnWidth; + int colGap = columnGap(); + int colWidth = max(1, static_cast(style()->columnWidth())); + int colCount = max(1, static_cast(style()->columnCount())); + + if (style()->hasAutoColumnWidth()) { + if ((colCount - 1) * colGap < availWidth) { + desiredColumnCount = colCount; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } else if (colGap < availWidth) { + desiredColumnCount = availWidth / colGap; + if (desiredColumnCount < 1) + desiredColumnCount = 1; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } else if (style()->hasAutoColumnCount()) { + if (colWidth < availWidth) { + desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + if (desiredColumnCount < 1) + desiredColumnCount = 1; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } else { + // Both are set. + if (colCount * colWidth + (colCount - 1) * colGap <= availWidth) { + desiredColumnCount = colCount; + desiredColumnWidth = colWidth; + } else if (colWidth < availWidth) { + desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + if (desiredColumnCount < 1) + desiredColumnCount = 1; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } + setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); +} + +void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) +{ + bool destroyColumns = !firstChild() + || (count == 1 && style()->hasAutoColumnWidth()) + || firstChild()->isAnonymousColumnsBlock() + || firstChild()->isAnonymousColumnSpanBlock(); + if (destroyColumns) { + if (hasColumns()) { + delete gColumnInfoMap->take(this); + setHasColumns(false); + } + } else { + ColumnInfo* info; + if (hasColumns()) + info = gColumnInfoMap->get(this); + else { + if (!gColumnInfoMap) + gColumnInfoMap = new ColumnInfoMap; + info = new ColumnInfo; + gColumnInfoMap->add(this, info); + setHasColumns(true); + } + info->setDesiredColumnCount(count); + info->setDesiredColumnWidth(width); + } +} + +int RenderBlock::desiredColumnWidth() const +{ + if (!hasColumns()) + return contentWidth(); + return gColumnInfoMap->get(this)->desiredColumnWidth(); +} + +unsigned RenderBlock::desiredColumnCount() const +{ + if (!hasColumns()) + return 1; + return gColumnInfoMap->get(this)->desiredColumnCount(); +} + +ColumnInfo* RenderBlock::columnInfo() const +{ + if (!hasColumns()) + return 0; + return gColumnInfoMap->get(this); +} + +unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const +{ + ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo); + return colInfo->columnCount(); +} + +IntRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const +{ + ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo); + + // Compute the appropriate rect based off our information. + int colWidth = colInfo->desiredColumnWidth(); + int colHeight = colInfo->columnHeight(); + int colTop = borderTop() + paddingTop(); + int colGap = columnGap(); + int colLeft = style()->isLeftToRightDirection() ? + borderLeft() + paddingLeft() + (index * (colWidth + colGap)) + : borderLeft() + paddingLeft() + contentWidth() - colWidth - (index * (colWidth + colGap)); + return IntRect(colLeft, colTop, colWidth, colHeight); +} + +bool RenderBlock::layoutColumns(bool hasSpecifiedPageLogicalHeight, int pageLogicalHeight, LayoutStateMaintainer& statePusher) +{ + if (!hasColumns()) + return false; + + // FIXME: We don't balance properly at all in the presence of forced page breaks. We need to understand what + // the distance between forced page breaks is so that we can avoid making the minimum column height too tall. + ColumnInfo* colInfo = columnInfo(); + int desiredColumnCount = colInfo->desiredColumnCount(); + if (!hasSpecifiedPageLogicalHeight) { + int columnHeight = pageLogicalHeight; + int minColumnCount = colInfo->forcedBreaks() + 1; + if (minColumnCount >= desiredColumnCount) { + // The forced page breaks are in control of the balancing. Just set the column height to the + // maximum page break distance. + if (!pageLogicalHeight) { + int distanceBetweenBreaks = max(colInfo->maximumDistanceBetweenForcedBreaks(), + view()->layoutState()->pageLogicalOffset(borderTop() + paddingTop() + contentHeight()) - colInfo->forcedBreakOffset()); + columnHeight = max(colInfo->minimumColumnHeight(), distanceBetweenBreaks); + } + } else if (contentHeight() > pageLogicalHeight * desiredColumnCount) { + // Now that we know the intrinsic height of the columns, we have to rebalance them. + columnHeight = max(colInfo->minimumColumnHeight(), (int)ceilf((float)contentHeight() / desiredColumnCount)); + } + + if (columnHeight && columnHeight != pageLogicalHeight) { + statePusher.pop(); + m_everHadLayout = true; + layoutBlock(false, columnHeight); + return true; + } + } + + if (pageLogicalHeight) + colInfo->setColumnCountAndHeight(ceilf((float)contentHeight() / pageLogicalHeight), pageLogicalHeight); + + if (columnCount(colInfo)) { + setLogicalHeight(borderTop() + paddingTop() + colInfo->columnHeight() + borderBottom() + paddingBottom() + horizontalScrollbarHeight()); + m_overflow.clear(); + } + + return false; +} + +void RenderBlock::adjustPointToColumnContents(IntPoint& point) const +{ + // Just bail if we have no columns. + if (!hasColumns()) + return; + + ColumnInfo* colInfo = columnInfo(); + if (!columnCount(colInfo)) + return; + + // Determine which columns we intersect. + int colGap = columnGap(); + int leftGap = colGap / 2; + IntPoint columnPoint(columnRectAt(colInfo, 0).location()); + int yOffset = 0; + for (unsigned i = 0; i < colInfo->columnCount(); i++) { + // Add in half the column gap to the left and right of the rect. + IntRect colRect = columnRectAt(colInfo, i); + IntRect gapAndColumnRect(colRect.x() - leftGap, colRect.y(), colRect.width() + colGap, colRect.height()); + + if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.right()) { + // FIXME: The clamping that follows is not completely right for right-to-left + // content. + // Clamp everything above the column to its top left. + if (point.y() < gapAndColumnRect.y()) + point = gapAndColumnRect.location(); + // Clamp everything below the column to the next column's top left. If there is + // no next column, this still maps to just after this column. + else if (point.y() >= gapAndColumnRect.bottom()) { + point = gapAndColumnRect.location(); + point.move(0, gapAndColumnRect.height()); + } + + // We're inside the column. Translate the x and y into our column coordinate space. + point.move(columnPoint.x() - colRect.x(), yOffset); + return; + } + + // Move to the next position. + yOffset += colRect.height(); + } +} + +void RenderBlock::adjustRectForColumns(IntRect& r) const +{ + // Just bail if we have no columns. + if (!hasColumns()) + return; + + ColumnInfo* colInfo = columnInfo(); + + // Begin with a result rect that is empty. + IntRect result; + + // Determine which columns we intersect. + unsigned colCount = columnCount(colInfo); + if (!colCount) + return; + + int left = borderLeft() + paddingLeft(); + + int currYOffset = 0; + for (unsigned i = 0; i < colCount; i++) { + IntRect colRect = columnRectAt(colInfo, i); + int currXOffset = colRect.x() - left; + + IntRect repaintRect = r; + repaintRect.move(currXOffset, currYOffset); + + repaintRect.intersect(colRect); + + result.unite(repaintRect); + + // Move to the next position. + currYOffset -= colRect.height(); + } + + r = result; +} + +void RenderBlock::adjustForColumns(IntSize& offset, const IntPoint& point) const +{ + if (!hasColumns()) + return; + + ColumnInfo* colInfo = columnInfo(); + + int left = borderLeft() + paddingLeft(); + int yOffset = 0; + size_t colCount = columnCount(colInfo); + for (size_t i = 0; i < colCount; ++i) { + IntRect columnRect = columnRectAt(colInfo, i); + int xOffset = columnRect.x() - left; + if (point.y() < columnRect.bottom() + yOffset) { + offset.expand(xOffset, -yOffset); + return; + } + + yOffset += columnRect.height(); + } +} + +void RenderBlock::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + + updateFirstLetter(); + + if (!isTableCell() && style()->logicalWidth().isFixed() && style()->logicalWidth().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->logicalWidth().value()); + else { + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + + if (childrenInline()) + computeInlinePreferredLogicalWidths(); + else + computeBlockPreferredLogicalWidths(); + + m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + + if (!style()->autoWrap() && childrenInline()) { + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + + // A horizontal marquee with inline children has no minimum width. + if (layer() && layer()->marquee() && layer()->marquee()->isHorizontal()) + m_minPreferredLogicalWidth = 0; + } + + int scrollbarWidth = 0; + if (hasOverflowClip() && style()->overflowY() == OSCROLL) { + layer()->setHasVerticalScrollbar(true); + scrollbarWidth = verticalScrollbarWidth(); + m_maxPreferredLogicalWidth += scrollbarWidth; + } + + if (isTableCell()) { + Length w = toRenderTableCell(this)->styleOrColLogicalWidth(); + if (w.isFixed() && w.value() > 0) { + m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(w.value())); + scrollbarWidth = 0; + } + } + + m_minPreferredLogicalWidth += scrollbarWidth; + } + + if (style()->logicalMinWidth().isFixed() && style()->logicalMinWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMinWidth().value())); + } + + if (style()->logicalMaxWidth().isFixed() && style()->logicalMaxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->logicalMaxWidth().value())); + } + + int borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; + + setPreferredLogicalWidthsDirty(false); +} + +struct InlineMinMaxIterator { +/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to + inline min/max width calculations. Note the following about the way it walks: + (1) Positioned content is skipped (since it does not contribute to min/max width of a block) + (2) We do not drill into the children of floats or replaced elements, since you can't break + in the middle of such an element. + (3) Inline flows (e.g., , , ) are walked twice, since each side can have + distinct borders/margin/padding that contribute to the min/max width. +*/ + RenderObject* parent; + RenderObject* current; + bool endOfInline; + + InlineMinMaxIterator(RenderObject* p, bool end = false) + :parent(p), current(p), endOfInline(end) {} + + RenderObject* next(); +}; + +RenderObject* InlineMinMaxIterator::next() +{ + RenderObject* result = 0; + bool oldEndOfInline = endOfInline; + endOfInline = false; + while (current || current == parent) { + if (!oldEndOfInline && + (current == parent || + (!current->isFloating() && !current->isReplaced() && !current->isPositioned()))) + result = current->firstChild(); + if (!result) { + // We hit the end of our inline. (It was empty, e.g., .) + if (!oldEndOfInline && current->isRenderInline()) { + result = current; + endOfInline = true; + break; + } + + while (current && current != parent) { + result = current->nextSibling(); + if (result) break; + current = current->parent(); + if (current && current != parent && current->isRenderInline()) { + result = current; + endOfInline = true; + break; + } + } + } + + if (!result) + break; + + if (!result->isPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isRenderInline())) + break; + + current = result; + result = 0; + } + + // Update our position. + current = result; + return current; +} + +static int getBPMWidth(int childValue, Length cssUnit) +{ + if (cssUnit.type() != Auto) + return (cssUnit.isFixed() ? cssUnit.value() : childValue); + return 0; +} + +static int getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline) +{ + RenderStyle* cstyle = child->style(); + int result = 0; + bool leftSide = (cstyle->isLeftToRightDirection()) ? !endOfInline : endOfInline; + result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()), + (leftSide ? cstyle->marginLeft() : + cstyle->marginRight())); + result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()), + (leftSide ? cstyle->paddingLeft() : + cstyle->paddingRight())); + result += leftSide ? child->borderLeft() : child->borderRight(); + return result; +} + +static inline void stripTrailingSpace(int& inlineMax, int& inlineMin, + RenderObject* trailingSpaceChild) +{ + if (trailingSpaceChild && trailingSpaceChild->isText()) { + // Collapse away the trailing space at the end of a block. + RenderText* t = toRenderText(trailingSpaceChild); + const UChar space = ' '; + const Font& font = t->style()->font(); // FIXME: This ignores first-line. + int spaceWidth = font.width(TextRun(&space, 1)); + inlineMax -= spaceWidth + font.wordSpacing(); + if (inlineMin > inlineMax) + inlineMin = inlineMax; + } +} + +void RenderBlock::computeInlinePreferredLogicalWidths() +{ + int inlineMax = 0; + int inlineMin = 0; + + int cw = containingBlock()->contentWidth(); + + // If we are at the start of a line, we want to ignore all white-space. + // Also strip spaces if we previously had text that ended in a trailing space. + bool stripFrontSpaces = true; + RenderObject* trailingSpaceChild = 0; + + // Firefox and Opera will allow a table cell to grow to fit an image inside it under + // very specific cirucumstances (in order to match common WinIE renderings). + // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) + bool allowImagesToBreak = !document()->inQuirksMode() || !isTableCell() || !style()->width().isIntrinsicOrAuto(); + + bool autoWrap, oldAutoWrap; + autoWrap = oldAutoWrap = style()->autoWrap(); + + InlineMinMaxIterator childIterator(this); + bool addedTextIndent = false; // Only gets added in once. + RenderObject* prevFloat = 0; + while (RenderObject* child = childIterator.next()) { + autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() : + child->style()->autoWrap(); + + if (!child->isBR()) { + // Step One: determine whether or not we need to go ahead and + // terminate our current line. Each discrete chunk can become + // the new min-width, if it is the widest chunk seen so far, and + // it can also become the max-width. + + // Children fall into three categories: + // (1) An inline flow object. These objects always have a min/max of 0, + // and are included in the iteration solely so that their margins can + // be added in. + // + // (2) An inline non-text non-flow object, e.g., an inline replaced element. + // These objects can always be on a line by themselves, so in this situation + // we need to go ahead and break the current line, and then add in our own + // margins and min/max width on its own line, and then terminate the line. + // + // (3) A text object. Text runs can have breakable characters at the start, + // the middle or the end. They may also lose whitespace off the front if + // we're already ignoring whitespace. In order to compute accurate min-width + // information, we need three pieces of information. + // (a) the min-width of the first non-breakable run. Should be 0 if the text string + // starts with whitespace. + // (b) the min-width of the last non-breakable run. Should be 0 if the text string + // ends with whitespace. + // (c) the min/max width of the string (trimmed for whitespace). + // + // If the text string starts with whitespace, then we need to go ahead and + // terminate our current line (unless we're already in a whitespace stripping + // mode. + // + // If the text string has a breakable character in the middle, but didn't start + // with whitespace, then we add the width of the first non-breakable run and + // then end the current line. We then need to use the intermediate min/max width + // values (if any of them are larger than our current min/max). We then look at + // the width of the last non-breakable run and use that to start a new line + // (unless we end in whitespace). + RenderStyle* cstyle = child->style(); + int childMin = 0; + int childMax = 0; + + if (!child->isText()) { + // Case (1) and (2). Inline replaced and inline flow elements. + if (child->isRenderInline()) { + // Add in padding/border/margin from the appropriate side of + // the element. + int bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline); + childMin += bpm; + childMax += bpm; + + inlineMin += childMin; + inlineMax += childMax; + + child->setPreferredLogicalWidthsDirty(false); + } else { + // Inline replaced elts add in their margins to their min/max values. + int margins = 0; + Length leftMargin = cstyle->marginLeft(); + Length rightMargin = cstyle->marginRight(); + if (leftMargin.isFixed()) + margins += leftMargin.value(); + if (rightMargin.isFixed()) + margins += rightMargin.value(); + childMin += margins; + childMax += margins; + } + } + + if (!child->isRenderInline() && !child->isText()) { + // Case (2). Inline replaced elements and floats. + // Go ahead and terminate the current line as far as + // minwidth is concerned. + childMin += child->minPreferredLogicalWidth(); + childMax += child->maxPreferredLogicalWidth(); + + bool clearPreviousFloat; + if (child->isFloating()) { + clearPreviousFloat = (prevFloat + && ((prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT)) + || (prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT)))); + prevFloat = child; + } else + clearPreviousFloat = false; + + bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; + if ((canBreakReplacedElement && (autoWrap || oldAutoWrap)) || clearPreviousFloat) { + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + inlineMin = 0; + } + + // If we're supposed to clear the previous float, then terminate maxwidth as well. + if (clearPreviousFloat) { + m_maxPreferredLogicalWidth = max(inlineMax, m_maxPreferredLogicalWidth); + inlineMax = 0; + } + + // Add in text-indent. This is added in only once. + int ti = 0; + if (!addedTextIndent) { + addedTextIndent = true; + ti = style()->textIndent().calcMinValue(cw); + childMin+=ti; + childMax+=ti; + } + + // Add our width to the max. + inlineMax += childMax; + + if (!autoWrap || !canBreakReplacedElement) { + if (child->isFloating()) + m_minPreferredLogicalWidth = max(childMin, m_minPreferredLogicalWidth); + else + inlineMin += childMin; + } else { + // Now check our line. + m_minPreferredLogicalWidth = max(childMin, m_minPreferredLogicalWidth); + + // Now start a new line. + inlineMin = 0; + } + + // We are no longer stripping whitespace at the start of + // a line. + if (!child->isFloating()) { + stripFrontSpaces = false; + trailingSpaceChild = 0; + } + } else if (child->isText()) { + // Case (3). Text. + RenderText* t = toRenderText(child); + + if (t->isWordBreak()) { + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + inlineMin = 0; + continue; + } + + // Determine if we have a breakable character. Pass in + // whether or not we should ignore any spaces at the front + // of the string. If those are going to be stripped out, + // then they shouldn't be considered in the breakable char + // check. + bool hasBreakableChar, hasBreak; + int beginMin, endMin; + bool beginWS, endWS; + int beginMax, endMax; + t->trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS, + hasBreakableChar, hasBreak, beginMax, endMax, + childMin, childMax, stripFrontSpaces); + + // This text object will not be rendered, but it may still provide a breaking opportunity. + if (!hasBreak && childMax == 0) { + if (autoWrap && (beginWS || endWS)) { + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + inlineMin = 0; + } + continue; + } + + if (stripFrontSpaces) + trailingSpaceChild = child; + else + trailingSpaceChild = 0; + + // Add in text-indent. This is added in only once. + int ti = 0; + if (!addedTextIndent) { + addedTextIndent = true; + ti = style()->textIndent().calcMinValue(cw); + childMin+=ti; beginMin += ti; + childMax+=ti; beginMax += ti; + } + + // If we have no breakable characters at all, + // then this is the easy case. We add ourselves to the current + // min and max and continue. + if (!hasBreakableChar) { + inlineMin += childMin; + } else { + // We have a breakable character. Now we need to know if + // we start and end with whitespace. + if (beginWS) + // Go ahead and end the current line. + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + else { + inlineMin += beginMin; + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + childMin -= ti; + } + + inlineMin = childMin; + + if (endWS) { + // We end in whitespace, which means we can go ahead + // and end our current line. + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + inlineMin = 0; + } else { + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + inlineMin = endMin; + } + } + + if (hasBreak) { + inlineMax += beginMax; + m_maxPreferredLogicalWidth = max(inlineMax, m_maxPreferredLogicalWidth); + m_maxPreferredLogicalWidth = max(childMax, m_maxPreferredLogicalWidth); + inlineMax = endMax; + } else + inlineMax += childMax; + } + + // Ignore spaces after a list marker. + if (child->isListMarker()) + stripFrontSpaces = true; + } else { + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + m_maxPreferredLogicalWidth = max(inlineMax, m_maxPreferredLogicalWidth); + inlineMin = inlineMax = 0; + stripFrontSpaces = true; + trailingSpaceChild = 0; + } + + oldAutoWrap = autoWrap; + } + + if (style()->collapseWhiteSpace()) + stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); + + m_minPreferredLogicalWidth = max(inlineMin, m_minPreferredLogicalWidth); + m_maxPreferredLogicalWidth = max(inlineMax, m_maxPreferredLogicalWidth); +} + +// Use a very large value (in effect infinite). +#define BLOCK_MAX_WIDTH 15000 + +void RenderBlock::computeBlockPreferredLogicalWidths() +{ + bool nowrap = style()->whiteSpace() == NOWRAP; + + RenderObject *child = firstChild(); + int floatLeftWidth = 0, floatRightWidth = 0; + while (child) { + // Positioned children don't affect the min/max width + if (child->isPositioned()) { + child = child->nextSibling(); + continue; + } + + if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { + int floatTotalWidth = floatLeftWidth + floatRightWidth; + if (child->style()->clear() & CLEFT) { + m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + floatLeftWidth = 0; + } + if (child->style()->clear() & CRIGHT) { + m_maxPreferredLogicalWidth = max(floatTotalWidth, m_maxPreferredLogicalWidth); + floatRightWidth = 0; + } + } + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto and percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0, marginLeft = 0, marginRight = 0; + if (ml.isFixed()) + marginLeft += ml.value(); + if (mr.isFixed()) + marginRight += mr.value(); + margin = marginLeft + marginRight; + + int w = child->minPreferredLogicalWidth() + margin; + m_minPreferredLogicalWidth = max(w, m_minPreferredLogicalWidth); + + // IE ignores tables for calculation of nowrap. Makes some sense. + if (nowrap && !child->isTable()) + m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + + w = child->maxPreferredLogicalWidth() + margin; + + if (!child->isFloating()) { + if (child->isBox() && toRenderBox(child)->avoidsFloats()) { + // Determine a left and right max value based off whether or not the floats can fit in the + // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin + // is smaller than the float width. + int maxLeft = marginLeft > 0 ? max(floatLeftWidth, marginLeft) : floatLeftWidth + marginLeft; + int maxRight = marginRight > 0 ? max(floatRightWidth, marginRight) : floatRightWidth + marginRight; + w = child->maxPreferredLogicalWidth() + maxLeft + maxRight; + w = max(w, floatLeftWidth + floatRightWidth); + } + else + m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); + floatLeftWidth = floatRightWidth = 0; + } + + if (child->isFloating()) { + if (style()->floating() == FLEFT) + floatLeftWidth += w; + else + floatRightWidth += w; + } else + m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + + // A very specific WinIE quirk. + // Example: + /* +

+ */ + // In the above example, the inner absolute positioned block should have a computed width + // of 100px because of the table. + // We can achieve this effect by making the maxwidth of blocks that contain tables + // with percentage widths be infinite (as long as they are not inside a table cell). + if (document()->inQuirksMode() && child->style()->width().isPercent() && + !isTableCell() && child->isTable() && m_maxPreferredLogicalWidth < BLOCK_MAX_WIDTH) { + RenderBlock* cb = containingBlock(); + while (!cb->isRenderView() && !cb->isTableCell()) + cb = cb->containingBlock(); + if (!cb->isTableCell()) + m_maxPreferredLogicalWidth = BLOCK_MAX_WIDTH; + } + + child = child->nextSibling(); + } + + // Always make sure these values are non-negative. + m_minPreferredLogicalWidth = max(0, m_minPreferredLogicalWidth); + m_maxPreferredLogicalWidth = max(0, m_maxPreferredLogicalWidth); + + m_maxPreferredLogicalWidth = max(floatLeftWidth + floatRightWidth, m_maxPreferredLogicalWidth); +} + +bool RenderBlock::hasLineIfEmpty() const +{ + if (!node()) + return false; + + if (node()->isContentEditable() && node()->rootEditableElement() == node()) + return true; + + if (node()->isShadowRoot() && (node()->shadowHost()->hasTagName(inputTag))) + return true; + + return false; +} + +int RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const +{ + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isReplaced() && linePositionMode == PositionOnContainingLine) + return RenderBox::lineHeight(firstLine, direction, linePositionMode); + + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; +} + +int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const +{ + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isReplaced() && linePositionMode == PositionOnContainingLine) { + // For "leaf" theme objects, let the theme decide what the baseline position is. + // FIXME: Might be better to have a custom CSS property instead, so that if the theme + // is turned off, checkboxes/radios will still have decent baselines. + // FIXME: Need to patch form controls to deal with vertical lines. + if (style()->hasAppearance() && !theme()->isControlContainer(style()->appearance())) + return theme()->baselinePosition(this); + + // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in + // the normal flow. We make an exception for marquees, since their baselines are meaningless + // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. + // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled + // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside + // of our content box. + bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollYOffset() != 0) + : (layer()->horizontalScrollbar() || layer()->scrollXOffset() != 0)))) || (isWritingModeRoot() && !isRubyRun()); + + int baselinePos = ignoreBaseline ? -1 : lastLineBoxBaseline(); + + int bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); + if (baselinePos != -1 && baselinePos <= bottomOfContent) + return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos; + + return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); + } + + const Font& f = style(firstLine)->font(); + return f.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - f.height()) / 2; +} + +int RenderBlock::firstLineBoxBaseline() const +{ + if (!isBlockFlow() || (isWritingModeRoot() && !isRubyRun())) + return -1; + + if (childrenInline()) { + if (firstLineBox()) + return firstLineBox()->logicalTop() + style(true)->font().ascent(firstRootBox()->baselineType()); + else + return -1; + } + else { + for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { + if (!curr->isFloatingOrPositioned()) { + int result = curr->firstLineBoxBaseline(); + if (result != -1) + return curr->logicalTop() + result; // Translate to our coordinate space. + } + } + } + + return -1; +} + +int RenderBlock::lastLineBoxBaseline() const +{ + if (!isBlockFlow() || (isWritingModeRoot() && !isRubyRun())) + return -1; + + LineDirectionMode lineDirection = style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine; + + if (childrenInline()) { + if (!firstLineBox() && hasLineIfEmpty()) { + const Font& f = firstLineStyle()->font(); + return f.ascent() + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - f.height()) / 2 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); + } + if (lastLineBox()) + return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->font().ascent(lastRootBox()->baselineType()); + return -1; + } else { + bool haveNormalFlowChild = false; + for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { + if (!curr->isFloatingOrPositioned()) { + haveNormalFlowChild = true; + int result = curr->lastLineBoxBaseline(); + if (result != -1) + return curr->logicalTop() + result; // Translate to our coordinate space. + } + } + if (!haveNormalFlowChild && hasLineIfEmpty()) { + const Font& f = firstLineStyle()->font(); + return f.ascent() + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - f.height()) / 2 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); + } + } + + return -1; +} + +bool RenderBlock::containsNonZeroBidiLevel() const +{ + for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { + for (InlineBox* box = root->firstLeafChild(); box; box = box->nextLeafChild()) { + if (box->bidiLevel()) + return true; + } + } + return false; +} + +RenderBlock* RenderBlock::firstLineBlock() const +{ + RenderBlock* firstLineBlock = const_cast(this); + bool hasPseudo = false; + while (true) { + hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE); + if (hasPseudo) + break; + RenderObject* parentBlock = firstLineBlock->parent(); + if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() || + !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) + break; + ASSERT(parentBlock->isRenderBlock()); + firstLineBlock = toRenderBlock(parentBlock); + } + + if (!hasPseudo) + return 0; + + return firstLineBlock; +} + +static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer) +{ + RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); + // Force inline display (except for floating first-letters). + pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); + // CSS2 says first-letter can't be positioned. + pseudoStyle->setPosition(StaticPosition); + return pseudoStyle; +} + +// CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter +// "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe), +// "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included" +static inline bool isPunctuationForFirstLetter(UChar c) +{ + CharCategory charCategory = category(c); + return charCategory == Punctuation_Open + || charCategory == Punctuation_Close + || charCategory == Punctuation_InitialQuote + || charCategory == Punctuation_FinalQuote + || charCategory == Punctuation_Other; +} + +static inline bool shouldSkipForFirstLetter(UChar c) +{ + return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c); +} + +void RenderBlock::updateFirstLetter() +{ + if (!document()->usesFirstLetterRules()) + return; + // Don't recur + if (style()->styleType() == FIRST_LETTER) + return; + + // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find + // an efficient way to check for that situation though before implementing anything. + RenderObject* firstLetterBlock = this; + bool hasPseudoStyle = false; + while (true) { + // We only honor first-letter if the firstLetterBlock can have children in the DOM. This correctly + // prevents form controls from honoring first-letter. + hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) + && firstLetterBlock->canHaveChildren(); + if (hasPseudoStyle) + break; + RenderObject* parentBlock = firstLetterBlock->parent(); + if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock || + !parentBlock->isBlockFlow()) + break; + firstLetterBlock = parentBlock; + } + + if (!hasPseudoStyle) + return; + + // Drill into inlines looking for our first text child. + RenderObject* currChild = firstLetterBlock->firstChild(); + while (currChild && ((!currChild->isReplaced() && !currChild->isRenderButton() && !currChild->isMenuList()) || currChild->isFloatingOrPositioned()) && !currChild->isText()) { + if (currChild->isFloatingOrPositioned()) { + if (currChild->style()->styleType() == FIRST_LETTER) { + currChild = currChild->firstChild(); + break; + } + currChild = currChild->nextSibling(); + } else + currChild = currChild->firstChild(); + } + + // Get list markers out of the way. + while (currChild && currChild->isListMarker()) + currChild = currChild->nextSibling(); + + if (!currChild) + return; + + // If the child already has style, then it has already been created, so we just want + // to update it. + if (currChild->parent()->style()->styleType() == FIRST_LETTER) { + RenderObject* firstLetter = currChild->parent(); + RenderObject* firstLetterContainer = firstLetter->parent(); + RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); + + if (Node::diff(firstLetter->style(), pseudoStyle) == Node::Detach) { + // The first-letter renderer needs to be replaced. Create a new renderer of the right type. + RenderObject* newFirstLetter; + if (pseudoStyle->display() == INLINE) + newFirstLetter = new (renderArena()) RenderInline(document()); + else + newFirstLetter = new (renderArena()) RenderBlock(document()); + newFirstLetter->setStyle(pseudoStyle); + + // Move the first letter into the new renderer. + view()->disableLayoutState(); + while (RenderObject* child = firstLetter->firstChild()) { + if (child->isText()) + toRenderText(child)->dirtyLineBoxes(true); + firstLetter->removeChild(child); + newFirstLetter->addChild(child, 0); + } + RenderTextFragment* remainingText = toRenderTextFragment(firstLetter->nextSibling()); + ASSERT(remainingText->node()->renderer() == remainingText); + // Replace the old renderer with the new one. + remainingText->setFirstLetter(newFirstLetter); + firstLetter->destroy(); + firstLetter = newFirstLetter; + firstLetterContainer->addChild(firstLetter, remainingText); + view()->enableLayoutState(); + } else + firstLetter->setStyle(pseudoStyle); + + for (RenderObject* genChild = firstLetter->firstChild(); genChild; genChild = genChild->nextSibling()) { + if (genChild->isText()) + genChild->setStyle(pseudoStyle); + } + + return; + } + + if (!currChild->isText() || currChild->isBR()) + return; + + // If the child does not already have style, we create it here. + RenderObject* firstLetterContainer = currChild->parent(); + + // Our layout state is not valid for the repaints we are going to trigger by + // adding and removing children of firstLetterContainer. + view()->disableLayoutState(); + + RenderText* textObj = toRenderText(currChild); + + // Create our pseudo style now that we have our firstLetterContainer determined. + RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); + + RenderObject* firstLetter = 0; + if (pseudoStyle->display() == INLINE) + firstLetter = new (renderArena()) RenderInline(document()); + else + firstLetter = new (renderArena()) RenderBlock(document()); + firstLetter->setStyle(pseudoStyle); + firstLetterContainer->addChild(firstLetter, currChild); + + // The original string is going to be either a generated content string or a DOM node's + // string. We want the original string before it got transformed in case first-letter has + // no text-transform or a different text-transform applied to it. + RefPtr oldText = textObj->originalText(); + ASSERT(oldText); + + if (oldText && oldText->length() > 0) { + unsigned length = 0; + + // Account for leading spaces and punctuation. + while (length < oldText->length() && shouldSkipForFirstLetter((*oldText)[length])) + length++; + + // Account for first letter. + length++; + + // Keep looking for whitespace and allowed punctuation, but avoid + // accumulating just whitespace into the :first-letter. + for (unsigned scanLength = length; scanLength < oldText->length(); ++scanLength) { + UChar c = (*oldText)[scanLength]; + + if (!shouldSkipForFirstLetter(c)) + break; + + if (isPunctuationForFirstLetter(c)) + length = scanLength + 1; + } + + // Construct a text fragment for the text after the first letter. + // This text fragment might be empty. + RenderTextFragment* remainingText = + new (renderArena()) RenderTextFragment(textObj->node() ? textObj->node() : textObj->document(), oldText.get(), length, oldText->length() - length); + remainingText->setStyle(textObj->style()); + if (remainingText->node()) + remainingText->node()->setRenderer(remainingText); + + RenderObject* nextObj = textObj->nextSibling(); + firstLetterContainer->removeChild(textObj); + firstLetterContainer->addChild(remainingText, nextObj); + remainingText->setFirstLetter(firstLetter); + + // construct text fragment for the first letter + RenderTextFragment* letter = + new (renderArena()) RenderTextFragment(remainingText->node() ? remainingText->node() : remainingText->document(), oldText.get(), 0, length); + letter->setStyle(pseudoStyle); + firstLetter->addChild(letter); + + textObj->destroy(); + } + view()->enableLayoutState(); +} + +// Helper methods for obtaining the last line, computing line counts and heights for line counts +// (crawling into blocks). +static bool shouldCheckLines(RenderObject* obj) +{ + return !obj->isFloatingOrPositioned() && !obj->isRunIn() && + obj->isBlockFlow() && obj->style()->height().isAuto() && + (!obj->isFlexibleBox() || obj->style()->boxOrient() == VERTICAL); +} + +static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) +{ + if (block->style()->visibility() == VISIBLE) { + if (block->childrenInline()) { + for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { + if (count++ == i) + return box; + } + } + else { + for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { + if (shouldCheckLines(obj)) { + RootInlineBox *box = getLineAtIndex(toRenderBlock(obj), i, count); + if (box) + return box; + } + } + } + } + return 0; +} + +static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count) +{ + if (block->style()->visibility() == VISIBLE) { + if (block->childrenInline()) { + for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { + if (++count == l) + return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); + } + } + else { + RenderBox* normalFlowChildWithoutLines = 0; + for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) { + if (shouldCheckLines(obj)) { + int result = getHeightForLineCount(toRenderBlock(obj), l, false, count); + if (result != -1) + return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); + } + else if (!obj->isFloatingOrPositioned() && !obj->isRunIn()) + normalFlowChildWithoutLines = obj; + } + if (normalFlowChildWithoutLines && l == 0) + return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height(); + } + } + + return -1; +} + +RootInlineBox* RenderBlock::lineAtIndex(int i) +{ + int count = 0; + return getLineAtIndex(this, i, count); +} + +int RenderBlock::lineCount() +{ + int count = 0; + if (style()->visibility() == VISIBLE) { + if (childrenInline()) + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + count++; + else + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) + if (shouldCheckLines(obj)) + count += toRenderBlock(obj)->lineCount(); + } + return count; +} + +int RenderBlock::heightForLineCount(int l) +{ + int count = 0; + return getHeightForLineCount(this, l, true, count); +} + +void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const +{ + // We don't deal with relative positioning. Our assumption is that you shrink to fit the lines without accounting + // for either overflow or translations via relative positioning. + if (style()->visibility() == VISIBLE) { + if (childrenInline()) { + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { + if (box->firstChild()) + left = min(left, x + box->firstChild()->x()); + if (box->lastChild()) + right = max(right, x + box->lastChild()->x() + box->lastChild()->logicalWidth()); + } + } + else { + for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) { + if (!obj->isFloatingOrPositioned()) { + if (obj->isBlockFlow() && !obj->hasOverflowClip()) + toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right); + else if (obj->style()->visibility() == VISIBLE) { + // We are a replaced element or some kind of non-block-flow object. + left = min(left, x + obj->x()); + right = max(right, x + obj->x() + obj->width()); + } + } + } + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + // Only examine the object if our m_shouldPaint flag is set. + if (r->m_shouldPaint) { + int floatLeft = r->left() - r->m_renderer->x() + r->m_renderer->marginLeft(); + int floatRight = floatLeft + r->m_renderer->width(); + left = min(left, floatLeft); + right = max(right, floatRight); + } + } + } + } +} + +void RenderBlock::borderFitAdjust(int& x, int& w) const +{ + if (style()->borderFit() == BorderFitBorder) + return; + + // Walk any normal flow lines to snugly fit. + int left = INT_MAX; + int right = INT_MIN; + int oldWidth = w; + adjustForBorderFit(0, left, right); + if (left != INT_MAX) { + left -= (borderLeft() + paddingLeft()); + if (left > 0) { + x += left; + w -= left; + } + } + if (right != INT_MIN) { + right += (borderRight() + paddingRight()); + if (right < oldWidth) + w -= (oldWidth - right); + } +} + +void RenderBlock::clearTruncation() +{ + if (style()->visibility() == VISIBLE) { + if (childrenInline() && hasMarkupTruncation()) { + setHasMarkupTruncation(false); + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + box->clearTruncation(); + } + else + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) + if (shouldCheckLines(obj)) + toRenderBlock(obj)->clearTruncation(); + } +} + +void RenderBlock::setMaxMarginBeforeValues(int pos, int neg) +{ + if (!m_rareData) { + if (pos == RenderBlockRareData::positiveMarginBeforeDefault(this) && neg == RenderBlockRareData::negativeMarginBeforeDefault(this)) + return; + m_rareData = new RenderBlockRareData(this); + } + m_rareData->m_margins.setPositiveMarginBefore(pos); + m_rareData->m_margins.setNegativeMarginBefore(neg); +} + +void RenderBlock::setMaxMarginAfterValues(int pos, int neg) +{ + if (!m_rareData) { + if (pos == RenderBlockRareData::positiveMarginAfterDefault(this) && neg == RenderBlockRareData::negativeMarginAfterDefault(this)) + return; + m_rareData = new RenderBlockRareData(this); + } + m_rareData->m_margins.setPositiveMarginAfter(pos); + m_rareData->m_margins.setNegativeMarginAfter(neg); +} + +void RenderBlock::setPaginationStrut(int strut) +{ + if (!m_rareData) { + if (!strut) + return; + m_rareData = new RenderBlockRareData(this); + } + m_rareData->m_paginationStrut = strut; +} + +void RenderBlock::setPageLogicalOffset(int logicalOffset) +{ + if (!m_rareData) { + if (!logicalOffset) + return; + m_rareData = new RenderBlockRareData(this); + } + m_rareData->m_pageLogicalOffset = logicalOffset; +} + +void RenderBlock::absoluteRects(Vector& rects, int tx, int ty) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (isAnonymousBlockContinuation()) { + // FIXME: This is wrong for block-flows that are horizontal. + // https://bugs.webkit.org/show_bug.cgi?id=46781 + rects.append(IntRect(tx, ty - collapsedMarginBefore(), + width(), height() + collapsedMarginBefore() + collapsedMarginAfter())); + continuation()->absoluteRects(rects, + tx - x() + inlineElementContinuation()->containingBlock()->x(), + ty - y() + inlineElementContinuation()->containingBlock()->y()); + } else + rects.append(IntRect(tx, ty, width(), height())); +} + +void RenderBlock::absoluteQuads(Vector& quads) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (isAnonymousBlockContinuation()) { + // FIXME: This is wrong for block-flows that are horizontal. + // https://bugs.webkit.org/show_bug.cgi?id=46781 + FloatRect localRect(0, -collapsedMarginBefore(), + width(), height() + collapsedMarginBefore() + collapsedMarginAfter()); + quads.append(localToAbsoluteQuad(localRect)); + continuation()->absoluteQuads(quads); + } else + quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width(), height()))); +} + +IntRect RenderBlock::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) +{ + IntRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + if (isAnonymousBlockContinuation()) + r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal. + return r; +} + +RenderObject* RenderBlock::hoverAncestor() const +{ + return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor(); +} + +void RenderBlock::updateDragState(bool dragOn) +{ + RenderBox::updateDragState(dragOn); + if (continuation()) + continuation()->updateDragState(dragOn); +} + +RenderStyle* RenderBlock::outlineStyleForRepaint() const +{ + return isAnonymousBlockContinuation() ? continuation()->style() : style(); +} + +void RenderBlock::childBecameNonInline(RenderObject*) +{ + makeChildrenNonInline(); + if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) + toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); + // |this| may be dead here +} + +void RenderBlock::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* n = node(); + if (isAnonymousBlockContinuation()) + // We are in the margins of block elements that are part of a continuation. In + // this case we're actually still inside the enclosing element that was + // split. Go ahead and set our inner node accordingly. + n = continuation()->node(); + + if (n) { + result.setInnerNode(n); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(n); + result.setLocalPoint(point); + } +} + +IntRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + // Do the normal calculation in most cases. + if (firstChild()) + return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); + + // This is a special case: + // The element is not an inline element, and it's empty. So we have to + // calculate a fake position to indicate where objects are to be inserted. + + // FIXME: This does not take into account either :first-line or :first-letter + // However, as soon as some content is entered, the line boxes will be + // constructed and this kludge is not called any more. So only the caret size + // of an empty :first-line'd block is wrong. I think we can live with that. + RenderStyle* currentStyle = firstLineStyle(); + int height = lineHeight(true, currentStyle->isHorizontalWritingMode() ? HorizontalLine : VerticalLine); + + enum CaretAlignment { alignLeft, alignRight, alignCenter }; + + CaretAlignment alignment = alignLeft; + + switch (currentStyle->textAlign()) { + case TAAUTO: + case JUSTIFY: + if (!currentStyle->isLeftToRightDirection()) + alignment = alignRight; + break; + case LEFT: + case WEBKIT_LEFT: + break; + case CENTER: + case WEBKIT_CENTER: + alignment = alignCenter; + break; + case RIGHT: + case WEBKIT_RIGHT: + alignment = alignRight; + break; + } + + int x = borderLeft() + paddingLeft(); + int w = width(); + + switch (alignment) { + case alignLeft: + break; + case alignCenter: + x = (x + w - (borderRight() + paddingRight())) / 2; + break; + case alignRight: + x = w - (borderRight() + paddingRight()) - caretWidth; + break; + } + + if (extraWidthToEndOfLine) { + if (isRenderBlock()) { + *extraWidthToEndOfLine = w - (x + caretWidth); + } else { + // FIXME: This code looks wrong. + // myRight and containerRight are set up, but then clobbered. + // So *extraWidthToEndOfLine will always be 0 here. + + int myRight = x + caretWidth; + // FIXME: why call localToAbsoluteForContent() twice here, too? + FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight, 0)); + + int containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent(); + FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight, 0)); + + *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); + } + } + + int y = paddingTop() + borderTop(); + + return IntRect(x, y, caretWidth, height); +} + +void RenderBlock::addFocusRingRects(Vector& rects, int tx, int ty) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (inlineElementContinuation()) { + // FIXME: This check really isn't accurate. + bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox(); + // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. + // FIXME: This is wrong for block-flows that are horizontal. + // https://bugs.webkit.org/show_bug.cgi?id=46781 + bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox(); + int topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : 0; + int bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : 0; + IntRect rect(tx, ty - topMargin, width(), height() + topMargin + bottomMargin); + if (!rect.isEmpty()) + rects.append(rect); + } else if (width() && height()) + rects.append(IntRect(tx, ty, width(), height())); + + if (!hasOverflowClip() && !hasControlClip()) { + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + int top = max(curr->lineTop(), curr->y()); + int bottom = min(curr->lineBottom(), curr->y() + curr->logicalHeight()); + IntRect rect(tx + curr->x(), ty + top, curr->logicalWidth(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { + RenderBox* box = toRenderBox(curr); + FloatPoint pos; + // FIXME: This doesn't work correctly with transforms. + if (box->layer()) + pos = curr->localToAbsolute(); + else + pos = FloatPoint(tx + box->x(), ty + box->y()); + box->addFocusRingRects(rects, pos.x(), pos.y()); + } + } + } + + if (inlineElementContinuation()) + inlineElementContinuation()->addFocusRingRects(rects, + tx - x() + inlineElementContinuation()->containingBlock()->x(), + ty - y() + inlineElementContinuation()->containingBlock()->y()); +} + +RenderBlock* RenderBlock::createAnonymousBlock(bool isFlexibleBox) const +{ + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + + RenderBlock* newBox = 0; + if (isFlexibleBox) { + newStyle->setDisplay(BOX); + newBox = new (renderArena()) RenderFlexibleBox(document() /* anonymous box */); + } else { + newStyle->setDisplay(BLOCK); + newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + } + + newBox->setStyle(newStyle.release()); + return newBox; +} + +RenderBlock* RenderBlock::createAnonymousBlockWithSameTypeAs(RenderBlock* otherAnonymousBlock) const +{ + if (otherAnonymousBlock->isAnonymousColumnsBlock()) + return createAnonymousColumnsBlock(); + if (otherAnonymousBlock->isAnonymousColumnSpanBlock()) + return createAnonymousColumnSpanBlock(); + return createAnonymousBlock(otherAnonymousBlock->style()->display() == BOX); +} + +RenderBlock* RenderBlock::createAnonymousColumnsBlock() const +{ + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->inheritColumnPropertiesFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + return newBox; +} + +RenderBlock* RenderBlock::createAnonymousColumnSpanBlock() const +{ + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setColumnSpan(true); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + return newBox; +} + +int RenderBlock::nextPageTop(int yPos) const +{ + LayoutState* layoutState = view()->layoutState(); + if (!layoutState->m_pageLogicalHeight) + return yPos; + + // The yPos is in our coordinate space. We can add in our pushed offset. + int pageLogicalHeight = layoutState->m_pageLogicalHeight; + int remainingHeight = (pageLogicalHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageLogicalHeight) % pageLogicalHeight; + return yPos + remainingHeight; +} + +static bool inNormalFlow(RenderBox* child) +{ + RenderBlock* curr = child->containingBlock(); + RenderBlock* initialBlock = child->view(); + while (curr && curr != initialBlock) { + if (curr->hasColumns()) + return true; + if (curr->isFloatingOrPositioned()) + return false; + curr = curr->containingBlock(); + } + return true; +} + +int RenderBlock::applyBeforeBreak(RenderBox* child, int yPos) +{ + // FIXME: Add page break checking here when we support printing. + bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); + bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. + bool checkBeforeAlways = (checkColumnBreaks && child->style()->columnBreakBefore() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakBefore() == PBALWAYS); + if (checkBeforeAlways && inNormalFlow(child)) { + if (checkColumnBreaks) + view()->layoutState()->addForcedColumnBreak(yPos); + return nextPageTop(yPos); + } + return yPos; +} + +int RenderBlock::applyAfterBreak(RenderBox* child, int yPos, MarginInfo& marginInfo) +{ + // FIXME: Add page break checking here when we support printing. + bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns(); + bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->m_pageLogicalHeight; // FIXME: Once columns can print we have to check this. + bool checkAfterAlways = (checkColumnBreaks && child->style()->columnBreakAfter() == PBALWAYS) || (checkPageBreaks && child->style()->pageBreakAfter() == PBALWAYS); + if (checkAfterAlways && inNormalFlow(child)) { + marginInfo.setMarginAfterQuirk(true); // Cause margins to be discarded for any following content. + if (checkColumnBreaks) + view()->layoutState()->addForcedColumnBreak(yPos); + return nextPageTop(yPos); + } + return yPos; +} + +int RenderBlock::adjustForUnsplittableChild(RenderBox* child, int yPos, bool includeMargins) +{ + bool isUnsplittable = child->isReplaced() || child->scrollsOverflow(); + if (!isUnsplittable) + return yPos; + int childHeight = child->height() + (includeMargins ? child->marginTop() + child->marginBottom() : 0); + LayoutState* layoutState = view()->layoutState(); + if (layoutState->m_columnInfo) + layoutState->m_columnInfo->updateMinimumColumnHeight(childHeight); + int pageLogicalHeight = layoutState->m_pageLogicalHeight; + if (!pageLogicalHeight || childHeight > pageLogicalHeight) + return yPos; + int remainingHeight = (pageLogicalHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageLogicalHeight) % pageLogicalHeight; + if (remainingHeight < childHeight) + return yPos + remainingHeight; + return yPos; +} + +void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, int& delta) +{ + // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we + // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since + // the line on the top of the next page will appear too far down relative to the same kind of line at the top + // of the first column. + // + // The rendering we would like to see is one where the lineTop is at the top of the column, and any line overflow + // simply spills out above the top of the column. This effect would match what happens at the top of the first column. + // We can't achieve this rendering, however, until we stop columns from clipping to the column bounds (thus allowing + // for overflow to occur), and then cache visible overflow for each column rect. + // + // Furthermore, the paint we have to do when a column has overflow has to be special. We need to exclude + // content that paints in a previous column (and content that paints in the following column). + // + // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats). + // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the + // line and all following lines. + LayoutState* layoutState = view()->layoutState(); + int pageLogicalHeight = layoutState->m_pageLogicalHeight; + int yPos = lineBox->topVisualOverflow(); + int lineHeight = lineBox->bottomVisualOverflow() - yPos; + if (layoutState->m_columnInfo) + layoutState->m_columnInfo->updateMinimumColumnHeight(lineHeight); + yPos += delta; + lineBox->setPaginationStrut(0); + if (!pageLogicalHeight || lineHeight > pageLogicalHeight) + return; + int remainingHeight = pageLogicalHeight - ((layoutState->m_layoutOffset - layoutState->m_pageOffset).height() + yPos) % pageLogicalHeight; + if (remainingHeight < lineHeight) { + int totalHeight = lineHeight + max(0, yPos); + if (lineBox == firstRootBox() && totalHeight < pageLogicalHeight && !isPositioned() && !isTableCell()) + setPaginationStrut(remainingHeight + max(0, yPos)); + else { + delta += remainingHeight; + lineBox->setPaginationStrut(remainingHeight); + } + } +} + +int RenderBlock::collapsedMarginBeforeForChild(RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // collapsed margin. + if (!child->isWritingModeRoot()) + return child->collapsedMarginBefore(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the collapsed margin for the opposite edge. + if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) + return child->collapsedMarginAfter(); + + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" sides of the child box. We can just return the raw margin in this case. + return marginBeforeForChild(child); +} + +int RenderBlock::collapsedMarginAfterForChild(RenderBox* child) const +{ + // If the child has the same directionality as we do, then we can just return its + // collapsed margin. + if (!child->isWritingModeRoot()) + return child->collapsedMarginAfter(); + + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the collapsed margin for the opposite edge. + if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) + return child->collapsedMarginBefore(); + + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" side of the child box. We can just return the raw margin in this case. + return marginAfterForChild(child); +} + +int RenderBlock::marginBeforeForChild(RenderBoxModelObject* child) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return child->marginTop(); + case BottomToTopWritingMode: + return child->marginBottom(); + case LeftToRightWritingMode: + return child->marginLeft(); + case RightToLeftWritingMode: + return child->marginRight(); + } + ASSERT_NOT_REACHED(); + return child->marginTop(); +} + +int RenderBlock::marginAfterForChild(RenderBoxModelObject* child) const +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + return child->marginBottom(); + case BottomToTopWritingMode: + return child->marginTop(); + case LeftToRightWritingMode: + return child->marginRight(); + case RightToLeftWritingMode: + return child->marginLeft(); + } + ASSERT_NOT_REACHED(); + return child->marginBottom(); +} + +int RenderBlock::marginStartForChild(RenderBoxModelObject* child) const +{ + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? child->marginLeft() : child->marginRight(); + return style()->isLeftToRightDirection() ? child->marginTop() : child->marginBottom(); +} + +int RenderBlock::marginEndForChild(RenderBoxModelObject* child) const +{ + if (style()->isHorizontalWritingMode()) + return style()->isLeftToRightDirection() ? child->marginRight() : child->marginLeft(); + return style()->isLeftToRightDirection() ? child->marginBottom() : child->marginTop(); +} + +void RenderBlock::setMarginStartForChild(RenderBox* child, int margin) +{ + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) + child->setMarginLeft(margin); + else + child->setMarginRight(margin); + } else { + if (style()->isLeftToRightDirection()) + child->setMarginTop(margin); + else + child->setMarginBottom(margin); + } +} + +void RenderBlock::setMarginEndForChild(RenderBox* child, int margin) +{ + if (style()->isHorizontalWritingMode()) { + if (style()->isLeftToRightDirection()) + child->setMarginRight(margin); + else + child->setMarginLeft(margin); + } else { + if (style()->isLeftToRightDirection()) + child->setMarginBottom(margin); + else + child->setMarginTop(margin); + } +} + +void RenderBlock::setMarginBeforeForChild(RenderBox* child, int margin) +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + child->setMarginTop(margin); + break; + case BottomToTopWritingMode: + child->setMarginBottom(margin); + break; + case LeftToRightWritingMode: + child->setMarginLeft(margin); + break; + case RightToLeftWritingMode: + child->setMarginRight(margin); + break; + } +} + +void RenderBlock::setMarginAfterForChild(RenderBox* child, int margin) +{ + switch (style()->writingMode()) { + case TopToBottomWritingMode: + child->setMarginBottom(margin); + break; + case BottomToTopWritingMode: + child->setMarginTop(margin); + break; + case LeftToRightWritingMode: + child->setMarginRight(margin); + break; + case RightToLeftWritingMode: + child->setMarginLeft(margin); + break; + } +} + +RenderBlock::MarginValues RenderBlock::marginValuesForChild(RenderBox* child) +{ + int childBeforePositive = 0; + int childBeforeNegative = 0; + int childAfterPositive = 0; + int childAfterNegative = 0; + + int beforeMargin = 0; + int afterMargin = 0; + + RenderBlock* childRenderBlock = child->isRenderBlock() ? toRenderBlock(child) : 0; + + // If the child has the same directionality as we do, then we can just return its + // margins in the same direction. + if (!child->isWritingModeRoot()) { + if (childRenderBlock) { + childBeforePositive = childRenderBlock->maxPositiveMarginBefore(); + childBeforeNegative = childRenderBlock->maxNegativeMarginBefore(); + childAfterPositive = childRenderBlock->maxPositiveMarginAfter(); + childAfterNegative = childRenderBlock->maxNegativeMarginAfter(); + } else { + beforeMargin = child->marginBefore(); + afterMargin = child->marginAfter(); + } + } else if (child->style()->isHorizontalWritingMode() == style()->isHorizontalWritingMode()) { + // The child has a different directionality. If the child is parallel, then it's just + // flipped relative to us. We can use the margins for the opposite edges. + if (childRenderBlock) { + childBeforePositive = childRenderBlock->maxPositiveMarginAfter(); + childBeforeNegative = childRenderBlock->maxNegativeMarginAfter(); + childAfterPositive = childRenderBlock->maxPositiveMarginBefore(); + childAfterNegative = childRenderBlock->maxNegativeMarginBefore(); + } else { + beforeMargin = child->marginAfter(); + afterMargin = child->marginBefore(); + } + } else { + // The child is perpendicular to us, which means its margins don't collapse but are on the + // "logical left/right" sides of the child box. We can just return the raw margin in this case. + beforeMargin = marginBeforeForChild(child); + afterMargin = marginAfterForChild(child); + } + + // Resolve uncollapsing margins into their positive/negative buckets. + if (beforeMargin) { + if (beforeMargin > 0) + childBeforePositive = beforeMargin; + else + childBeforeNegative = -beforeMargin; + } + if (afterMargin) { + if (afterMargin > 0) + childAfterPositive = afterMargin; + else + childAfterNegative = -afterMargin; + } + + return MarginValues(childBeforePositive, childBeforeNegative, childAfterPositive, childAfterNegative); +} + +const char* RenderBlock::renderName() const +{ + if (isBody()) + return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass. + + if (isFloating()) + return "RenderBlock (floating)"; + if (isPositioned()) + return "RenderBlock (positioned)"; + if (isAnonymousColumnsBlock()) + return "RenderBlock (anonymous multi-column)"; + if (isAnonymousColumnSpanBlock()) + return "RenderBlock (anonymous multi-column span)"; + if (isAnonymousBlock()) + return "RenderBlock (anonymous)"; + else if (isAnonymous()) + return "RenderBlock (generated)"; + if (isRelPositioned()) + return "RenderBlock (relative positioned)"; + if (isRunIn()) + return "RenderBlock (run-in)"; + return "RenderBlock"; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderBlock.h b/Source/WebCore/rendering/RenderBlock.h new file mode 100644 index 0000000..bd8be2c --- /dev/null +++ b/Source/WebCore/rendering/RenderBlock.h @@ -0,0 +1,739 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef RenderBlock_h +#define RenderBlock_h + +#include "DeprecatedPtrList.h" +#include "GapRects.h" +#include "RenderBox.h" +#include "RenderLineBoxList.h" +#include "RootInlineBox.h" +#include + +namespace WebCore { + +class ColumnInfo; +class InlineIterator; +class LayoutStateMaintainer; +class RenderInline; + +struct BidiRun; + +template class BidiResolver; +template struct MidpointState; +typedef BidiResolver InlineBidiResolver; +typedef MidpointState LineMidpointState; + +enum CaretType { CursorCaret, DragCaret }; + +class RenderBlock : public RenderBox { +public: + RenderBlock(Node*); + virtual ~RenderBlock(); + + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + virtual void destroy(); + + // These two functions are overridden for inline-block. + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + + void deleteLineBoxTree(); + + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + + virtual void layoutBlock(bool relayoutChildren, int pageLogicalHeight = 0); + + void insertPositionedObject(RenderBox*); + void removePositionedObject(RenderBox*); + void removePositionedObjects(RenderBlock*); + + typedef ListHashSet PositionedObjectsListHashSet; + PositionedObjectsListHashSet* positionedObjects() const { return m_positionedObjects; } + + void addPercentHeightDescendant(RenderBox*); + static void removePercentHeightDescendant(RenderBox*); + HashSet* percentHeightDescendants() const; + + RootInlineBox* createAndAppendRootInlineBox(); + + bool generatesLineBoxesForInlineChild(RenderObject*, bool isLineEmpty = true, bool previousLineBrokeCleanly = true); + + void markAllDescendantsWithFloatsForLayout(RenderBox* floatToRemove = 0, bool inLayout = true); + void markPositionedObjectsForLayout(); + virtual void markForPaginationRelayoutIfNeeded(); + + bool containsFloats() { return m_floatingObjects && !m_floatingObjects->isEmpty(); } + bool containsFloat(RenderObject*); + + int availableLogicalWidthForLine(int position, bool firstLine) const; + int logicalRightOffsetForLine(int position, bool firstLine) const { return logicalRightOffsetForLine(position, logicalRightOffsetForContent(), firstLine); } + int logicalLeftOffsetForLine(int position, bool firstLine) const { return logicalLeftOffsetForLine(position, logicalLeftOffsetForContent(), firstLine); } + + virtual VisiblePosition positionForPoint(const IntPoint&); + + // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) + virtual int availableLogicalWidth() const; + + RootInlineBox* firstRootBox() const { return static_cast(firstLineBox()); } + RootInlineBox* lastRootBox() const { return static_cast(lastLineBox()); } + + bool containsNonZeroBidiLevel() const; + + GapRects selectionGapRectsForRepaint(RenderBoxModelObject* repaintContainer); + IntRect logicalLeftSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + RenderObject* selObj, int logicalLeft, int logicalTop, int logicalHeight, const PaintInfo*); + IntRect logicalRightSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + RenderObject* selObj, int logicalRight, int logicalTop, int logicalHeight, const PaintInfo*); + void getSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); + IntRect logicalRectToPhysicalRect(const IntPoint& physicalPosition, const IntRect& logicalRect); + + // Helper methods for computing line counts and heights for line counts. + RootInlineBox* lineAtIndex(int); + int lineCount(); + int heightForLineCount(int); + void clearTruncation(); + + void adjustRectForColumns(IntRect&) const; + virtual void adjustForColumns(IntSize&, const IntPoint&) const; + + void addContinuationWithOutline(RenderInline*); + + virtual RenderBoxModelObject* virtualContinuation() const { return continuation(); } + bool isAnonymousBlockContinuation() const { return continuation() && isAnonymousBlock(); } + RenderInline* inlineElementContinuation() const; + RenderBlock* blockElementContinuation() const; + + using RenderBoxModelObject::continuation; + using RenderBoxModelObject::setContinuation; + + // This function is a convenience helper for creating an anonymous block that inherits its + // style from this RenderBlock. + RenderBlock* createAnonymousBlock(bool isFlexibleBox = false) const; + RenderBlock* createAnonymousColumnsBlock() const; + RenderBlock* createAnonymousColumnSpanBlock() const; + RenderBlock* createAnonymousBlockWithSameTypeAs(RenderBlock* otherAnonymousBlock) const; + + static void appendRunsForObject(int start, int end, RenderObject*, InlineBidiResolver&); + static bool requiresLineBox(const InlineIterator&, bool isLineEmpty = true, bool previousLineBrokeCleanly = true); + + ColumnInfo* columnInfo() const; + int columnGap() const; + + // These two functions take the ColumnInfo* to avoid repeated lookups of the info in the global HashMap. + unsigned columnCount(ColumnInfo*) const; + IntRect columnRectAt(ColumnInfo*, unsigned) const; + + int paginationStrut() const { return m_rareData ? m_rareData->m_paginationStrut : 0; } + void setPaginationStrut(int); + + // The page logical offset is the object's offset from the top of the page in the page progression + // direction (so an x-offset in vertical text and a y-offset for horizontal text). + int pageLogicalOffset() const { return m_rareData ? m_rareData->m_pageLogicalOffset : 0; } + void setPageLogicalOffset(int); + + // Accessors for logical width/height and margins in the containing block's block-flow direction. + enum ApplyLayoutDeltaMode { ApplyLayoutDelta, DoNotApplyLayoutDelta }; + int logicalWidthForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->width() : child->height(); } + int logicalHeightForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->height() : child->width(); } + int logicalTopForChild(RenderBox* child) { return style()->isHorizontalWritingMode() ? child->y() : child->x(); } + void setLogicalLeftForChild(RenderBox* child, int logicalLeft, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); + void setLogicalTopForChild(RenderBox* child, int logicalTop, ApplyLayoutDeltaMode = DoNotApplyLayoutDelta); + int marginBeforeForChild(RenderBoxModelObject* child) const; + int marginAfterForChild(RenderBoxModelObject* child) const; + int marginStartForChild(RenderBoxModelObject* child) const; + int marginEndForChild(RenderBoxModelObject* child) const; + void setMarginStartForChild(RenderBox* child, int); + void setMarginEndForChild(RenderBox* child, int); + void setMarginBeforeForChild(RenderBox* child, int); + void setMarginAfterForChild(RenderBox* child, int); + int collapsedMarginBeforeForChild(RenderBox* child) const; + int collapsedMarginAfterForChild(RenderBox* child) const; + + virtual void updateFirstLetter(); + + class MarginValues { + public: + MarginValues(int beforePos, int beforeNeg, int afterPos, int afterNeg) + : m_positiveMarginBefore(beforePos) + , m_negativeMarginBefore(beforeNeg) + , m_positiveMarginAfter(afterPos) + , m_negativeMarginAfter(afterNeg) + { } + + int positiveMarginBefore() const { return m_positiveMarginBefore; } + int negativeMarginBefore() const { return m_negativeMarginBefore; } + int positiveMarginAfter() const { return m_positiveMarginAfter; } + int negativeMarginAfter() const { return m_negativeMarginAfter; } + + void setPositiveMarginBefore(int pos) { m_positiveMarginBefore = pos; } + void setNegativeMarginBefore(int neg) { m_negativeMarginBefore = neg; } + void setPositiveMarginAfter(int pos) { m_positiveMarginAfter = pos; } + void setNegativeMarginAfter(int neg) { m_negativeMarginAfter = neg; } + + private: + int m_positiveMarginBefore; + int m_negativeMarginBefore; + int m_positiveMarginAfter; + int m_negativeMarginAfter; + }; + MarginValues marginValuesForChild(RenderBox* child); + + virtual void scrollbarsChanged(bool /*horizontalScrollbarChanged*/, bool /*verticalScrollbarChanged*/) { }; + +protected: + // These functions are only used internally to manipulate the render tree structure via remove/insert/appendChildNode. + // Since they are typically called only to move objects around within anonymous blocks (which only have layers in + // the case of column spans), the default for fullRemoveInsert is false rather than true. + void moveChildTo(RenderBlock* to, RenderObject* child, bool fullRemoveInsert = false) + { + return moveChildTo(to, child, 0, fullRemoveInsert); + } + void moveChildTo(RenderBlock* to, RenderObject* child, RenderObject* beforeChild, bool fullRemoveInsert = false); + void moveAllChildrenTo(RenderBlock* to, bool fullRemoveInsert = false) + { + return moveAllChildrenTo(to, 0, fullRemoveInsert); + } + void moveAllChildrenTo(RenderBlock* to, RenderObject* beforeChild, bool fullRemoveInsert = false) + { + return moveChildrenTo(to, firstChild(), 0, beforeChild, fullRemoveInsert); + } + // Move all of the kids from |startChild| up to but excluding |endChild|. 0 can be passed as the endChild to denote + // that all the kids from |startChild| onwards should be added. + void moveChildrenTo(RenderBlock* to, RenderObject* startChild, RenderObject* endChild, bool fullRemoveInsert = false) + { + return moveChildrenTo(to, startChild, endChild, 0, fullRemoveInsert); + } + void moveChildrenTo(RenderBlock* to, RenderObject* startChild, RenderObject* endChild, RenderObject* beforeChild, bool fullRemoveInsert = false); + + int maxPositiveMarginBefore() const { return m_rareData ? m_rareData->m_margins.positiveMarginBefore() : RenderBlockRareData::positiveMarginBeforeDefault(this); } + int maxNegativeMarginBefore() const { return m_rareData ? m_rareData->m_margins.negativeMarginBefore() : RenderBlockRareData::negativeMarginBeforeDefault(this); } + int maxPositiveMarginAfter() const { return m_rareData ? m_rareData->m_margins.positiveMarginAfter() : RenderBlockRareData::positiveMarginAfterDefault(this); } + int maxNegativeMarginAfter() const { return m_rareData ? m_rareData->m_margins.negativeMarginAfter() : RenderBlockRareData::negativeMarginAfterDefault(this); } + + void setMaxMarginBeforeValues(int pos, int neg); + void setMaxMarginAfterValues(int pos, int neg); + + void initMaxMarginValues() + { + if (m_rareData) { + m_rareData->m_margins = MarginValues(RenderBlockRareData::positiveMarginBeforeDefault(this) , RenderBlockRareData::negativeMarginBeforeDefault(this), + RenderBlockRareData::positiveMarginAfterDefault(this), RenderBlockRareData::negativeMarginAfterDefault(this)); + m_rareData->m_paginationStrut = 0; + } + } + + virtual void layout(); + + void layoutPositionedObjects(bool relayoutChildren); + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintObject(PaintInfo&, int tx, int ty); + + int logicalRightOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() + availableLogicalWidth() : borderTop() + paddingTop() + availableLogicalWidth(); } + int logicalLeftOffsetForContent() const { return style()->isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); } + int logicalRightOffsetForLine(int position, int fixedOffset, bool applyTextIndent = true, int* logicalHeightRemaining = 0) const; + int logicalLeftOffsetForLine(int position, int fixedOffset, bool applyTextIndent = true, int* logicalHeightRemaining = 0) const; + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual void computePreferredLogicalWidths(); + + virtual int firstLineBoxBaseline() const; + virtual int lastLineBoxBaseline() const; + + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); + + // Delay update scrollbar until finishDelayRepaint() will be + // called. This function is used when a flexbox is laying out its + // descendant. If multiple calls are made to startDelayRepaint(), + // finishDelayRepaint() will do nothing until finishDelayRepaint() + // is called the same number of times. + static void startDelayUpdateScrollInfo(); + static void finishDelayUpdateScrollInfo(); + + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual bool hasLineIfEmpty() const; + bool layoutOnlyPositionedObjects(); + + void computeOverflow(int oldClientAfterEdge, bool recomputeFloats = false); + virtual void addOverflowFromChildren(); + void addOverflowFromFloats(); + void addOverflowFromPositionedObjects(); + void addOverflowFromBlockChildren(); + void addOverflowFromInlineChildren(); + +#if ENABLE(SVG) +protected: + + // Only used by RenderSVGText, which explicitely overrides RenderBlock::layoutBlock(), do NOT use for anything else. + void forceLayoutInlineChildren() + { + int repaintLogicalTop = 0; + int repaintLogicalBottom = 0; + layoutInlineChildren(true, repaintLogicalTop, repaintLogicalBottom); + } +#endif + +private: + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + + virtual const char* renderName() const; + + virtual bool isRenderBlock() const { return true; } + virtual bool isBlockFlow() const { return (!isInline() || isReplaced()) && !isTable(); } + virtual bool isInlineBlockOrInlineTable() const { return isInline() && isReplaced(); } + + void makeChildrenNonInline(RenderObject* insertionPoint = 0); + virtual void removeLeftoverAnonymousBlock(RenderBlock* child); + + virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } + + void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild); + void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild); + void addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild); + virtual void addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild = 0); + + virtual bool isSelfCollapsingBlock() const; + + virtual int collapsedMarginBefore() const { return maxPositiveMarginBefore() - maxNegativeMarginBefore(); } + virtual int collapsedMarginAfter() const { return maxPositiveMarginAfter() - maxNegativeMarginAfter(); } + + virtual void repaintOverhangingFloats(bool paintAllDescendants); + + void layoutBlockChildren(bool relayoutChildren, int& maxFloatLogicalBottom); + void layoutInlineChildren(bool relayoutChildren, int& repaintLogicalTop, int& repaintLogicalBottom); + + virtual void borderFitAdjust(int& x, int& w) const; // Shrink the box in which the border paints if border-fit is set. + + virtual void updateBeforeAfterContent(PseudoId); + + virtual RootInlineBox* createRootInlineBox(); // Subclassed by SVG and Ruby. + + // Called to lay out the legend for a fieldset or the ruby text of a ruby run. + virtual RenderObject* layoutSpecialExcludedChild(bool /*relayoutChildren*/) { return 0; } + + struct FloatWithRect { + FloatWithRect(RenderBox* f) + : object(f) + , rect(IntRect(f->x() - f->marginLeft(), f->y() - f->marginTop(), f->width() + f->marginLeft() + f->marginRight(), f->height() + f->marginTop() + f->marginBottom())) + , everHadLayout(f->m_everHadLayout) + { + } + + RenderBox* object; + IntRect rect; + bool everHadLayout; + }; + + struct FloatingObject : Noncopyable { + // Note that Type uses bits so you can use FloatBoth as a mask to query for both left and right. + enum Type { FloatLeft = 1, FloatRight = 2, FloatBoth = 3 }; + + FloatingObject(Type type) + : m_renderer(0) + , m_paginationStrut(0) + , m_type(type) + , m_shouldPaint(true) + , m_isDescendant(false) + , m_isPlaced(false) + { + } + + FloatingObject(Type type, const IntRect& frameRect) + : m_renderer(0) + , m_frameRect(frameRect) + , m_paginationStrut(0) + , m_type(type) + , m_shouldPaint(true) + , m_isDescendant(false) + , m_isPlaced(true) + { + } + + Type type() const { return static_cast(m_type); } + RenderBox* renderer() const { return m_renderer; } + + bool isPlaced() const { return m_isPlaced; } + void setIsPlaced(bool placed = true) { m_isPlaced = placed; } + + int left() const { ASSERT(isPlaced()); return m_frameRect.x(); } + int right() const { ASSERT(isPlaced()); return m_frameRect.right(); } + int top() const { ASSERT(isPlaced()); return m_frameRect.y(); } + int bottom() const { ASSERT(isPlaced()); return m_frameRect.bottom(); } + int width() const { return m_frameRect.width(); } + int height() const { return m_frameRect.height(); } + + void setLeft(int left) { m_frameRect.setX(left); } + void setTop(int top) { m_frameRect.setY(top); } + void setWidth(int width) { m_frameRect.setWidth(width); } + void setHeight(int height) { m_frameRect.setHeight(height); } + + const IntRect& frameRect() const { ASSERT(isPlaced()); return m_frameRect; } + void setFrameRect(const IntRect& frameRect) { m_frameRect = frameRect; } + + RenderBox* m_renderer; + IntRect m_frameRect; + int m_paginationStrut; + unsigned m_type : 2; // Type (left or right aligned) + bool m_shouldPaint : 1; + bool m_isDescendant : 1; + bool m_isPlaced : 1; + }; + + int logicalTopForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->top() : child->left(); } + int logicalBottomForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->bottom() : child->right(); } + int logicalLeftForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->left() : child->top(); } + int logicalRightForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->right() : child->bottom(); } + int logicalWidthForFloat(FloatingObject* child) const { return style()->isHorizontalWritingMode() ? child->width() : child->height(); } + void setLogicalTopForFloat(FloatingObject* child, int logicalTop) + { + if (style()->isHorizontalWritingMode()) + child->setTop(logicalTop); + else + child->setLeft(logicalTop); + } + void setLogicalLeftForFloat(FloatingObject* child, int logicalLeft) + { + if (style()->isHorizontalWritingMode()) + child->setLeft(logicalLeft); + else + child->setTop(logicalLeft); + } + void setLogicalHeightForFloat(FloatingObject* child, int logicalHeight) + { + if (style()->isHorizontalWritingMode()) + child->setHeight(logicalHeight); + else + child->setWidth(logicalHeight); + } + void setLogicalWidthForFloat(FloatingObject* child, int logicalWidth) + { + if (style()->isHorizontalWritingMode()) + child->setWidth(logicalWidth); + else + child->setHeight(logicalWidth); + } + + // The following functions' implementations are in RenderBlockLineLayout.cpp. + RootInlineBox* determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, + InlineBidiResolver&, Vector& floats, unsigned& numCleanFloats, + bool& useRepaintBounds, int& repaintTop, int& repaintBottom); + RootInlineBox* determineEndPosition(RootInlineBox* startBox, InlineIterator& cleanLineStart, + BidiStatus& cleanLineBidiStatus, + int& yPos); + bool matchedEndLine(const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, + RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop); + + void skipTrailingWhitespace(InlineIterator&, bool isLineEmpty, bool previousLineBrokeCleanly); + int skipLeadingWhitespace(InlineBidiResolver&, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, FloatingObject* lastFloatFromPreviousLine); + void fitBelowFloats(int widthToFit, bool firstLine, int& availableWidth); + InlineIterator findNextLineBreak(InlineBidiResolver&, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, bool& hyphenated, EClear*, FloatingObject* lastFloatFromPreviousLine); + RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject); + InlineFlowBox* createLineBoxes(RenderObject*, bool firstLine); + void computeInlineDirectionPositionsForLine(RootInlineBox*, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap&); + void computeBlockDirectionPositionsForLine(RootInlineBox*, BidiRun*, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&); + void deleteEllipsisLineBoxes(); + void checkLinesForTextOverflow(); + int beforeSideVisualOverflowForLine(RootInlineBox*) const; + int afterSideVisualOverflowForLine(RootInlineBox*) const; + int beforeSideLayoutOverflowForLine(RootInlineBox*) const; + int afterSideLayoutOverflowForLine(RootInlineBox*) const; + // End of functions defined in RenderBlockLineLayout.cpp. + + void paintFloats(PaintInfo&, int tx, int ty, bool preservePhase = false); + void paintContents(PaintInfo&, int tx, int ty); + void paintColumnContents(PaintInfo&, int tx, int ty, bool paintFloats = false); + void paintColumnRules(PaintInfo&, int tx, int ty); + void paintChildren(PaintInfo&, int tx, int ty); + void paintEllipsisBoxes(PaintInfo&, int tx, int ty); + void paintSelection(PaintInfo&, int tx, int ty); + void paintCaret(PaintInfo&, int tx, int ty, CaretType); + + FloatingObject* insertFloatingObject(RenderBox*); + void removeFloatingObject(RenderBox*); + void removeFloatingObjectsBelow(FloatingObject*, int y); + + // Called from lineWidth, to position the floats added in the last line. + // Returns true if and only if it has positioned any floats. + bool positionNewFloats(); + + // Positions new floats and also adjust all floats encountered on the line if any of them + // have to move to the next page/column. + bool positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine); + + void clearFloats(); + int getClearDelta(RenderBox* child, int yPos); + + virtual bool avoidsFloats() const; + + bool hasOverhangingFloats() { return parent() && !hasColumns() && lowestFloatLogicalBottom() > logicalHeight(); } + void addIntrudingFloats(RenderBlock* prev, int xoffset, int yoffset); + int addOverhangingFloats(RenderBlock* child, int xoffset, int yoffset, bool makeChildPaintOtherFloats); + + int lowestFloatLogicalBottom(FloatingObject::Type = FloatingObject::FloatBoth) const; + int nextFloatLogicalBottomBelow(int) const; + + virtual bool hitTestColumns(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + bool hitTestFloats(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); + + void computeInlinePreferredLogicalWidths(); + void computeBlockPreferredLogicalWidths(); + + // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline + // children. + virtual RenderBlock* firstLineBlock() const; + + virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); + virtual RenderStyle* outlineStyleForRepaint() const; + + virtual RenderObject* hoverAncestor() const; + virtual void updateDragState(bool dragOn); + virtual void childBecameNonInline(RenderObject* child); + + virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool /*clipToVisibleContent*/) + { + return selectionGapRectsForRepaint(repaintContainer); + } + virtual bool shouldPaintSelectionGaps() const; + bool isSelectionRoot() const; + GapRects selectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo* = 0); + GapRects inlineSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo*); + GapRects blockSelectionGaps(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int& lastLogicalTop, int& lastLogicalLeft, int& lastLogicalRight, const PaintInfo*); + IntRect blockSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, + int lastLogicalTop, int lastLogicalLeft, int lastLogicalRight, int logicalBottom, const PaintInfo*); + int logicalLeftSelectionOffset(RenderBlock* rootBlock, int position); + int logicalRightSelectionOffset(RenderBlock* rootBlock, int position); + + virtual void absoluteRects(Vector&, int tx, int ty); + virtual void absoluteQuads(Vector&); + + int desiredColumnWidth() const; + unsigned desiredColumnCount() const; + void setDesiredColumnCountAndWidth(int count, int width); + + void paintContinuationOutlines(PaintInfo&, int tx, int ty); + + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual void addFocusRingRects(Vector&, int tx, int ty); + + void adjustPointToColumnContents(IntPoint&) const; + void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust + + void markLinesDirtyInBlockRange(int logicalTop, int logicalBottom, RootInlineBox* highest = 0); + + void newLine(EClear); + + Position positionForBox(InlineBox*, bool start = true) const; + Position positionForRenderer(RenderObject*, bool start = true) const; + VisiblePosition positionForPointWithInlineChildren(const IntPoint&); + + // Adjust tx and ty from painting offsets to the local coords of this renderer + void offsetForContents(int& tx, int& ty) const; + + void calcColumnWidth(); + bool layoutColumns(bool hasSpecifiedPageLogicalHeight, int pageLogicalHeight, LayoutStateMaintainer&); + void makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild); + + bool expandsToEncloseOverhangingFloats() const; + + void updateScrollInfoAfterLayout(); + + RenderObject* splitAnonymousBlocksAroundChild(RenderObject* beforeChild); + void splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, + RenderObject* beforeChild, RenderBoxModelObject* oldCont); + void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderBoxModelObject* oldCont); + RenderBlock* clone() const; + RenderBlock* continuationBefore(RenderObject* beforeChild); + RenderBlock* containingColumnsBlock(bool allowAnonymousColumnBlock = true); + RenderBlock* columnsBlockForSpanningElement(RenderObject* newChild); + + class MarginInfo { + // Collapsing flags for whether we can collapse our margins with our children's margins. + bool m_canCollapseWithChildren : 1; + bool m_canCollapseMarginBeforeWithChildren : 1; + bool m_canCollapseMarginAfterWithChildren : 1; + + // Whether or not we are a quirky container, i.e., do we collapse away top and bottom + // margins in our container. Table cells and the body are the common examples. We + // also have a custom style property for Safari RSS to deal with TypePad blog articles. + bool m_quirkContainer : 1; + + // This flag tracks whether we are still looking at child margins that can all collapse together at the beginning of a block. + // They may or may not collapse with the top margin of the block (|m_canCollapseTopWithChildren| tells us that), but they will + // always be collapsing with one another. This variable can remain set to true through multiple iterations + // as long as we keep encountering self-collapsing blocks. + bool m_atBeforeSideOfBlock : 1; + + // This flag is set when we know we're examining bottom margins and we know we're at the bottom of the block. + bool m_atAfterSideOfBlock : 1; + + // These variables are used to detect quirky margins that we need to collapse away (in table cells + // and in the body element). + bool m_marginBeforeQuirk : 1; + bool m_marginAfterQuirk : 1; + bool m_determinedMarginBeforeQuirk : 1; + + // These flags track the previous maximal positive and negative margins. + int m_positiveMargin; + int m_negativeMargin; + + public: + MarginInfo(RenderBlock* b, int beforeBorderPadding, int afterBorderPadding); + + void setAtBeforeSideOfBlock(bool b) { m_atBeforeSideOfBlock = b; } + void setAtAfterSideOfBlock(bool b) { m_atAfterSideOfBlock = b; } + void clearMargin() { m_positiveMargin = m_negativeMargin = 0; } + void setMarginBeforeQuirk(bool b) { m_marginBeforeQuirk = b; } + void setMarginAfterQuirk(bool b) { m_marginAfterQuirk = b; } + void setDeterminedMarginBeforeQuirk(bool b) { m_determinedMarginBeforeQuirk = b; } + void setPositiveMargin(int p) { m_positiveMargin = p; } + void setNegativeMargin(int n) { m_negativeMargin = n; } + void setPositiveMarginIfLarger(int p) { if (p > m_positiveMargin) m_positiveMargin = p; } + void setNegativeMarginIfLarger(int n) { if (n > m_negativeMargin) m_negativeMargin = n; } + + void setMargin(int p, int n) { m_positiveMargin = p; m_negativeMargin = n; } + + bool atBeforeSideOfBlock() const { return m_atBeforeSideOfBlock; } + bool canCollapseWithMarginBefore() const { return m_atBeforeSideOfBlock && m_canCollapseMarginBeforeWithChildren; } + bool canCollapseWithMarginAfter() const { return m_atAfterSideOfBlock && m_canCollapseMarginAfterWithChildren; } + bool canCollapseMarginBeforeWithChildren() const { return m_canCollapseMarginBeforeWithChildren; } + bool canCollapseMarginAfterWithChildren() const { return m_canCollapseMarginAfterWithChildren; } + bool quirkContainer() const { return m_quirkContainer; } + bool determinedMarginBeforeQuirk() const { return m_determinedMarginBeforeQuirk; } + bool marginBeforeQuirk() const { return m_marginBeforeQuirk; } + bool marginAfterQuirk() const { return m_marginAfterQuirk; } + int positiveMargin() const { return m_positiveMargin; } + int negativeMargin() const { return m_negativeMargin; } + int margin() const { return m_positiveMargin - m_negativeMargin; } + }; + + void layoutBlockChild(RenderBox* child, MarginInfo&, int& previousFloatLogicalBottom, int& maxFloatLogicalBottom); + void adjustPositionedBlock(RenderBox* child, const MarginInfo&); + void adjustFloatingBlock(const MarginInfo&); + bool handleSpecialChild(RenderBox* child, const MarginInfo&); + bool handleFloatingChild(RenderBox* child, const MarginInfo&); + bool handlePositionedChild(RenderBox* child, const MarginInfo&); + bool handleRunInChild(RenderBox* child); + int collapseMargins(RenderBox* child, MarginInfo&); + int clearFloatsIfNeeded(RenderBox* child, MarginInfo&, int oldTopPosMargin, int oldTopNegMargin, int yPos); + int estimateLogicalTopPosition(RenderBox* child, const MarginInfo&); + void determineLogicalLeftPositionForChild(RenderBox* child); + void handleAfterSideOfBlock(int top, int bottom, MarginInfo&); + void setCollapsedBottomMargin(const MarginInfo&); + // End helper functions and structs used by layoutBlockChildren. + + // Pagination routines. + int nextPageTop(int yPos) const; // Returns the top of the next page following yPos. + int applyBeforeBreak(RenderBox* child, int yPos); // If the child has a before break, then return a new yPos that shifts to the top of the next page/column. + int applyAfterBreak(RenderBox* child, int yPos, MarginInfo& marginInfo); // If the child has an after break, then return a new yPos that shifts to the top of the next page/column. + int adjustForUnsplittableChild(RenderBox* child, int yPos, bool includeMargins = false); // If the child is unsplittable and can't fit on the current page, return the top of the next page/column. + void adjustLinePositionForPagination(RootInlineBox*, int& deltaY); // Computes a deltaY value that put a line at the top of the next page if it doesn't fit on the current page. + + typedef PositionedObjectsListHashSet::const_iterator Iterator; + DeprecatedPtrList* m_floatingObjects; + + PositionedObjectsListHashSet* m_positionedObjects; + + // Allocated only when some of these fields have non-default values + struct RenderBlockRareData : Noncopyable { + RenderBlockRareData(const RenderBlock* block) + : m_margins(positiveMarginBeforeDefault(block), negativeMarginBeforeDefault(block), positiveMarginAfterDefault(block), negativeMarginAfterDefault(block)) + , m_paginationStrut(0) + , m_pageLogicalOffset(0) + { + } + + static int positiveMarginBeforeDefault(const RenderBlock* block) + { + return std::max(block->marginBefore(), 0); + } + + static int negativeMarginBeforeDefault(const RenderBlock* block) + { + return std::max(-block->marginBefore(), 0); + } + static int positiveMarginAfterDefault(const RenderBlock* block) + { + return std::max(block->marginAfter(), 0); + } + static int negativeMarginAfterDefault(const RenderBlock* block) + { + return std::max(-block->marginAfter(), 0); + } + + MarginValues m_margins; + int m_paginationStrut; + int m_pageLogicalOffset; + }; + + OwnPtr m_rareData; + + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example,
Hello
world.
will have two total lines for the
. + + mutable int m_lineHeight; + + // RenderRubyBase objects need to be able to split and merge, moving their children around + // (calling moveChildTo, moveAllChildrenTo, and makeChildrenNonInline). + friend class RenderRubyBase; +}; + +inline RenderBlock* toRenderBlock(RenderObject* object) +{ + ASSERT(!object || object->isRenderBlock()); + return static_cast(object); +} + +inline const RenderBlock* toRenderBlock(const RenderObject* object) +{ + ASSERT(!object || object->isRenderBlock()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBlock(const RenderBlock*); + +} // namespace WebCore + +#endif // RenderBlock_h diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp new file mode 100644 index 0000000..1d909a3 --- /dev/null +++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp @@ -0,0 +1,2244 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010 Apple Inc. All right reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#include "BidiResolver.h" +#include "CharacterNames.h" +#include "Hyphenation.h" +#include "InlineIterator.h" +#include "InlineTextBox.h" +#include "Logging.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderListMarker.h" +#include "RenderView.h" +#include "Settings.h" +#include "TrailingFloatsRootInlineBox.h" +#include "VerticalPositionCache.h" +#include "break_lines.h" +#include +#include +#include +#include +#ifdef ANDROID_LAYOUT +#include "Frame.h" +#include "FrameTree.h" +#include "Settings.h" +#include "Text.h" +#include "HTMLNames.h" +#endif // ANDROID_LAYOUT + +#if ENABLE(SVG) +#include "RenderSVGInlineText.h" +#include "SVGRootInlineBox.h" +#endif + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +// We don't let our line box tree for a single line get any deeper than this. +const unsigned cMaxLineDepth = 200; + +static int getBorderPaddingMargin(RenderBoxModelObject* child, bool endOfInline) +{ + if (endOfInline) + return child->marginEnd() + child->paddingEnd() + child->borderEnd(); + return child->marginStart() + child->paddingStart() + child->borderStart(); +} + +static int inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true) +{ + unsigned lineDepth = 1; + int extraWidth = 0; + RenderObject* parent = child->parent(); + while (parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) { + if (start && !child->previousSibling()) + extraWidth += getBorderPaddingMargin(toRenderBoxModelObject(parent), false); + if (end && !child->nextSibling()) + extraWidth += getBorderPaddingMargin(toRenderBoxModelObject(parent), true); + child = parent; + parent = child->parent(); + } + return extraWidth; +} + +static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak) +{ + // Check to see if our last midpoint is a start point beyond the line break. If so, + // shave it off the list, and shave off a trailing space if the previous end point doesn't + // preserve whitespace. + if (lBreak.obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) { + InlineIterator* midpoints = lineMidpointState.midpoints.data(); + InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2]; + const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1]; + InlineIterator currpoint = endpoint; + while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) + currpoint.increment(); + if (currpoint == lBreak) { + // We hit the line break before the start point. Shave off the start point. + lineMidpointState.numMidpoints--; + if (endpoint.obj->style()->collapseWhiteSpace()) + endpoint.pos--; + } + } +} + +static void addMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint) +{ + if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints) + lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10); + + InlineIterator* midpoints = lineMidpointState.midpoints.data(); + midpoints[lineMidpointState.numMidpoints++] = midpoint; +} + +void RenderBlock::appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) +{ + if (start > end || obj->isFloating() || + (obj->isPositioned() && !obj->style()->hasStaticX() && !obj->style()->hasStaticY() && !obj->container()->isRenderInline())) + return; + + LineMidpointState& lineMidpointState = resolver.midpointState(); + bool haveNextMidpoint = (lineMidpointState.currentMidpoint < lineMidpointState.numMidpoints); + InlineIterator nextMidpoint; + if (haveNextMidpoint) + nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint]; + if (lineMidpointState.betweenMidpoints) { + if (!(haveNextMidpoint && nextMidpoint.obj == obj)) + return; + // This is a new start point. Stop ignoring objects and + // adjust our start. + lineMidpointState.betweenMidpoints = false; + start = nextMidpoint.pos; + lineMidpointState.currentMidpoint++; + if (start < end) + return appendRunsForObject(start, end, obj, resolver); + } else { + if (!haveNextMidpoint || (obj != nextMidpoint.obj)) { + resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); + return; + } + + // An end midpoint has been encountered within our object. We + // need to go ahead and append a run with our endpoint. + if (static_cast(nextMidpoint.pos + 1) <= end) { + lineMidpointState.betweenMidpoints = true; + lineMidpointState.currentMidpoint++; + if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it. + if (static_cast(nextMidpoint.pos + 1) > start) + resolver.addRun(new (obj->renderArena()) + BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir())); + return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver); + } + } else + resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); + } +} + +static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) +{ + if (isRootLineBox) + return toRenderBlock(obj)->createAndAppendRootInlineBox(); + + if (obj->isText()) { + InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox(); + // We only treat a box as text for a
if we are on a line by ourself or in strict mode + // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for
as text.) + if (obj->isBR()) + textBox->setIsText(isOnlyRun || obj->document()->inNoQuirksMode()); + return textBox; + } + + if (obj->isBox()) + return toRenderBox(obj)->createInlineBox(); + + return toRenderInline(obj)->createAndAppendInlineFlowBox(); +} + +static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout) +{ + if (o->isText()) { + if (o->preferredLogicalWidthsDirty() && o->isCounter()) + toRenderText(o)->computePreferredLogicalWidths(0); // FIXME: Counters depend on this hack. No clue why. Should be investigated and removed. + toRenderText(o)->dirtyLineBoxes(fullLayout); + } else + toRenderInline(o)->dirtyLineBoxes(fullLayout); +} + +InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, bool firstLine) +{ + // See if we have an unconstructed line box for this object that is also + // the last item on the line. + unsigned lineDepth = 1; + InlineFlowBox* childBox = 0; + InlineFlowBox* parentBox = 0; + InlineFlowBox* result = 0; + do { + ASSERT(obj->isRenderInline() || obj == this); + + // Get the last box we made for this render object. + parentBox = obj->isRenderInline() ? toRenderInline(obj)->lastLineBox() : toRenderBlock(obj)->lastLineBox(); + + // If this box is constructed then it is from a previous line, and we need + // to make a new box for our line. If this box is unconstructed but it has + // something following it on the line, then we know we have to make a new box + // as well. In this situation our inline has actually been split in two on + // the same line (this can happen with very fancy language mixtures). + bool constructedNewBox = false; + if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) { + // We need to make a new box for this render object. Once + // made, we need to place it at the end of the current line. + InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); + ASSERT(newBox->isInlineFlowBox()); + parentBox = static_cast(newBox); + parentBox->setFirstLineStyleBit(firstLine); + parentBox->setIsHorizontal(style()->isHorizontalWritingMode()); + constructedNewBox = true; + } + + if (!result) + result = parentBox; + + // If we have hit the block itself, then |box| represents the root + // inline box for the line, and it doesn't have to be appended to any parent + // inline. + if (childBox) + parentBox->addToLine(childBox); + + if (!constructedNewBox || obj == this) + break; + + childBox = parentBox; + + // If we've exceeded our line depth, then jump straight to the root and skip all the remaining + // intermediate inline flows. + obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); + + } while (true); + + return result; +} + +RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool firstLine, bool lastLine, RenderObject* endObject) +{ + ASSERT(firstRun); + + bool rootHasSelectedChildren = false; + InlineFlowBox* parentBox = 0; + for (BidiRun* r = firstRun; r; r = r->next()) { + // Create a box for our object. + bool isOnlyRun = (runCount == 1); + if (runCount == 2 && !r->m_object->isListMarker()) + isOnlyRun = (!style()->isLeftToRightDirection() ? lastRun : firstRun)->m_object->isListMarker(); + + InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun); + r->m_box = box; + + ASSERT(box); + if (!box) + continue; + + if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone) + rootHasSelectedChildren = true; + + // If we have no parent box yet, or if the run is not simply a sibling, + // then we need to construct inline boxes as necessary to properly enclose the + // run's inline box. + if (!parentBox || parentBox->renderer() != r->m_object->parent()) + // Create new inline boxes all the way back to the appropriate insertion point. + parentBox = createLineBoxes(r->m_object->parent(), firstLine); + + // Append the inline box to this line. + parentBox->addToLine(box); + + bool visuallyOrdered = r->m_object->style()->visuallyOrdered(); + box->setBidiLevel(visuallyOrdered ? 0 : r->level()); + + if (box->isInlineTextBox()) { + InlineTextBox* text = static_cast(box); + text->setStart(r->m_start); + text->setLen(r->m_stop - r->m_start); + text->m_dirOverride = r->dirOverride(visuallyOrdered); + if (r->m_hasHyphen) + text->setHasHyphen(true); + } + } + + // We should have a root inline box. It should be unconstructed and + // be the last continuation of our line list. + ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); + + // Set the m_selectedChildren flag on the root inline box if one of the leaf inline box + // from the bidi runs walk above has a selection state. + if (rootHasSelectedChildren) + lastLineBox()->root()->setHasSelectedChildren(true); + + // Set bits on our inline flow boxes that indicate which sides should + // paint borders/margins/padding. This knowledge will ultimately be used when + // we determine the horizontal positions and widths of all the inline boxes on + // the line. + lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject); + + // Now mark the line boxes as being constructed. + lastLineBox()->setConstructed(); + + // Return the last line. + return lastRootBox(); +} + +void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, bool firstLine, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) +{ + // First determine our total logical width. + int availableLogicalWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); + int totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); + bool needsWordSpacing = false; + unsigned numSpaces = 0; + ETextAlign textAlign = style()->textAlign(); + + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak()) + continue; // Positioned objects are only participating to figure out their + // correct static x position. They have no effect on the width. + // Similarly, line break boxes have no effect on the width. + if (r->m_object->isText()) { + RenderText* rt = toRenderText(r->m_object); + + if (textAlign == JUSTIFY && r != trailingSpaceRun) { + const UChar* characters = rt->characters(); + for (int i = r->m_start; i < r->m_stop; i++) { + UChar c = characters[i]; + if (c == ' ' || c == '\n' || c == '\t') + numSpaces++; + } + } + + if (int length = rt->textLength()) { + if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start])) + totalLogicalWidth += rt->style(firstLine)->font().wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; + } + HashSet fallbackFonts; + GlyphOverflow glyphOverflow; + int hyphenWidth = 0; + if (static_cast(r->m_box)->hasHyphen()) { + const AtomicString& hyphenString = rt->style()->hyphenString(); + hyphenWidth = rt->style(firstLine)->font().width(TextRun(hyphenString.characters(), hyphenString.length())); + } + r->m_box->setLogicalWidth(rt->width(r->m_start, r->m_stop - r->m_start, totalLogicalWidth, firstLine, &fallbackFonts, &glyphOverflow) + hyphenWidth); + if (!fallbackFonts.isEmpty()) { + ASSERT(r->m_box->isText()); + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast(r->m_box), make_pair(Vector(), GlyphOverflow())).first; + ASSERT(it->second.first.isEmpty()); + copyToVector(fallbackFonts, it->second.first); + } + if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { + ASSERT(r->m_box->isText()); + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(static_cast(r->m_box), make_pair(Vector(), GlyphOverflow())).first; + it->second.second = glyphOverflow; + } + } else if (!r->m_object->isRenderInline()) { + RenderBox* renderBox = toRenderBox(r->m_object); + renderBox->computeLogicalWidth(); + r->m_box->setLogicalWidth(logicalWidthForChild(renderBox)); + totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); + } + + totalLogicalWidth += r->m_box->logicalWidth(); + } + + // Armed with the total width of the line (without justification), + // we now examine our text-align property in order to determine where to position the + // objects horizontally. The total width of the line can be increased if we end up + // justifying text. + int logicalLeft = logicalLeftOffsetForLine(logicalHeight(), firstLine); + switch (textAlign) { + case LEFT: + case WEBKIT_LEFT: + // The direction of the block should determine what happens with wide lines. In + // particular with RTL blocks, wide lines should still spill out to the left. + if (style()->isLeftToRightDirection()) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + } else { + if (trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(0); + else if (totalLogicalWidth > availableLogicalWidth) + logicalLeft -= (totalLogicalWidth - availableLogicalWidth); + } + break; + case JUSTIFY: + if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) { + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceRun->m_box->setLogicalWidth(0); + } + break; + } + // fall through + case TAAUTO: + numSpaces = 0; + // for right to left fall through to right aligned + if (style()->isLeftToRightDirection()) { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + break; + } + case RIGHT: + case WEBKIT_RIGHT: + // Wide lines spill out of the block based off direction. + // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right + // side of the block. + if (style()->isLeftToRightDirection()) { + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceRun->m_box->setLogicalWidth(0); + } + if (totalLogicalWidth < availableLogicalWidth) + logicalLeft += availableLogicalWidth - totalLogicalWidth; + } else { + if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) { + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth)); + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + } else + logicalLeft += availableLogicalWidth - totalLogicalWidth; + } + break; + case CENTER: + case WEBKIT_CENTER: + int trailingSpaceWidth = 0; + if (trailingSpaceRun) { + totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth(); + trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2); + trailingSpaceRun->m_box->setLogicalWidth(max(0, trailingSpaceWidth)); + } + if (style()->isLeftToRightDirection()) + logicalLeft += max((availableLogicalWidth - totalLogicalWidth) / 2, 0); + else + logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth; + break; + } + + if (numSpaces) { + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box || r == trailingSpaceRun) + continue; + + int spaceAdd = 0; + if (r->m_object->isText()) { + unsigned spaces = 0; + const UChar* characters = toRenderText(r->m_object)->characters(); + for (int i = r->m_start; i < r->m_stop; i++) { + UChar c = characters[i]; + if (c == ' ' || c == '\n' || c == '\t') + spaces++; + } + + ASSERT(spaces <= numSpaces); + + // Only justify text if whitespace is collapsed. + if (r->m_object->style()->collapseWhiteSpace()) { + spaceAdd = (availableLogicalWidth - totalLogicalWidth) * spaces / numSpaces; + static_cast(r->m_box)->setSpaceAdd(spaceAdd); + totalLogicalWidth += spaceAdd; + } + numSpaces -= spaces; + if (!numSpaces) + break; + } + } + } + + // The widths of all runs are now known. We can now place every inline box (and + // compute accurate widths for the inline flow boxes). + needsWordSpacing = false; + lineBox->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); +} + +void RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, + VerticalPositionCache& verticalPositionCache) +{ + setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache)); + lineBox->setBlockLogicalHeight(logicalHeight()); + + // Now make sure we place replaced render objects correctly. + for (BidiRun* r = firstRun; r; r = r->next()) { + ASSERT(r->m_box); + if (!r->m_box) + continue; // Skip runs with no line boxes. + + // Align positioned boxes with the top of the line box. This is + // a reasonable approximation of an appropriate y position. + if (r->m_object->isPositioned()) + r->m_box->setLogicalTop(logicalHeight()); + + // Position is used to properly position both replaced elements and + // to update the static normal flow x/y of positioned elements. + if (r->m_object->isText()) + toRenderText(r->m_object)->positionLineBox(r->m_box); + else if (r->m_object->isBox()) + toRenderBox(r->m_object)->positionLineBox(r->m_box); + } + // Positioned objects and zero-length text nodes destroy their boxes in + // position(), which unnecessarily dirties the line. + lineBox->markDirty(false); +} + +static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) +{ + if (character == ' ' || character == '\t' || character == softHyphen) + return true; + if (character == '\n') + return !renderer->style()->preserveNewline(); + if (character == noBreakSpace) + return renderer->style()->nbspMode() == SPACE; + return false; +} + +void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintLogicalTop, int& repaintLogicalBottom) +{ + bool useRepaintBounds = false; + + m_overflow.clear(); + + setLogicalHeight(borderBefore() + paddingBefore()); + + // Figure out if we should clear out our line boxes. + // FIXME: Handle resize eventually! + bool fullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren; + if (fullLayout) + lineBoxes()->deleteLineBoxes(renderArena()); + + // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't + // clip. + // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely + // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense + // anyway, so we won't worry about following the draft here. + bool hasTextOverflow = style()->textOverflow() && hasOverflowClip(); + + // Walk all the lines and delete our ellipsis line boxes if they exist. + if (hasTextOverflow) + deleteEllipsisLineBoxes(); + + if (firstChild()) { +#ifdef ANDROID_LAYOUT + // if we are in fitColumnToScreen mode + // and the current object is not float:right in LTR or not float:left in RTL, + // and text align is auto, or justify or left in LTR, or right in RTL, we + // will wrap text around screen width so that it doesn't need to scroll + // horizontally when reading a paragraph. + // In case the line height is less than the font size, we skip + // the text wrapping since this will cause text overlapping. + // If a text has background image, we ignore text wrapping, + // otherwise the background will be potentially messed up. + const Settings* settings = document()->settings(); + bool doTextWrap = settings && settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen; + if (doTextWrap) { + int ta = style()->textAlign(); + int dir = style()->direction(); + bool autowrap = style()->autoWrap(); + // if the RenderBlock is positioned, don't wrap text around screen + // width as it may cause text to overlap. + bool positioned = isPositioned(); + EFloat cssfloat = style()->floating(); + const int lineHeight = style()->computedLineHeight(); + const int fontSize = style()->fontSize(); + doTextWrap = autowrap && !positioned && + (fontSize <= lineHeight) && !style()->hasBackground() && + (((dir == LTR && cssfloat != FRIGHT) || + (dir == RTL && cssfloat != FLEFT)) && + ((ta == TAAUTO) || (ta == JUSTIFY) || + ((ta == LEFT || ta == WEBKIT_LEFT) && (dir == LTR)) || + ((ta == RIGHT || ta == WEBKIT_RIGHT) && (dir == RTL)))); + } + bool hasTextToWrap = false; +#endif + // layout replaced elements + bool endOfInline = false; + RenderObject* o = bidiFirst(this, 0, false); + Vector floats; + bool hasInlineChild = false; + while (o) { + if (!hasInlineChild && o->isInline()) + hasInlineChild = true; + + if (o->isReplaced() || o->isFloating() || o->isPositioned()) { + RenderBox* box = toRenderBox(o); + + if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent()) + o->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (o->style()->paddingStart().isPercent() || o->style()->paddingEnd().isPercent())) + o->setPreferredLogicalWidthsDirty(true, false); + + if (o->isPositioned()) + o->containingBlock()->insertPositionedObject(box); +#if PLATFORM(ANDROID) + else { +#ifdef ANDROID_LAYOUT + // ignore text wrap for textField or menuList + if (doTextWrap && (o->isTextField() || o->isMenuList())) + doTextWrap = false; +#endif + if (o->isFloating()) + floats.append(FloatWithRect(box)); + else if (fullLayout || o->needsLayout()) { + // Replaced elements + toRenderBox(o)->dirtyLineBoxes(fullLayout); + o->layoutIfNeeded(); + } + } +#else + else if (o->isFloating()) + floats.append(FloatWithRect(box)); + else if (fullLayout || o->needsLayout()) { + // Replaced elements + toRenderBox(o)->dirtyLineBoxes(fullLayout); + o->layoutIfNeeded(); + } +#endif // PLATFORM(ANDROID) + } else if (o->isText() || (o->isRenderInline() && !endOfInline)) { + if (fullLayout || o->selfNeedsLayout()) + dirtyLineBoxesForRenderer(o, fullLayout); + o->setNeedsLayout(false); +#ifdef ANDROID_LAYOUT + if (doTextWrap && !hasTextToWrap && o->isText()) { + Node* node = o->node(); + // as it is very common for sites to use a serial of
or + //
  • as tabs, we don't force text to wrap if all the text + // are short and within an or
  • tag, and only separated + // by short word like "|" or ";". + if (node && node->isTextNode() && + !static_cast(node)->containsOnlyWhitespace()) { + int length = static_cast(node)->length(); + // FIXME, need a magic number to decide it is too long to + // be a tab. Pick 25 for now as it covers around 160px + // (half of 320px) with the default font. + if (length > 25 || (length > 3 && + (!node->parentOrHostNode()->hasTagName(HTMLNames::aTag) && + !node->parentOrHostNode()->hasTagName(HTMLNames::liTag)))) + hasTextToWrap = true; + } + } +#endif + } + o = bidiNext(this, o, 0, false, &endOfInline); + } + +#ifdef ANDROID_LAYOUT + // try to make sure that inline text will not span wider than the + // screen size unless the container has a fixed height, + if (doTextWrap && hasTextToWrap) { + // check all the nested containing blocks, unless it is table or + // table-cell, to make sure there is no fixed height as it implies + // fixed layout. If we constrain the text to fit screen, we may + // cause text overlap with the block after. + bool isConstrained = false; + RenderObject* obj = this; + while (obj) { + if (obj->style()->height().isFixed() && (!obj->isTable() && !obj->isTableCell())) { + isConstrained = true; + break; + } + if (obj->isFloating() || obj->isPositioned()) { + // floating and absolute or fixed positioning are done out + // of normal flow. Don't need to worry about height any more. + break; + } + obj = obj->container(); + } + if (!isConstrained) { + int textWrapWidth = view()->frameView()->textWrapWidth(); + int padding = paddingLeft() + paddingRight(); + if (textWrapWidth > 0 && width() > (textWrapWidth + padding)) { + // limit the content width (width excluding padding) to be + // (textWrapWidth - 2 * ANDROID_FCTS_MARGIN_PADDING) + int maxWidth = textWrapWidth - 2 * ANDROID_FCTS_MARGIN_PADDING + padding; + setWidth(min(width(), maxWidth)); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, maxWidth); + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, maxWidth); + + IntRect overflow = layoutOverflowRect(); + if (overflow.width() > maxWidth) { + overflow.setWidth(maxWidth); + clearLayoutOverflow(); + addLayoutOverflow(overflow); + } + } + } + } +#endif + // We want to skip ahead to the first dirty line + InlineBidiResolver resolver; + unsigned floatIndex; + bool firstLine = true; + bool previousLineBrokeCleanly = true; + RootInlineBox* startLine = determineStartPosition(firstLine, fullLayout, previousLineBrokeCleanly, resolver, floats, floatIndex, + useRepaintBounds, repaintLogicalTop, repaintLogicalBottom); + + if (fullLayout && hasInlineChild && !selfNeedsLayout()) { + setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like + // we're supposed to. + RenderView* v = view(); + if (v && !v->doingFullRepaint() && hasLayer()) { + // Because we waited until we were already inside layout to discover + // that the block really needed a full layout, we missed our chance to repaint the layer + // before layout started. Luckily the layer has cached the repaint rect for its original + // position and size, and so we can use that to make a repaint happen now. + repaintUsingContainer(containerForRepaint(), layer()->repaintRect()); + } + } + + FloatingObject* lastFloat = m_floatingObjects ? m_floatingObjects->last() : 0; + + LineMidpointState& lineMidpointState = resolver.midpointState(); + + // We also find the first clean line and extract these lines. We will add them back + // if we determine that we're able to synchronize after handling all our dirty lines. + InlineIterator cleanLineStart; + BidiStatus cleanLineBidiStatus; + int endLineLogicalTop = 0; + RootInlineBox* endLine = (fullLayout || !startLine) ? + 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineLogicalTop); + + if (startLine) { + if (!useRepaintBounds) { + useRepaintBounds = true; + repaintLogicalTop = logicalHeight(); + repaintLogicalBottom = logicalHeight(); + } + RenderArena* arena = renderArena(); + RootInlineBox* box = startLine; + while (box) { + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(box)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(box)); + RootInlineBox* next = box->nextRootBox(); + box->deleteLine(arena); + box = next; + } + } + + InlineIterator end = resolver.position(); + + if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) { + // If the last line before the start line ends with a line break that clear floats, + // adjust the height accordingly. + // A line break can be either the first or the last object on a line, depending on its direction. + if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) { + RenderObject* lastObject = lastLeafChild->renderer(); + if (!lastObject->isBR()) + lastObject = lastRootBox()->firstLeafChild()->renderer(); + if (lastObject->isBR()) { + EClear clear = lastObject->style()->clear(); + if (clear != CNONE) + newLine(clear); + } + } + } + + bool endLineMatched = false; + bool checkForEndLineMatch = endLine; + bool checkForFloatsFromLastLine = false; + + bool isLineEmpty = true; + bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); + + VerticalPositionCache verticalPositionCache; + + while (!end.atEnd()) { + // FIXME: Is this check necessary before the first iteration or can it be moved to the end? + if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineLogicalTop, repaintLogicalBottom, repaintLogicalTop))) + break; + + lineMidpointState.reset(); + + isLineEmpty = true; + + EClear clear = CNONE; + bool hyphenated; + + InlineIterator oldEnd = end; + FloatingObject* lastFloatFromPreviousLine = m_floatingObjects ? m_floatingObjects->last() : 0; + end = findNextLineBreak(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, hyphenated, &clear, lastFloatFromPreviousLine); + if (resolver.position().atEnd()) { + resolver.deleteRuns(); + checkForFloatsFromLastLine = true; + break; + } + ASSERT(end != resolver.position()); + + if (!isLineEmpty) { + resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly); + ASSERT(resolver.position() == end); + + BidiRun* trailingSpaceRun = 0; + if (!previousLineBrokeCleanly && resolver.runCount() && resolver.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace() + && resolver.logicallyLastRun()->m_object->style()->autoWrap()) { + trailingSpaceRun = resolver.logicallyLastRun(); + RenderObject* lastObject = trailingSpaceRun->m_object; + if (lastObject->isText()) { + RenderText* lastText = toRenderText(lastObject); + const UChar* characters = lastText->characters(); + int firstSpace = trailingSpaceRun->stop(); + while (firstSpace > trailingSpaceRun->start()) { + UChar current = characters[firstSpace - 1]; + if (!isCollapsibleSpace(current, lastText)) + break; + firstSpace--; + } + if (firstSpace == trailingSpaceRun->stop()) + trailingSpaceRun = 0; + else { + TextDirection direction = style()->direction(); + bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun()); + if (firstSpace != trailingSpaceRun->start()) { + BidiContext* baseContext = resolver.context(); + while (BidiContext* parent = baseContext->parent()) + baseContext = parent; + + BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral); + trailingSpaceRun->m_stop = firstSpace; + if (direction == LTR) + resolver.addRun(newTrailingRun); + else + resolver.prependRun(newTrailingRun); + trailingSpaceRun = newTrailingRun; + shouldReorder = false; + } + if (shouldReorder) { + if (direction == LTR) { + resolver.moveRunToEnd(trailingSpaceRun); + trailingSpaceRun->m_level = 0; + } else { + resolver.moveRunToBeginning(trailingSpaceRun); + trailingSpaceRun->m_level = 1; + } + } + } + } else + trailingSpaceRun = 0; + } + + // Now that the runs have been ordered, we create the line boxes. + // At the same time we figure out where border/padding/margin should be applied for + // inline flow boxes. + + RootInlineBox* lineBox = 0; + int oldLogicalHeight = logicalHeight(); + if (resolver.runCount()) { + if (hyphenated) + resolver.logicallyLastRun()->m_hasHyphen = true; + lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), firstLine, !end.obj, end.obj && !end.pos ? end.obj : 0); + if (lineBox) { + lineBox->setEndsWithBreak(previousLineBrokeCleanly); + +#if ENABLE(SVG) + bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); +#else + bool isSVGRootInlineBox = false; +#endif + + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + + // Now we position all of our text runs horizontally. + if (!isSVGRootInlineBox) + computeInlineDirectionPositionsForLine(lineBox, firstLine, resolver.firstRun(), trailingSpaceRun, end.atEnd(), textBoxDataMap); + + // Now position our text runs vertically. + computeBlockDirectionPositionsForLine(lineBox, resolver.firstRun(), textBoxDataMap, verticalPositionCache); + +#if ENABLE(SVG) + // SVG text layout code computes vertical & horizontal positions on its own. + // Note that we still need to execute computeVerticalPositionsForLine() as + // it calls InlineTextBox::positionLineBox(), which tracks whether the box + // contains reversed text or not. If we wouldn't do that editing and thus + // text selection in RTL boxes would not work as expected. + if (isSVGRootInlineBox) { + ASSERT(isSVGText()); + static_cast(lineBox)->computePerCharacterLayoutInformation(); + } +#endif + + // Compute our overflow now. + lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), document()->inNoQuirksMode(), textBoxDataMap); + +#if PLATFORM(MAC) + // Highlight acts as an overflow inflation. + if (style()->highlight() != nullAtom) + lineBox->addHighlightOverflow(); +#endif + } + } + + resolver.deleteRuns(); + + if (lineBox) { + lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status()); + if (useRepaintBounds) { + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(lineBox)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(lineBox)); + } + + if (paginated) { + int adjustment = 0; + adjustLinePositionForPagination(lineBox, adjustment); + if (adjustment) { + int oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, firstLine); + lineBox->adjustPosition(0, adjustment); + if (useRepaintBounds) // This can only be a positive adjustment, so no need to update repaintTop. + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(lineBox)); + + if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, firstLine) != oldLineWidth) { + // We have to delete this line, remove all floats that got added, and let line layout re-run. + lineBox->deleteLine(renderArena()); + removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight); + setLogicalHeight(oldLogicalHeight + adjustment); + resolver.setPosition(oldEnd); + end = oldEnd; + continue; + } + + setLogicalHeight(lineBox->blockLogicalHeight()); + } + } + } + + firstLine = false; + newLine(clear); + } + + if (m_floatingObjects && lastRootBox()) { + if (lastFloat) { + for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { + } + m_floatingObjects->next(); + } else + m_floatingObjects->first(); + for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) { + lastRootBox()->floats().append(f->m_renderer); + ASSERT(f->m_renderer == floats[floatIndex].object); + // If a float's geometry has changed, give up on syncing with clean lines. + if (floats[floatIndex].rect != f->frameRect()) + checkForEndLineMatch = false; + floatIndex++; + } + lastFloat = m_floatingObjects->last(); + } + + lineMidpointState.reset(); + resolver.setPosition(end); + } + + if (endLine) { + if (endLineMatched) { + // Attach all the remaining lines, and then adjust their y-positions as needed. + int delta = logicalHeight() - endLineLogicalTop; + for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) { + line->attachLine(); + if (paginated) { + delta -= line->paginationStrut(); + adjustLinePositionForPagination(line, delta); + } + if (delta) { + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(line) + min(delta, 0)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(line) + max(delta, 0)); + line->adjustPosition(0, delta); + } + if (Vector* cleanLineFloats = line->floatsPtr()) { + Vector::iterator end = cleanLineFloats->end(); + for (Vector::iterator f = cleanLineFloats->begin(); f != end; ++f) { + insertFloatingObject(*f); + setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta); + positionNewFloats(); + } + } + } + setLogicalHeight(lastRootBox()->blockLogicalHeight()); + } else { + // Delete all the remaining lines. + RootInlineBox* line = endLine; + RenderArena* arena = renderArena(); + while (line) { + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(line)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(line)); + RootInlineBox* next = line->nextRootBox(); + line->deleteLine(arena); + line = next; + } + } + } + if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) { + // In case we have a float on the last line, it might not be positioned up to now. + // This has to be done before adding in the bottom border/padding, or the float will + // include the padding incorrectly. -dwh + if (checkForFloatsFromLastLine) { + int bottomVisualOverflow = afterSideVisualOverflowForLine(lastRootBox()); + int bottomLayoutOverflow = afterSideLayoutOverflowForLine(lastRootBox()); + TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this); + m_lineBoxes.appendLineBox(trailingFloatsLineBox); + trailingFloatsLineBox->setConstructed(); + GlyphOverflowAndFallbackFontsMap textBoxDataMap; + VerticalPositionCache verticalPositionCache; + trailingFloatsLineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache); + IntRect logicalLayoutOverflow(0, logicalHeight(), 1, bottomLayoutOverflow); + IntRect logicalVisualOverflow(0, logicalHeight(), 1, bottomVisualOverflow); + trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow); + trailingFloatsLineBox->setBlockLogicalHeight(logicalHeight()); + } + if (lastFloat) { + for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { + } + m_floatingObjects->next(); + } else + m_floatingObjects->first(); + for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) + lastRootBox()->floats().append(f->m_renderer); + lastFloat = m_floatingObjects->last(); + } + size_t floatCount = floats.size(); + // Floats that did not have layout did not repaint when we laid them out. They would have + // painted by now if they had moved, but if they stayed at (0, 0), they still need to be + // painted. + for (size_t i = 0; i < floatCount; ++i) { + if (!floats[i].everHadLayout) { + RenderBox* f = floats[i].object; + if (!f->x() && !f->y() && f->checkForRepaintDuringLayout()) + f->repaint(); + } + } + } + + // Expand the last line to accommodate Ruby and emphasis marks. + int lastLineAnnotationsAdjustment = 0; + if (lastRootBox()) { + int lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter()); + if (!style()->isFlippedLinesWritingMode()) + lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition); + else + lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); + } + + // Now add in the bottom border/padding. + setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAfter() + paddingAfter() + scrollbarLogicalHeight()); + + if (!firstLineBox() && hasLineIfEmpty()) + setLogicalHeight(logicalHeight() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); + + // See if we have any lines that spill out of our block. If we do, then we will possibly need to + // truncate text. + if (hasTextOverflow) + checkLinesForTextOverflow(); +} + +RootInlineBox* RenderBlock::determineStartPosition(bool& firstLine, bool& fullLayout, bool& previousLineBrokeCleanly, + InlineBidiResolver& resolver, Vector& floats, unsigned& numCleanFloats, + bool& useRepaintBounds, int& repaintLogicalTop, int& repaintLogicalBottom) +{ + RootInlineBox* curr = 0; + RootInlineBox* last = 0; + + bool dirtiedByFloat = false; + if (!fullLayout) { + // Paginate all of the clean lines. + bool paginated = view()->layoutState() && view()->layoutState()->isPaginated(); + int paginationDelta = 0; + size_t floatIndex = 0; + for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) { + if (paginated) { + paginationDelta -= curr->paginationStrut(); + adjustLinePositionForPagination(curr, paginationDelta); + if (paginationDelta) { + if (containsFloats() || !floats.isEmpty()) { + // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout. + fullLayout = true; + break; + } + + if (!useRepaintBounds) + useRepaintBounds = true; + + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(curr) + min(paginationDelta, 0)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(curr) + max(paginationDelta, 0)); + curr->adjustPosition(0, paginationDelta); + } + } + + if (Vector* cleanLineFloats = curr->floatsPtr()) { + Vector::iterator end = cleanLineFloats->end(); + for (Vector::iterator o = cleanLineFloats->begin(); o != end; ++o) { + RenderBox* f = *o; + f->layoutIfNeeded(); + IntSize newSize(f->width() + f->marginLeft() + f->marginRight(), f->height() + f->marginTop() + f->marginBottom()); + ASSERT(floatIndex < floats.size()); + if (floats[floatIndex].object != f) { + // A new float has been inserted before this line or before its last known float. + // Just do a full layout. + fullLayout = true; + break; + } + if (floats[floatIndex].rect.size() != newSize) { + int floatTop = style()->isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); + int floatHeight = style()->isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height()) + : max(floats[floatIndex].rect.width(), newSize.width()); + curr->markDirty(); + markLinesDirtyInBlockRange(curr->blockLogicalHeight(), floatTop + floatHeight, curr); + floats[floatIndex].rect.setSize(newSize); + dirtiedByFloat = true; + } + floatIndex++; + } + } + if (dirtiedByFloat || fullLayout) + break; + } + // Check if a new float has been inserted after the last known float. + if (!curr && floatIndex < floats.size()) + fullLayout = true; + } + + if (fullLayout) { + // Nuke all our lines. + if (firstRootBox()) { + RenderArena* arena = renderArena(); + curr = firstRootBox(); + while (curr) { + RootInlineBox* next = curr->nextRootBox(); + curr->deleteLine(arena); + curr = next; + } + ASSERT(!firstLineBox() && !lastLineBox()); + } + } else { + if (curr) { + // We have a dirty line. + if (RootInlineBox* prevRootBox = curr->prevRootBox()) { + // We have a previous line. + if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))) + // The previous line didn't break cleanly or broke at a newline + // that has been deleted, so treat it as dirty too. + curr = prevRootBox; + } + } else { + // No dirty lines were found. + // If the last line didn't break cleanly, treat it as dirty. + if (lastRootBox() && !lastRootBox()->endsWithBreak()) + curr = lastRootBox(); + } + + // If we have no dirty lines, then last is just the last root box. + last = curr ? curr->prevRootBox() : lastRootBox(); + } + + numCleanFloats = 0; + if (!floats.isEmpty()) { + int savedLogicalHeight = logicalHeight(); + // Restore floats from clean lines. + RootInlineBox* line = firstRootBox(); + while (line != curr) { + if (Vector* cleanLineFloats = line->floatsPtr()) { + Vector::iterator end = cleanLineFloats->end(); + for (Vector::iterator f = cleanLineFloats->begin(); f != end; ++f) { + insertFloatingObject(*f); + setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f)); + positionNewFloats(); + ASSERT(floats[numCleanFloats].object == *f); + numCleanFloats++; + } + } + line = line->nextRootBox(); + } + setLogicalHeight(savedLogicalHeight); + } + + firstLine = !last; + previousLineBrokeCleanly = !last || last->endsWithBreak(); + + RenderObject* startObj; + int pos = 0; + if (last) { + setLogicalHeight(last->blockLogicalHeight()); + startObj = last->lineBreakObj(); + pos = last->lineBreakPos(); + resolver.setStatus(last->lineBreakBidiStatus()); + } else { + bool ltr = style()->isLeftToRightDirection() + #if ENABLE(SVG) + || (style()->unicodeBidi() == UBNormal && isSVGText()) + #endif + ; + + Direction direction = ltr ? LeftToRight : RightToLeft; + resolver.setLastStrongDir(direction); + resolver.setLastDir(direction); + resolver.setEorDir(direction); + resolver.setContext(BidiContext::create(ltr ? 0 : 1, direction, style()->unicodeBidi() == Override)); + + startObj = bidiFirst(this, &resolver); + } + + resolver.setPosition(InlineIterator(this, startObj, pos)); + + return curr; +} + +RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& logicalTop) +{ + RootInlineBox* last = 0; + if (!startLine) + last = 0; + else { + for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { + if (curr->isDirty()) + last = 0; + else if (!last) + last = curr; + } + } + + if (!last) + return 0; + + RootInlineBox* prev = last->prevRootBox(); + cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); + cleanLineBidiStatus = prev->lineBreakBidiStatus(); + logicalTop = prev->blockLogicalHeight(); + + for (RootInlineBox* line = last; line; line = line->nextRootBox()) + line->extractLine(); // Disconnect all line boxes from their render objects while preserving + // their connections to one another. + + return last; +} + +bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, + int& endLogicalTop, int& repaintLogicalBottom, int& repaintLogicalTop) +{ + if (resolver.position() == endLineStart) { + if (resolver.status() != endLineStatus) + return false; + + int delta = logicalHeight() - endLogicalTop; + if (!delta || !m_floatingObjects) + return true; + + // See if any floats end in the range along which we want to shift the lines vertically. + int logicalTop = min(logicalHeight(), endLogicalTop); + + RootInlineBox* lastLine = endLine; + while (RootInlineBox* nextLine = lastLine->nextRootBox()) + lastLine = nextLine; + + int logicalBottom = lastLine->blockLogicalHeight() + abs(delta); + + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom) + return false; + } + + return true; + } + + // The first clean line doesn't match, but we can check a handful of following lines to try + // to match back up. + static int numLines = 8; // The # of lines we're willing to match against. + RootInlineBox* line = endLine; + for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { + if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) { + // We have a match. + if (line->lineBreakBidiStatus() != resolver.status()) + return false; // ...but the bidi state doesn't match. + RootInlineBox* result = line->nextRootBox(); + + // Set our logical top to be the block height of endLine. + if (result) + endLogicalTop = line->blockLogicalHeight(); + + int delta = logicalHeight() - endLogicalTop; + if (delta && m_floatingObjects) { + // See if any floats end in the range along which we want to shift the lines vertically. + int logicalTop = min(logicalHeight(), endLogicalTop); + + RootInlineBox* lastLine = endLine; + while (RootInlineBox* nextLine = lastLine->nextRootBox()) + lastLine = nextLine; + + int logicalBottom = lastLine->blockLogicalHeight() + abs(delta); + + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom) + return false; + } + } + + // Now delete the lines that we failed to sync. + RootInlineBox* boxToDelete = endLine; + RenderArena* arena = renderArena(); + while (boxToDelete && boxToDelete != result) { + repaintLogicalTop = min(repaintLogicalTop, beforeSideVisualOverflowForLine(boxToDelete)); + repaintLogicalBottom = max(repaintLogicalBottom, afterSideVisualOverflowForLine(boxToDelete)); + RootInlineBox* next = boxToDelete->nextRootBox(); + boxToDelete->deleteLine(arena); + boxToDelete = next; + } + + endLine = result; + return result; + } + } + + return false; +} + +static inline bool skipNonBreakingSpace(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) +{ + if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace) + return false; + + // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly + // with m_minWidth/m_maxWidth. + // Do not skip a non-breaking space if it is the first character + // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off + // |true|). + if (isLineEmpty && previousLineBrokeCleanly) + return false; + + return true; +} + +static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, bool isLineEmpty, bool previousLineBrokeCleanly) +{ + return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly)); +} + +static inline bool shouldPreserveNewline(RenderObject* object) +{ +#if ENABLE(SVG) + if (object->isSVGInlineText()) + return false; +#endif + + return object->style()->preserveNewline(); +} + +static bool inlineFlowRequiresLineBox(RenderInline* flow) +{ + // FIXME: Right now, we only allow line boxes for inlines that are truly empty. + // We need to fix this, though, because at the very least, inlines containing only + // ignorable whitespace should should also have line boxes. + return !flow->firstChild() && flow->hasInlineDirectionBordersPaddingOrMargin(); +} + +bool RenderBlock::requiresLineBox(const InlineIterator& it, bool isLineEmpty, bool previousLineBrokeCleanly) +{ + if (it.obj->isFloatingOrPositioned()) + return false; + + if (it.obj->isRenderInline() && !inlineFlowRequiresLineBox(toRenderInline(it.obj))) + return false; + + if (!shouldCollapseWhiteSpace(it.obj->style(), isLineEmpty, previousLineBrokeCleanly) || it.obj->isBR()) + return true; + + UChar current = it.current(); + return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) + && !skipNonBreakingSpace(it, isLineEmpty, previousLineBrokeCleanly); +} + +bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj, bool isLineEmpty, bool previousLineBrokeCleanly) +{ + ASSERT(inlineObj->parent() == this); + + InlineIterator it(this, inlineObj, 0); + while (!it.atEnd() && !requiresLineBox(it, isLineEmpty, previousLineBrokeCleanly)) + it.increment(); + + return !it.atEnd(); +} + +// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building +// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned +// elements quite right. In other words, we need to build this function's work into the normal line +// object iteration process. +// NB. this function will insert any floating elements that would otherwise +// be skipped but it will not position them. +void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator, bool isLineEmpty, bool previousLineBrokeCleanly) +{ + while (!iterator.atEnd() && !requiresLineBox(iterator, isLineEmpty, previousLineBrokeCleanly)) { + RenderObject* object = iterator.obj; + if (object->isFloating()) { + insertFloatingObject(toRenderBox(object)); + } else if (object->isPositioned()) { + // FIXME: The math here is actually not really right. It's a best-guess approximation that + // will work for the common cases + RenderObject* c = object->container(); + if (c->isRenderInline()) { + // A relative positioned inline encloses us. In this case, we also have to determine our + // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned + // inline so that we can obtain the value later. + toRenderInline(c)->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), false) : logicalRightOffsetForLine(height(), false)); + toRenderInline(c)->layer()->setStaticY(height()); + } + + RenderBox* box = toRenderBox(object); + if (box->style()->hasStaticX()) { + if (box->style()->isOriginalDisplayInlineType()) + box->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), false) : width() - logicalRightOffsetForLine(height(), false)); + else + box->layer()->setStaticX(style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + } + + if (box->style()->hasStaticY()) + box->layer()->setStaticY(height()); + } + iterator.increment(); + } +} + +int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver, bool firstLine, bool isLineEmpty, bool previousLineBrokeCleanly, + FloatingObject* lastFloatFromPreviousLine) +{ + int availableWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); + while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), isLineEmpty, previousLineBrokeCleanly)) { + RenderObject* object = resolver.position().obj; + if (object->isFloating()) { + positionNewFloatOnLine(insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine); + availableWidth = availableLogicalWidthForLine(logicalHeight(), firstLine); + } else if (object->isPositioned()) { + // FIXME: The math here is actually not really right. It's a best-guess approximation that + // will work for the common cases + RenderObject* c = object->container(); + if (c->isRenderInline()) { + // A relative positioned inline encloses us. In this case, we also have to determine our + // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned + // inline so that we can obtain the value later. + toRenderInline(c)->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), firstLine) : logicalRightOffsetForLine(height(), firstLine)); + toRenderInline(c)->layer()->setStaticY(height()); + } + + RenderBox* box = toRenderBox(object); + if (box->style()->hasStaticX()) { + if (box->style()->isOriginalDisplayInlineType()) + box->layer()->setStaticX(style()->isLeftToRightDirection() ? logicalLeftOffsetForLine(height(), firstLine) : width() - logicalRightOffsetForLine(height(), firstLine)); + else + box->layer()->setStaticX(style()->isLeftToRightDirection() ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + } + + if (box->style()->hasStaticY()) + box->layer()->setStaticY(height()); + } + resolver.increment(); + } + resolver.commitExplicitEmbedding(); + return availableWidth; +} + +// This is currently just used for list markers and inline flows that have line boxes. Neither should +// have an effect on whitespace at the start of the line. +static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o, LineMidpointState& lineMidpointState) +{ + RenderObject* next = bidiNext(block, o); + if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) { + RenderText* nextText = toRenderText(next); + UChar nextChar = nextText->characters()[0]; + if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { + addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); + return true; + } + } + + return false; +} + +void RenderBlock::fitBelowFloats(int widthToFit, bool firstLine, int& availableWidth) +{ + ASSERT(widthToFit > availableWidth); + + int floatLogicalBottom; + int lastFloatLogicalBottom = logicalHeight(); + int newLineWidth = availableWidth; + while (true) { + floatLogicalBottom = nextFloatLogicalBottomBelow(lastFloatLogicalBottom); + if (!floatLogicalBottom) + break; + + newLineWidth = availableLogicalWidthForLine(floatLogicalBottom, firstLine); + lastFloatLogicalBottom = floatLogicalBottom; + if (newLineWidth >= widthToFit) + break; + } + + if (newLineWidth > availableWidth) { + setLogicalHeight(lastFloatLogicalBottom); + availableWidth = newLineWidth; + } +} + +static inline unsigned textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, int xPos, bool isFixedPitch, bool collapseWhiteSpace) +{ + if (isFixedPitch || (!from && len == text->textLength())) + return text->width(from, len, font, xPos); + return font.width(TextRun(text->characters() + from, len, !collapseWhiteSpace, xPos)); +} + +static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, int lastSpace, int pos, int xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated) +{ + const AtomicString& hyphenString = text->style()->hyphenString(); + int hyphenWidth = font.width(TextRun(hyphenString.characters(), hyphenString.length())); + + int maxPrefixWidth = availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing; + // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely + // that an hyphenation opportunity exists, so do not bother to look for it. + if (maxPrefixWidth <= font.pixelSize() * 5 / 4) + return; + + unsigned prefixLength = font.offsetForPosition(TextRun(text->characters() + lastSpace, pos - lastSpace, !collapseWhiteSpace, xPos + lastSpaceWordSpacing), maxPrefixWidth, false); + if (!prefixLength) + return; + + prefixLength = lastHyphenLocation(text->characters() + lastSpace, pos - lastSpace, prefixLength + 1, localeIdentifier); + if (!prefixLength) + return; + +#if !ASSERT_DISABLED + int prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + ASSERT(xPos + prefixWidth <= availableWidth); +#else + UNUSED_PARAM(isFixedPitch); +#endif + + lineBreak.obj = text; + lineBreak.pos = lastSpace + prefixLength; + lineBreak.nextBreakablePosition = nextBreakable; + hyphenated = true; +} + +InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, bool firstLine, bool& isLineEmpty, bool& previousLineBrokeCleanly, + bool& hyphenated, EClear* clear, FloatingObject* lastFloatFromPreviousLine) +{ + ASSERT(resolver.position().block == this); + + bool appliedStartWidth = resolver.position().pos > 0; + LineMidpointState& lineMidpointState = resolver.midpointState(); + + int width = skipLeadingWhitespace(resolver, firstLine, isLineEmpty, previousLineBrokeCleanly, lastFloatFromPreviousLine); + + int w = 0; + int tmpW = 0; + + if (resolver.position().atEnd()) + return resolver.position(); + + // This variable is used only if whitespace isn't set to PRE, and it tells us whether + // or not we are currently ignoring whitespace. + bool ignoringSpaces = false; + InlineIterator ignoreStart; + + // This variable tracks whether the very last character we saw was a space. We use + // this to detect when we encounter a second space so we know we have to terminate + // a run. + bool currentCharacterIsSpace = false; + bool currentCharacterIsWS = false; + RenderObject* trailingSpaceObject = 0; + + InlineIterator lBreak = resolver.position(); + + RenderObject *o = resolver.position().obj; + RenderObject *last = o; + unsigned pos = resolver.position().pos; + int nextBreakable = resolver.position().nextBreakablePosition; + bool atStart = true; + + bool prevLineBrokeCleanly = previousLineBrokeCleanly; + previousLineBrokeCleanly = false; + + hyphenated = false; + + bool autoWrapWasEverTrueOnLine = false; + bool floatsFitOnLine = true; + + // Firefox and Opera will allow a table cell to grow to fit an image inside it under + // very specific circumstances (in order to match common WinIE renderings). + // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) + bool allowImagesToBreak = !document()->inQuirksMode() || !isTableCell() || !style()->logicalWidth().isIntrinsicOrAuto(); + + EWhiteSpace currWS = style()->whiteSpace(); + EWhiteSpace lastWS = currWS; + while (o) { + currWS = o->isReplaced() ? o->parent()->style()->whiteSpace() : o->style()->whiteSpace(); + lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace(); + + bool autoWrap = RenderStyle::autoWrap(currWS); + autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap; + +#if ENABLE(SVG) + bool preserveNewline = o->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS); +#else + bool preserveNewline = RenderStyle::preserveNewline(currWS); +#endif + + bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS); + + if (o->isBR()) { + if (w + tmpW <= width) { + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + lBreak.increment(); + + // A
    always breaks a line, so don't let the line be collapsed + // away. Also, the space at the end of a line with a
    does not + // get collapsed away. It only does this if the previous line broke + // cleanly. Otherwise the
    has no effect on whether the line is + // empty or not. + if (prevLineBrokeCleanly) + isLineEmpty = false; + trailingSpaceObject = 0; + previousLineBrokeCleanly = true; + + if (!isLineEmpty && clear) + *clear = o->style()->clear(); + } + goto end; + } + + if (o->isFloatingOrPositioned()) { + // add to special objects... + if (o->isFloating()) { + RenderBox* floatBox = toRenderBox(o); + FloatingObject* f = insertFloatingObject(floatBox); + // check if it fits in the current line. + // If it does, position it now, otherwise, position + // it after moving to next line (in newLine() func) + if (floatsFitOnLine && logicalWidthForFloat(f) + w + tmpW <= width) { + positionNewFloatOnLine(f, lastFloatFromPreviousLine); + width = availableLogicalWidthForLine(logicalHeight(), firstLine); + } else + floatsFitOnLine = false; + } else if (o->isPositioned()) { + // If our original display wasn't an inline type, then we can + // go ahead and determine our static x position now. + RenderBox* box = toRenderBox(o); + bool isInlineType = box->style()->isOriginalDisplayInlineType(); + bool needToSetStaticX = box->style()->hasStaticX(); + if (box->style()->hasStaticX() && !isInlineType) { + box->layer()->setStaticX(o->parent()->style()->isLeftToRightDirection() ? + borderLeft() + paddingLeft() : + borderRight() + paddingRight()); + needToSetStaticX = false; + } + + // If our original display was an INLINE type, then we can go ahead + // and determine our static y position now. + bool needToSetStaticY = box->style()->hasStaticY(); + if (box->style()->hasStaticY() && isInlineType) { + box->layer()->setStaticY(height()); + needToSetStaticY = false; + } + + bool needToCreateLineBox = needToSetStaticX || needToSetStaticY; + RenderObject* c = o->container(); + if (c->isRenderInline() && (!needToSetStaticX || !needToSetStaticY)) + needToCreateLineBox = true; + + // If we're ignoring spaces, we have to stop and include this object and + // then start ignoring spaces again. + if (needToCreateLineBox) { + trailingSpaceObject = 0; + ignoreStart.obj = o; + ignoreStart.pos = 0; + if (ignoringSpaces) { + addMidpoint(lineMidpointState, ignoreStart); // Stop ignoring spaces. + addMidpoint(lineMidpointState, ignoreStart); // Start ignoring again. + } + + } + } + } else if (o->isRenderInline()) { + // Right now, we should only encounter empty inlines here. + ASSERT(!o->firstChild()); + + RenderInline* flowBox = toRenderInline(o); + + // Now that some inline flows have line boxes, if we are already ignoring spaces, we need + // to make sure that we stop to include this object and then start ignoring spaces again. + // If this object is at the start of the line, we need to behave like list markers and + // start ignoring spaces. + if (inlineFlowRequiresLineBox(flowBox)) { + isLineEmpty = false; + if (ignoringSpaces) { + trailingSpaceObject = 0; + addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); // Stop ignoring spaces. + addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); // Start ignoring again. + } else if (style()->collapseWhiteSpace() && resolver.position().obj == o + && shouldSkipWhitespaceAfterStartObject(this, o, lineMidpointState)) { + // Like with list markers, we start ignoring spaces to make sure that any + // additional spaces we see will be discarded. + currentCharacterIsSpace = true; + currentCharacterIsWS = true; + ignoringSpaces = true; + } + } + + tmpW += flowBox->marginStart() + flowBox->borderStart() + flowBox->paddingStart() + + flowBox->marginEnd() + flowBox->borderEnd() + flowBox->paddingEnd(); + } else if (o->isReplaced()) { + RenderBox* replacedBox = toRenderBox(o); + + // Break on replaced elements if either has normal white-space. + if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) { + w += tmpW; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + + if (ignoringSpaces) + addMidpoint(lineMidpointState, InlineIterator(0, o, 0)); + + isLineEmpty = false; + ignoringSpaces = false; + currentCharacterIsSpace = false; + currentCharacterIsWS = false; + trailingSpaceObject = 0; + + // Optimize for a common case. If we can't find whitespace after the list + // item, then this is all moot. + int replacedLogicalWidth = logicalWidthForChild(replacedBox) + marginStartForChild(replacedBox) + marginEndForChild(replacedBox) + inlineLogicalWidth(o); + if (o->isListMarker()) { + if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o, lineMidpointState)) { + // Like with inline flows, we start ignoring spaces to make sure that any + // additional spaces we see will be discarded. + currentCharacterIsSpace = true; + currentCharacterIsWS = true; + ignoringSpaces = true; + } + if (toRenderListMarker(o)->isInside()) + tmpW += replacedLogicalWidth; + } else + tmpW += replacedLogicalWidth; + } else if (o->isText()) { + if (!pos) + appliedStartWidth = false; + + RenderText* t = toRenderText(o); + +#if ENABLE(SVG) + bool isSVGText = t->isSVGInlineText(); +#endif + + int strlen = t->textLength(); + int len = strlen - pos; + const UChar* str = t->characters(); + + RenderStyle* style = t->style(firstLine); + const Font& f = style->font(); + bool isFixedPitch = f.isFixedPitch(); + bool canHyphenate = style->hyphens() == HyphensAuto && WebCore::canHyphenate(style->hyphenationLocale()); + + int lastSpace = pos; + int wordSpacing = o->style()->wordSpacing(); + int lastSpaceWordSpacing = 0; + + // Non-zero only when kerning is enabled, in which case we measure words with their trailing + // space, then subtract its width. + int wordTrailingSpaceWidth = f.typesettingFeatures() & Kerning ? f.width(TextRun(&space, 1)) + wordSpacing : 0; + + int wrapW = tmpW + inlineLogicalWidth(o, !appliedStartWidth, true); + int charWidth = 0; + bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE; + // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, + // which is only possible if the word is the first thing on the line, that is, if |w| is zero. + bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE); + bool midWordBreak = false; + bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap; + int hyphenWidth = 0; + + if (t->isWordBreak()) { + w += tmpW; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + ASSERT(!len); + } + + while (len) { + bool previousCharacterIsSpace = currentCharacterIsSpace; + bool previousCharacterIsWS = currentCharacterIsWS; + UChar c = str[pos]; + currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n')); + + if (!collapseWhiteSpace || !currentCharacterIsSpace) + isLineEmpty = false; + + if (c == softHyphen && autoWrap && !hyphenWidth && style->hyphens() != HyphensNone) { + const AtomicString& hyphenString = style->hyphenString(); + hyphenWidth = f.width(TextRun(hyphenString.characters(), hyphenString.length())); + tmpW += hyphenWidth; + } + +#if ENABLE(SVG) + if (isSVGText) { + RenderSVGInlineText* svgInlineText = static_cast(t); + if (pos > 0) { + if (svgInlineText->characterStartsNewTextChunk(pos)) { + addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); + addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); + } + } + } +#endif + + bool applyWordSpacing = false; + + currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace); + + if ((breakAll || breakWords) && !midWordBreak) { + wrapW += charWidth; + charWidth = textWidth(t, pos, 1, f, w + wrapW, isFixedPitch, collapseWhiteSpace); + midWordBreak = w + wrapW + charWidth > width; + } + + bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP) && (style->hyphens() != HyphensNone || (pos && str[pos - 1] != softHyphen))); + + if (betweenWords || midWordBreak) { + bool stoppedIgnoringSpaces = false; + if (ignoringSpaces) { + if (!currentCharacterIsSpace) { + // Stop ignoring spaces and begin at this + // new point. + ignoringSpaces = false; + lastSpaceWordSpacing = 0; + lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces. + addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); + stoppedIgnoringSpaces = true; + } else { + // Just keep ignoring these spaces. + pos++; + len--; + continue; + } + } + + int additionalTmpW; + if (wordTrailingSpaceWidth && currentCharacterIsSpace) + additionalTmpW = textWidth(t, lastSpace, pos + 1 - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) - wordTrailingSpaceWidth + lastSpaceWordSpacing; + else + additionalTmpW = textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + tmpW += additionalTmpW; + if (!appliedStartWidth) { + tmpW += inlineLogicalWidth(o, true, false); + appliedStartWidth = true; + } + + applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace; + + if (!w && autoWrap && tmpW > width) + fitBelowFloats(tmpW, firstLine, width); + + if (autoWrap || breakWords) { + // If we break only after white-space, consider the current character + // as candidate width for this line. + bool lineWasTooWide = false; + if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) { + int charWidth = textWidth(t, pos, 1, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + (applyWordSpacing ? wordSpacing : 0); + // Check if line is too big even without the extra space + // at the end of the line. If it is not, do nothing. + // If the line needs the extra whitespace to be too long, + // then move the line break to the space and skip all + // additional whitespace. + if (w + tmpW + charWidth > width) { + lineWasTooWide = true; + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + skipTrailingWhitespace(lBreak, isLineEmpty, previousLineBrokeCleanly); + } + } + if (lineWasTooWide || w + tmpW > width) { + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, style->hyphenationLocale(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } + if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && !toRenderText(lBreak.obj)->isWordBreak() && toRenderText(lBreak.obj)->characters()[lBreak.pos] == '\n') { + if (!stoppedIgnoringSpaces && pos > 0) { + // We need to stop right before the newline and then start up again. + addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop + addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); // Start + } + lBreak.increment(); + previousLineBrokeCleanly = true; + } + if (lBreak.obj && lBreak.pos && lBreak.obj->isText() && toRenderText(lBreak.obj)->textLength() && toRenderText(lBreak.obj)->characters()[lBreak.pos - 1] == softHyphen && style->hyphens() != HyphensNone) + hyphenated = true; + goto end; // Didn't fit. Jump to the end. + } else { + if (!betweenWords || (midWordBreak && !autoWrap)) + tmpW -= additionalTmpW; + if (hyphenWidth) { + // Subtract the width of the soft hyphen out since we fit on a line. + tmpW -= hyphenWidth; + hyphenWidth = 0; + } + } + } + + if (c == '\n' && preserveNewline) { + if (!stoppedIgnoringSpaces && pos > 0) { + // We need to stop right before the newline and then start up again. + addMidpoint(lineMidpointState, InlineIterator(0, o, pos - 1)); // Stop + addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); // Start + } + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + lBreak.increment(); + previousLineBrokeCleanly = true; + return lBreak; + } + + if (autoWrap && betweenWords) { + w += tmpW; + wrapW = 0; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + // Auto-wrapping text should not wrap in the middle of a word once it has had an + // opportunity to break after a word. + breakWords = false; + } + + if (midWordBreak) { + // Remember this as a breakable position in case + // adding the end width forces a break. + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + midWordBreak &= (breakWords || breakAll); + } + + if (betweenWords) { + lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + lastSpace = pos; + } + + if (!ignoringSpaces && o->style()->collapseWhiteSpace()) { + // If we encounter a newline, or if we encounter a + // second space, we need to go ahead and break up this + // run and enter a mode where we start collapsing spaces. + if (currentCharacterIsSpace && previousCharacterIsSpace) { + ignoringSpaces = true; + + // We just entered a mode where we are ignoring + // spaces. Create a midpoint to terminate the run + // before the second space. + addMidpoint(lineMidpointState, ignoreStart); + } + } + } else if (ignoringSpaces) { + // Stop ignoring spaces and begin at this + // new point. + ignoringSpaces = false; + lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces. + addMidpoint(lineMidpointState, InlineIterator(0, o, pos)); + } + + if (currentCharacterIsSpace && !previousCharacterIsSpace) { + ignoreStart.obj = o; + ignoreStart.pos = pos; + } + + if (!currentCharacterIsWS && previousCharacterIsWS) { + if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) { + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + } + } + + if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces) + trailingSpaceObject = o; + else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace) + trailingSpaceObject = 0; + + pos++; + len--; + atStart = false; + } + + // IMPORTANT: pos is > length here! + int additionalTmpW = ignoringSpaces ? 0 : textWidth(t, lastSpace, pos - lastSpace, f, w + tmpW, isFixedPitch, collapseWhiteSpace) + lastSpaceWordSpacing; + tmpW += additionalTmpW; + tmpW += inlineLogicalWidth(o, !appliedStartWidth, true); + + if (canHyphenate && w + tmpW > width) { + tryHyphenating(t, f, style->hyphenationLocale(), lastSpace, pos, w + tmpW - additionalTmpW, width, isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, nextBreakable, hyphenated); + if (hyphenated) + goto end; + } + } else + ASSERT_NOT_REACHED(); + + RenderObject* next = bidiNext(this, o); + bool checkForBreak = autoWrap; + if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP) + checkForBreak = true; + else if (next && o->isText() && next->isText() && !next->isBR()) { + if (autoWrap || (next->style()->autoWrap())) { + if (currentCharacterIsSpace) + checkForBreak = true; + else { + checkForBreak = false; + RenderText* nextText = toRenderText(next); + if (nextText->textLength()) { + UChar c = nextText->characters()[0]; + if (c == ' ' || c == '\t' || (c == '\n' && !shouldPreserveNewline(next))) + // If the next item on the line is text, and if we did not end with + // a space, then the next text run continues our word (and so it needs to + // keep adding to |tmpW|. Just update and continue. + checkForBreak = true; + } else if (nextText->isWordBreak()) + checkForBreak = true; + bool willFitOnLine = w + tmpW <= width; + if (!willFitOnLine && !w) { + fitBelowFloats(tmpW, firstLine, width); + willFitOnLine = tmpW <= width; + } + bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine; + if (canPlaceOnLine && checkForBreak) { + w += tmpW; + tmpW = 0; + lBreak.obj = next; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + } + } + } + + if (checkForBreak && (w + tmpW > width)) { + // if we have floats, try to get below them. + if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace()) + trailingSpaceObject = 0; + + if (w) + goto end; + + fitBelowFloats(tmpW, firstLine, width); + + // |width| may have been adjusted because we got shoved down past a float (thus + // giving us more room), so we need to retest, and only jump to + // the end label if we still don't fit on the line. -dwh + if (w + tmpW > width) + goto end; + } + + if (!o->isFloatingOrPositioned()) { + last = o; + if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) { + w += tmpW; + tmpW = 0; + lBreak.obj = next; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + } + + o = next; + nextBreakable = -1; + + // Clear out our character space bool, since inline
    s don't collapse whitespace
    +        // with adjacent inline normal/nowrap spans.
    +        if (!collapseWhiteSpace)
    +            currentCharacterIsSpace = false;
    +        
    +        pos = 0;
    +        atStart = false;
    +    }
    +
    +    
    +    if (w + tmpW <= width || lastWS == NOWRAP) {
    +        lBreak.obj = 0;
    +        lBreak.pos = 0;
    +        lBreak.nextBreakablePosition = -1;
    +    }
    +
    + end:
    +    if (lBreak == resolver.position() && (!lBreak.obj || !lBreak.obj->isBR())) {
    +        // we just add as much as possible
    +        if (style()->whiteSpace() == PRE) {
    +            // FIXME: Don't really understand this case.
    +            if (pos != 0) {
    +                lBreak.obj = o;
    +                lBreak.pos = pos - 1;
    +            } else {
    +                lBreak.obj = last;
    +                lBreak.pos = last->isText() ? last->length() : 0;
    +                lBreak.nextBreakablePosition = -1;
    +            }
    +        } else if (lBreak.obj) {
    +            // Don't ever break in the middle of a word if we can help it.
    +            // There's no room at all. We just have to be on this line,
    +            // even though we'll spill out.
    +            lBreak.obj = o;
    +            lBreak.pos = pos;
    +            lBreak.nextBreakablePosition = -1;
    +        }
    +    }
    +
    +    // make sure we consume at least one char/object.
    +    if (lBreak == resolver.position())
    +        lBreak.increment();
    +
    +    // Sanity check our midpoints.
    +    checkMidpoints(lineMidpointState, lBreak);
    +        
    +    if (trailingSpaceObject) {
    +        // This object is either going to be part of the last midpoint, or it is going
    +        // to be the actual endpoint.  In both cases we just decrease our pos by 1 level to
    +        // exclude the space, allowing it to - in effect - collapse into the newline.
    +        if (lineMidpointState.numMidpoints % 2) {
    +            InlineIterator* midpoints = lineMidpointState.midpoints.data();
    +            midpoints[lineMidpointState.numMidpoints - 1].pos--;
    +        }
    +        //else if (lBreak.pos > 0)
    +        //    lBreak.pos--;
    +        else if (lBreak.obj == 0 && trailingSpaceObject->isText()) {
    +            // Add a new end midpoint that stops right at the very end.
    +            RenderText* text = toRenderText(trailingSpaceObject);
    +            unsigned length = text->textLength();
    +            unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
    +            InlineIterator endMid(0, trailingSpaceObject, pos);
    +            addMidpoint(lineMidpointState, endMid);
    +        }
    +    }
    +
    +    // We might have made lBreak an iterator that points past the end
    +    // of the object. Do this adjustment to make it point to the start
    +    // of the next object instead to avoid confusing the rest of the
    +    // code.
    +    if (lBreak.pos > 0) {
    +        lBreak.pos--;
    +        lBreak.increment();
    +    }
    +
    +    return lBreak;
    +}
    +
    +void RenderBlock::addOverflowFromInlineChildren()
    +{
    +    int endPadding = hasOverflowClip() ? paddingEnd() : 0;
    +    // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
    +    if (hasOverflowClip() && !endPadding && node() && node()->isContentEditable() && node() == node()->rootEditableElement() && style()->isLeftToRightDirection())
    +        endPadding = 1;
    +    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
    +        addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
    +        if (!hasOverflowClip())
    +            addVisualOverflow(curr->visualOverflowRect());
    +    }
    +}
    +
    +int RenderBlock::beforeSideVisualOverflowForLine(RootInlineBox* line) const
    +{
    +    // Overflow is in the block's coordinate space, which means it isn't purely physical.  For flipped blocks (rl and bt),
    +    // we continue to use top and left overflow even though physically it's bottom and right.
    +    if (style()->isHorizontalWritingMode())
    +        return line->topVisualOverflow();
    +    return line->leftVisualOverflow();
    +}
    +
    +int RenderBlock::afterSideVisualOverflowForLine(RootInlineBox* line) const
    +{
    +    // Overflow is in the block's coordinate space, which means it isn't purely physical.  For flipped blocks (rl and bt),
    +    // we continue to use bottom and right overflow even though physically it's top and left.
    +    if (style()->isHorizontalWritingMode())
    +        return line->bottomVisualOverflow();
    +    return line->rightVisualOverflow();
    +}
    +
    +int RenderBlock::beforeSideLayoutOverflowForLine(RootInlineBox* line) const
    +{
    +    // Overflow is in the block's coordinate space, which means it isn't purely physical.  For flipped blocks (rl and bt),
    +    // we continue to use top and left overflow even though physically it's bottom and right.
    +    if (style()->isHorizontalWritingMode())
    +        return line->topLayoutOverflow();
    +    return line->leftLayoutOverflow();
    +}
    +
    +int RenderBlock::afterSideLayoutOverflowForLine(RootInlineBox* line) const
    +{
    +    // Overflow is in the block's coordinate space, which means it isn't purely physical.  For flipped blocks (rl and bt),
    +    // we continue to use bottom and right overflow even though physically it's top and left.
    +    if (style()->isHorizontalWritingMode())
    +        return line->bottomLayoutOverflow();
    +    return line->rightLayoutOverflow();
    +}
    +
    +void RenderBlock::deleteEllipsisLineBoxes()
    +{
    +    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox())
    +        curr->clearTruncation();
    +}
    +
    +void RenderBlock::checkLinesForTextOverflow()
    +{
    +    // Determine the width of the ellipsis using the current font.
    +    // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable"
    +    TextRun ellipsisRun(&horizontalEllipsis, 1);
    +    DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1));
    +    const Font& firstLineFont = firstLineStyle()->font();
    +    const Font& font = style()->font();
    +    int firstLineEllipsisWidth = firstLineFont.width(ellipsisRun);
    +    int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(ellipsisRun);
    +
    +    // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
    +    // if the right edge of a line box exceeds that.  For RTL, we use the left edge of the padding box and
    +    // check the left edge of the line box to see if it is less
    +    // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
    +    bool ltr = style()->isLeftToRightDirection();
    +    for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
    +        int blockRightEdge = logicalRightOffsetForLine(curr->y(), curr == firstRootBox());
    +        int blockLeftEdge = logicalLeftOffsetForLine(curr->y(), curr == firstRootBox());
    +        int lineBoxEdge = ltr ? curr->x() + curr->logicalWidth() : curr->x();
    +        if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) {
    +            // This line spills out of our box in the appropriate direction.  Now we need to see if the line
    +            // can be truncated.  In order for truncation to be possible, the line must have sufficient space to
    +            // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis
    +            // space.
    +            int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth;
    +            int blockEdge = ltr ? blockRightEdge : blockLeftEdge;
    +            if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width))
    +                curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width);
    +        }
    +    }
    +}
    +
    +}
    diff --git a/Source/WebCore/rendering/RenderBox.cpp b/Source/WebCore/rendering/RenderBox.cpp
    new file mode 100644
    index 0000000..355d385
    --- /dev/null
    +++ b/Source/WebCore/rendering/RenderBox.cpp
    @@ -0,0 +1,3428 @@
    +/*
    + * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
    + *           (C) 1999 Antti Koivisto (koivisto@kde.org)
    + *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
    + *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
    + * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Library General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    + * Library General Public License for more details.
    + *
    + * You should have received a copy of the GNU Library General Public License
    + * along with this library; see the file COPYING.LIB.  If not, write to
    + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    + * Boston, MA 02110-1301, USA.
    + *
    + */
    +
    +#include "config.h"
    +#include "RenderBox.h"
    +
    +#include "CachedImage.h"
    +#include "Chrome.h"
    +#include "ChromeClient.h"
    +#include "Document.h"
    +#include "FrameView.h"
    +#include "GraphicsContext.h"
    +#include "HitTestResult.h"
    +#include "htmlediting.h"
    +#include "HTMLElement.h"
    +#include "HTMLNames.h"
    +#include "ImageBuffer.h"
    +#include "FloatQuad.h"
    +#include "Frame.h"
    +#include "Page.h"
    +#if PLATFORM(ANDROID)
    +#include "PlatformBridge.h"
    +#endif
    +#include "RenderArena.h"
    +#include "RenderFlexibleBox.h"
    +#include "RenderInline.h"
    +#include "RenderLayer.h"
    +#include "RenderTableCell.h"
    +#include "RenderTheme.h"
    +#ifdef ANDROID_LAYOUT
    +#include "Settings.h"
    +#endif
    +#include "RenderView.h"
    +#include "TransformState.h"
    +#include 
    +#include 
    +
    +#if ENABLE(WML)
    +#include "WMLNames.h"
    +#endif
    +
    +using namespace std;
    +
    +namespace WebCore {
    +
    +using namespace HTMLNames;
    +
    +// Used by flexible boxes when flexing this element.
    +typedef WTF::HashMap OverrideSizeMap;
    +static OverrideSizeMap* gOverrideSizeMap = 0;
    +
    +bool RenderBox::s_hadOverflowClip = false;
    +
    +RenderBox::RenderBox(Node* node)
    +    : RenderBoxModelObject(node)
    +    , m_marginLeft(0)
    +    , m_marginRight(0)
    +    , m_marginTop(0)
    +    , m_marginBottom(0)
    +    , m_minPreferredLogicalWidth(-1)
    +    , m_maxPreferredLogicalWidth(-1)
    +    , m_inlineBoxWrapper(0)
    +#ifdef ANDROID_LAYOUT
    +    , m_visibleWidth(0)
    +    , m_isVisibleWidthChangedBeforeLayout(false)
    +#endif
    +{
    +    setIsBox();
    +}
    +
    +RenderBox::~RenderBox()
    +{
    +}
    +
    +int RenderBox::marginBefore() const
    +{
    +    switch (style()->writingMode()) {
    +    case TopToBottomWritingMode:
    +        return m_marginTop;
    +    case BottomToTopWritingMode:
    +        return m_marginBottom;
    +    case LeftToRightWritingMode:
    +        return m_marginLeft;
    +    case RightToLeftWritingMode:
    +        return m_marginRight;
    +    }
    +    ASSERT_NOT_REACHED();
    +    return m_marginTop;
    +}
    +
    +int RenderBox::marginAfter() const
    +{
    +    switch (style()->writingMode()) {
    +    case TopToBottomWritingMode:
    +        return m_marginBottom;
    +    case BottomToTopWritingMode:
    +        return m_marginTop;
    +    case LeftToRightWritingMode:
    +        return m_marginRight;
    +    case RightToLeftWritingMode:
    +        return m_marginLeft;
    +    }
    +    ASSERT_NOT_REACHED();
    +    return m_marginBottom;
    +}
    +
    +int RenderBox::marginStart() const
    +{
    +    if (style()->isHorizontalWritingMode())
    +        return style()->isLeftToRightDirection() ? m_marginLeft : m_marginRight;
    +    return style()->isLeftToRightDirection() ? m_marginTop : m_marginBottom;
    +}
    +
    +int RenderBox::marginEnd() const
    +{
    +    if (style()->isHorizontalWritingMode())
    +        return style()->isLeftToRightDirection() ? m_marginRight : m_marginLeft;
    +    return style()->isLeftToRightDirection() ? m_marginBottom : m_marginTop;
    +}
    +
    +void RenderBox::setMarginStart(int margin)
    +{
    +    if (style()->isHorizontalWritingMode()) {
    +        if (style()->isLeftToRightDirection())
    +            m_marginLeft = margin;
    +        else
    +            m_marginRight = margin;
    +    } else {
    +        if (style()->isLeftToRightDirection())
    +            m_marginTop = margin;
    +        else
    +            m_marginBottom = margin;
    +    }
    +}
    +
    +void RenderBox::setMarginEnd(int margin)
    +{
    +    if (style()->isHorizontalWritingMode()) {
    +        if (style()->isLeftToRightDirection())
    +            m_marginRight = margin;
    +        else
    +            m_marginLeft = margin;
    +    } else {
    +        if (style()->isLeftToRightDirection())
    +            m_marginBottom = margin;
    +        else
    +            m_marginTop = margin;
    +    }
    +}
    +
    +void RenderBox::setMarginBefore(int margin)
    +{
    +    switch (style()->writingMode()) {
    +    case TopToBottomWritingMode:
    +        m_marginTop = margin;
    +        break;
    +    case BottomToTopWritingMode:
    +        m_marginBottom = margin;
    +        break;
    +    case LeftToRightWritingMode:
    +        m_marginLeft = margin;
    +        break;
    +    case RightToLeftWritingMode:
    +        m_marginRight = margin;
    +        break;
    +    }
    +}
    +
    +void RenderBox::setMarginAfter(int margin)
    +{
    +    switch (style()->writingMode()) {
    +    case TopToBottomWritingMode:
    +        m_marginBottom = margin;
    +        break;
    +    case BottomToTopWritingMode:
    +        m_marginTop = margin;
    +        break;
    +    case LeftToRightWritingMode:
    +        m_marginRight = margin;
    +        break;
    +    case RightToLeftWritingMode:
    +        m_marginLeft = margin;
    +        break;
    +    }
    +}
    +
    +void RenderBox::destroy()
    +{
    +    // A lot of the code in this function is just pasted into
    +    // RenderWidget::destroy. If anything in this function changes,
    +    // be sure to fix RenderWidget::destroy() as well.
    +    if (hasOverrideSize())
    +        gOverrideSizeMap->remove(this);
    +
    +    if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent()))
    +        RenderBlock::removePercentHeightDescendant(this);
    +
    +    RenderBoxModelObject::destroy();
    +}
    +
    +void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
    +{
    +    ASSERT(isFloatingOrPositioned());
    +
    +    if (documentBeingDestroyed())
    +        return;
    +
    +    if (isFloating()) {
    +        RenderBlock* outermostBlock = containingBlock();
    +        for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) {
    +            if (p->containsFloat(this))
    +                outermostBlock = p;
    +        }
    +
    +        if (outermostBlock) {
    +            RenderObject* parent = outermostBlock->parent();
    +            if (parent && parent->isFlexibleBox())
    +                outermostBlock = toRenderBlock(parent);
    +
    +            outermostBlock->markAllDescendantsWithFloatsForLayout(this, false);
    +        }
    +    }
    +
    +    if (isPositioned()) {
    +        RenderObject* p;
    +        for (p = parent(); p; p = p->parent()) {
    +            if (p->isRenderBlock())
    +                toRenderBlock(p)->removePositionedObject(this);
    +        }
    +    }
    +}
    +
    +void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
    +{
    +    s_hadOverflowClip = hasOverflowClip();
    +
    +    if (style()) {
    +        // The background of the root element or the body element could propagate up to
    +        // the canvas.  Just dirty the entire canvas when our style changes substantially.
    +        if (diff >= StyleDifferenceRepaint && node() &&
    +                (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag)))
    +            view()->repaint();
    +        
    +        // When a layout hint happens and an object's position style changes, we have to do a layout
    +        // to dirty the render tree using the old position value now.
    +        if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) {
    +            markContainingBlocksForLayout();
    +            if (style()->position() == StaticPosition)
    +                repaint();
    +            else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)
    +                parent()->setChildNeedsLayout(true);
    +            if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition))
    +                removeFloatingOrPositionedChildFromBlockLists();
    +        }
    +    } else if (newStyle && isBody())
    +        view()->repaint();
    +
    +    if (FrameView *frameView = view()->frameView()) {
    +        bool newStyleIsFixed = newStyle && newStyle->position() == FixedPosition;
    +        bool oldStyleIsFixed = style() && style()->position() == FixedPosition;
    +        if (newStyleIsFixed != oldStyleIsFixed) {
    +            if (newStyleIsFixed)
    +                frameView->addFixedObject();
    +            else
    +                frameView->removeFixedObject();
    +        }
    +    }
    +
    +    RenderBoxModelObject::styleWillChange(diff, newStyle);
    +}
    +
    +void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
    +{
    +    RenderBoxModelObject::styleDidChange(diff, oldStyle);
    +
    +    if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent()))
    +        RenderBlock::removePercentHeightDescendant(this);
    +
    +    // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
    +    // new zoomed coordinate space.
    +    if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) {
    +        if (int left = layer()->scrollXOffset()) {
    +            left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom();
    +            layer()->scrollToXOffset(left);
    +        }
    +        if (int top = layer()->scrollYOffset()) {
    +            top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom();
    +            layer()->scrollToYOffset(top);
    +        }
    +    }
    +
    +    bool isBodyRenderer = isBody();
    +    bool isRootRenderer = isRoot();
    +
    +    // Set the text color if we're the body.
    +    if (isBodyRenderer)
    +        document()->setTextColor(style()->visitedDependentColor(CSSPropertyColor));
    +
    +    if (isRootRenderer || isBodyRenderer) {
    +        // Propagate the new writing mode and direction up to the RenderView.
    +        RenderView* viewRenderer = view();
    +        RenderStyle* viewStyle = viewRenderer->style();
    +        if (viewStyle->direction() != style()->direction() && (isRootRenderer || !document()->directionSetOnDocumentElement())) {
    +            viewStyle->setDirection(style()->direction());
    +            if (isBodyRenderer)
    +                document()->documentElement()->renderer()->style()->setDirection(style()->direction());
    +            setNeedsLayoutAndPrefWidthsRecalc();
    +        }
    +
    +        if (viewStyle->writingMode() != style()->writingMode() && (isRootRenderer || !document()->writingModeSetOnDocumentElement())) {
    +            viewStyle->setWritingMode(style()->writingMode());
    +            if (isBodyRenderer)
    +                document()->documentElement()->renderer()->style()->setWritingMode(style()->writingMode());
    +            setNeedsLayoutAndPrefWidthsRecalc();
    +        }
    +    }
    +}
    +
    +void RenderBox::updateBoxModelInfoFromStyle()
    +{
    +    RenderBoxModelObject::updateBoxModelInfoFromStyle();
    +
    +    bool isRootObject = isRoot();
    +    bool isViewObject = isRenderView();
    +
    +    // The root and the RenderView always paint their backgrounds/borders.
    +    if (isRootObject || isViewObject)
    +        setHasBoxDecorations(true);
    +
    +    setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition);
    +    setFloating(!isPositioned() && style()->isFloating());
    +
    +    // We also handle  and , whose overflow applies to the viewport.
    +    if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) {
    +        bool boxHasOverflowClip = true;
    +        if (isBody()) {
    +            // Overflow on the body can propagate to the viewport under the following conditions.
    +            // (1) The root element is .
    +            // (2) We are the primary  (can be checked by looking at document.body).
    +            // (3) The root element has visible overflow.
    +            if (document()->documentElement()->hasTagName(htmlTag) &&
    +                document()->body() == node() &&
    +                document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE)
    +                boxHasOverflowClip = false;
    +        }
    +        
    +        // Check for overflow clip.
    +        // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
    +        if (boxHasOverflowClip) {
    +            if (!s_hadOverflowClip)
    +                // Erase the overflow
    +                repaint();
    +            setHasOverflowClip();
    +        }
    +    }
    +
    +    setHasTransform(style()->hasTransformRelatedProperty());
    +    setHasReflection(style()->boxReflect());
    +}
    +
    +void RenderBox::layout()
    +{
    +    ASSERT(needsLayout());
    +
    +    RenderObject* child = firstChild();
    +    if (!child) {
    +        setNeedsLayout(false);
    +        return;
    +    }
    +
    +    LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
    +    while (child) {
    +        child->layoutIfNeeded();
    +        ASSERT(!child->needsLayout());
    +        child = child->nextSibling();
    +    }
    +    statePusher.pop();
    +    setNeedsLayout(false);
    +}
    +
    +// More IE extensions.  clientWidth and clientHeight represent the interior of an object
    +// excluding border and scrollbar.
    +int RenderBox::clientWidth() const
    +{
    +    return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
    +}
    +
    +int RenderBox::clientHeight() const
    +{
    +    return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
    +}
    +
    +int RenderBox::scrollWidth() const
    +{
    +    if (hasOverflowClip())
    +        return layer()->scrollWidth();
    +    // For objects with visible overflow, this matches IE.
    +    // FIXME: Need to work right with writing modes.
    +    if (style()->isLeftToRightDirection())
    +        return max(clientWidth(), rightLayoutOverflow() - borderLeft());
    +    return clientWidth() - min(0, leftLayoutOverflow() - borderLeft());
    +}
    +
    +int RenderBox::scrollHeight() const
    +{
    +    if (hasOverflowClip())
    +        return layer()->scrollHeight();
    +    // For objects with visible overflow, this matches IE.
    +    // FIXME: Need to work right with writing modes.
    +    return max(clientHeight(), bottomLayoutOverflow() - borderTop());
    +}
    +
    +int RenderBox::scrollLeft() const
    +{
    +    return hasOverflowClip() ? layer()->scrollXOffset() : 0;
    +}
    +
    +int RenderBox::scrollTop() const
    +{
    +    return hasOverflowClip() ? layer()->scrollYOffset() : 0;
    +}
    +
    +void RenderBox::setScrollLeft(int newLeft)
    +{
    +    if (hasOverflowClip())
    +        layer()->scrollToXOffset(newLeft);
    +}
    +
    +void RenderBox::setScrollTop(int newTop)
    +{
    +    if (hasOverflowClip())
    +        layer()->scrollToYOffset(newTop);
    +}
    +
    +void RenderBox::absoluteRects(Vector& rects, int tx, int ty)
    +{
    +    rects.append(IntRect(tx, ty, width(), height()));
    +}
    +
    +void RenderBox::absoluteQuads(Vector& quads)
    +{
    +    quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height())));
    +}
    +
    +void RenderBox::updateLayerTransform()
    +{
    +    // Transform-origin depends on box size, so we need to update the layer transform after layout.
    +    if (hasLayer())
    +        layer()->updateTransform();
    +}
    +
    +IntRect RenderBox::absoluteContentBox() const
    +{
    +    IntRect rect = contentBoxRect();
    +    FloatPoint absPos = localToAbsolute(FloatPoint());
    +    rect.move(absPos.x(), absPos.y());
    +    return rect;
    +}
    +
    +FloatQuad RenderBox::absoluteContentQuad() const
    +{
    +    IntRect rect = contentBoxRect();
    +    return localToAbsoluteQuad(FloatRect(rect));
    +}
    +
    +IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint* cachedOffsetToRepaintContainer) const
    +{
    +    IntRect box = borderBoundingBox();
    +    adjustRectForOutlineAndShadow(box);
    +
    +    FloatQuad containerRelativeQuad = FloatRect(box);
    +    if (cachedOffsetToRepaintContainer)
    +        containerRelativeQuad.move(cachedOffsetToRepaintContainer->x(), cachedOffsetToRepaintContainer->y());
    +    else
    +        containerRelativeQuad = localToContainerQuad(containerRelativeQuad, repaintContainer);
    +
    +    box = containerRelativeQuad.enclosingBoundingBox();
    +
    +    // FIXME: layoutDelta needs to be applied in parts before/after transforms and
    +    // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
    +    box.move(view()->layoutDelta());
    +
    +    return box;
    +}
    +
    +void RenderBox::addFocusRingRects(Vector& rects, int tx, int ty)
    +{
    +    if (width() && height())
    +        rects.append(IntRect(tx, ty, width(), height()));
    +}
    +
    +IntRect RenderBox::reflectionBox() const
    +{
    +    IntRect result;
    +    if (!style()->boxReflect())
    +        return result;
    +    IntRect box = borderBoxRect();
    +    result = box;
    +    switch (style()->boxReflect()->direction()) {
    +        case ReflectionBelow:
    +            result.move(0, box.height() + reflectionOffset());
    +            break;
    +        case ReflectionAbove:
    +            result.move(0, -box.height() - reflectionOffset());
    +            break;
    +        case ReflectionLeft:
    +            result.move(-box.width() - reflectionOffset(), 0);
    +            break;
    +        case ReflectionRight:
    +            result.move(box.width() + reflectionOffset(), 0);
    +            break;
    +    }
    +    return result;
    +}
    +
    +int RenderBox::reflectionOffset() const
    +{
    +    if (!style()->boxReflect())
    +        return 0;
    +    if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
    +        return style()->boxReflect()->offset().calcValue(borderBoxRect().width());
    +    return style()->boxReflect()->offset().calcValue(borderBoxRect().height());
    +}
    +
    +IntRect RenderBox::reflectedRect(const IntRect& r) const
    +{
    +    if (!style()->boxReflect())
    +        return IntRect();
    +
    +    IntRect box = borderBoxRect();
    +    IntRect result = r;
    +    switch (style()->boxReflect()->direction()) {
    +        case ReflectionBelow:
    +            result.setY(box.bottom() + reflectionOffset() + (box.bottom() - r.bottom()));
    +            break;
    +        case ReflectionAbove:
    +            result.setY(box.y() - reflectionOffset() - box.height() + (box.bottom() - r.bottom()));
    +            break;
    +        case ReflectionLeft:
    +            result.setX(box.x() - reflectionOffset() - box.width() + (box.right() - r.right()));
    +            break;
    +        case ReflectionRight:
    +            result.setX(box.right() + reflectionOffset() + (box.right() - r.right()));
    +            break;
    +    }
    +    return result;
    +}
    +
    +int RenderBox::verticalScrollbarWidth() const
    +{
    +    return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0;
    +}
    +
    +int RenderBox::horizontalScrollbarHeight() const
    +{
    +    return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0;
    +}
    +
    +bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
    +{
    +    RenderLayer* l = layer();
    +    if (l && l->scroll(direction, granularity, multiplier)) {
    +        if (stopNode)
    +            *stopNode = node();
    +        return true;
    +    }
    +
    +    if (stopNode && *stopNode && *stopNode == node())
    +        return true;
    +
    +    RenderBlock* b = containingBlock();
    +    if (b && !b->isRenderView())
    +        return b->scroll(direction, granularity, multiplier, stopNode);
    +    return false;
    +}
    +
    +bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
    +{
    +    bool scrolled = false;
    +    
    +    RenderLayer* l = layer();
    +    if (l) {
    +#if PLATFORM(MAC)
    +        // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End).
    +        if (granularity == ScrollByDocument)
    +            scrolled = l->scroll(logicalToPhysical(ScrollInlineDirectionBackward, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), ScrollByDocument, multiplier);
    +#endif
    +        if (l->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier))
    +            scrolled = true;
    +        
    +        if (scrolled) {
    +            if (stopNode)
    +                *stopNode = node();
    +            return true;
    +        }
    +    }
    +
    +    if (stopNode && *stopNode && *stopNode == node())
    +        return true;
    +
    +    RenderBlock* b = containingBlock();
    +    if (b && !b->isRenderView())
    +        return b->logicalScroll(direction, granularity, multiplier, stopNode);
    +    return false;
    +}
    +
    +bool RenderBox::canBeScrolledAndHasScrollableArea() const
    +{
    +    return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
    +}
    +    
    +bool RenderBox::canBeProgramaticallyScrolled(bool) const
    +{
    +    return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode());
    +}
    +
    +void RenderBox::autoscroll()
    +{
    +    if (layer())
    +        layer()->autoscroll();
    +}
    +
    +void RenderBox::panScroll(const IntPoint& source)
    +{
    +    if (layer())
    +        layer()->panScrollFromPoint(source);
    +}
    +
    +int RenderBox::minPreferredLogicalWidth() const
    +{
    +    if (preferredLogicalWidthsDirty())
    +        const_cast(this)->computePreferredLogicalWidths();
    +        
    +    return m_minPreferredLogicalWidth;
    +}
    +
    +int RenderBox::maxPreferredLogicalWidth() const
    +{
    +    if (preferredLogicalWidthsDirty())
    +        const_cast(this)->computePreferredLogicalWidths();
    +        
    +    return m_maxPreferredLogicalWidth;
    +}
    +
    +int RenderBox::overrideSize() const
    +{
    +    if (!hasOverrideSize())
    +        return -1;
    +    return gOverrideSizeMap->get(this);
    +}
    +
    +void RenderBox::setOverrideSize(int s)
    +{
    +    if (s == -1) {
    +        if (hasOverrideSize()) {
    +            setHasOverrideSize(false);
    +            gOverrideSizeMap->remove(this);
    +        }
    +    } else {
    +        if (!gOverrideSizeMap)
    +            gOverrideSizeMap = new OverrideSizeMap();
    +        setHasOverrideSize(true);
    +        gOverrideSizeMap->set(this, s);
    +    }
    +}
    +
    +int RenderBox::overrideWidth() const
    +{
    +    return hasOverrideSize() ? overrideSize() : width();
    +}
    +
    +int RenderBox::overrideHeight() const
    +{
    +    return hasOverrideSize() ? overrideSize() : height();
    +}
    +
    +int RenderBox::computeBorderBoxLogicalWidth(int width) const
    +{
    +    int bordersPlusPadding = borderAndPaddingLogicalWidth();
    +    if (style()->boxSizing() == CONTENT_BOX)
    +        return width + bordersPlusPadding;
    +    return max(width, bordersPlusPadding);
    +}
    +
    +int RenderBox::computeBorderBoxLogicalHeight(int height) const
    +{
    +    int bordersPlusPadding = borderAndPaddingLogicalHeight();
    +    if (style()->boxSizing() == CONTENT_BOX)
    +        return height + bordersPlusPadding;
    +    return max(height, bordersPlusPadding);
    +}
    +
    +int RenderBox::computeContentBoxLogicalWidth(int width) const
    +{
    +    if (style()->boxSizing() == BORDER_BOX)
    +        width -= borderAndPaddingLogicalWidth();
    +    return max(0, width);
    +}
    +
    +int RenderBox::computeContentBoxLogicalHeight(int height) const
    +{
    +    if (style()->boxSizing() == BORDER_BOX)
    +        height -= borderAndPaddingLogicalHeight();
    +    return max(0, height);
    +}
    +
    +// Hit Testing
    +bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
    +{
    +    tx += x();
    +    ty += y();
    +
    +    // Check kids first.
    +    for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
    +        if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
    +            updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
    +            return true;
    +        }
    +    }
    +
    +    // Check our bounds next. For this purpose always assume that we can only be hit in the
    +    // foreground phase (which is true for replaced elements like images).
    +    IntRect boundsRect = IntRect(tx, ty, width(), height());
    +    if (visibleToHitTesting() && action == HitTestForeground && boundsRect.intersects(result.rectForPoint(xPos, yPos))) {
    +        updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
    +        if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect))
    +            return true;
    +    }
    +
    +    return false;
    +}
    +
    +// --------------------- painting stuff -------------------------------
    +
    +void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty)
    +{
    +    tx += x();
    +    ty += y();
    +
    +    // default implementation. Just pass paint through to the children
    +    PaintInfo childInfo(paintInfo);
    +    childInfo.updatePaintingRootForChildren(this);
    +    for (RenderObject* child = firstChild(); child; child = child->nextSibling())
    +        child->paint(childInfo, tx, ty);
    +}
    +
    +void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
    +{
    +    const FillLayer* bgLayer = style()->backgroundLayers();
    +    Color bgColor = style()->visitedDependentColor(CSSPropertyBackgroundColor);
    +    RenderObject* bodyObject = 0;
    +    if (!hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) {
    +        // Locate the  element using the DOM.  This is easier than trying
    +        // to crawl around a render tree with potential :before/:after content and
    +        // anonymous blocks created by inline  tags etc.  We can locate the 
    +        // render object very easily via the DOM.
    +        HTMLElement* body = document()->body();
    +        bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0;
    +        if (bodyObject) {
    +            bgLayer = bodyObject->style()->backgroundLayers();
    +            bgColor = bodyObject->style()->visitedDependentColor(CSSPropertyBackgroundColor);
    +        }
    +    }
    +
    +    // The background of the box generated by the root element covers the entire canvas, so just use
    +    // the RenderView's docTop/Left/Width/Height accessors.
    +    paintFillLayers(paintInfo, bgColor, bgLayer, view()->docLeft(), view()->docTop(), view()->docWidth(), view()->docHeight(), CompositeSourceOver, bodyObject);
    +
    +    if (style()->hasBorder() && style()->display() != INLINE)
    +        paintBorder(paintInfo.context, tx, ty, width(), height(), style());
    +}
    +
    +void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
    +{
    +    if (!paintInfo.shouldPaintWithinRoot(this))
    +        return;
    +
    +    if (isRoot()) {
    +        paintRootBoxDecorations(paintInfo, tx, ty);
    +        return;
    +    }
    +
    +    return paintBoxDecorationsWithSize(paintInfo, tx, ty, width(), height());
    +}
    +
    +void RenderBox::paintBoxDecorationsWithSize(PaintInfo& paintInfo, int tx, int ty, int width, int height)
    +{
    +    // border-fit can adjust where we paint our border and background.  If set, we snugly fit our line box descendants.  (The iChat
    +    // balloon layout is an example of this).
    +    borderFitAdjust(tx, width);
    +
    +    // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
    +    // custom shadows of their own.
    +    paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Normal);
    +
    +    // If we have a native theme appearance, paint that before painting our background.
    +    // The theme will tell us whether or not we should also paint the CSS background.
    +    bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, width, height));
    +    if (!themePainted) {
    +        // The  only paints its background if the root element has defined a background
    +        // independent of the body.  Go through the DOM to get to the root element's render object,
    +        // since the root could be inline and wrapped in an anonymous block.
    +        if (!isBody() || document()->documentElement()->renderer()->hasBackground())
    +            paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, width, height);
    +        if (style()->hasAppearance())
    +            theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, width, height));
    +    }
    +    paintBoxShadow(paintInfo.context, tx, ty, width, height, style(), Inset);
    +
    +    // The theme will tell us whether or not we should also paint the CSS border.
    +    if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, width, height)))) && style()->hasBorder())
    +        paintBorder(paintInfo.context, tx, ty, width, height, style());
    +}
    +
    +void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty)
    +{
    +    if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
    +        return;
    +
    +    int w = width();
    +    int h = height();
    +
    +    // border-fit can adjust where we paint our border and background.  If set, we snugly fit our line box descendants.  (The iChat
    +    // balloon layout is an example of this).
    +    borderFitAdjust(tx, w);
    +
    +    paintMaskImages(paintInfo, tx, ty, w, h);
    +}
    +
    +void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h)
    +{
    +    // Figure out if we need to push a transparency layer to render our mask.
    +    bool pushTransparencyLayer = false;
    +    bool compositedMask = hasLayer() && layer()->hasCompositedMask();
    +    CompositeOperator compositeOp = CompositeSourceOver;
    +
    +    bool allMaskImagesLoaded = true;
    +    
    +    if (!compositedMask) {
    +        // If the context has a rotation, scale or skew, then use a transparency layer to avoid
    +        // pixel cruft around the edge of the mask.
    +        const AffineTransform& currentCTM = paintInfo.context->getCTM();
    +        pushTransparencyLayer = !currentCTM.isIdentityOrTranslationOrFlipped();
    +
    +        StyleImage* maskBoxImage = style()->maskBoxImage().image();
    +        const FillLayer* maskLayers = style()->maskLayers();
    +
    +        // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
    +        if (maskBoxImage)
    +            allMaskImagesLoaded &= maskBoxImage->isLoaded();
    +
    +        if (maskLayers)
    +            allMaskImagesLoaded &= maskLayers->imagesAreLoaded();
    +
    +        // Before all images have loaded, just use an empty transparency layer as the mask.
    +        if (!allMaskImagesLoaded)
    +            pushTransparencyLayer = true;
    +
    +        if (maskBoxImage && maskLayers->hasImage()) {
    +            // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask.
    +            pushTransparencyLayer = true;
    +        } else {
    +            // We have to use an extra image buffer to hold the mask. Multiple mask images need
    +            // to composite together using source-over so that they can then combine into a single unified mask that
    +            // can be composited with the content using destination-in.  SVG images need to be able to set compositing modes
    +            // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer
    +            // and composite that buffer as the mask.
    +            // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering
    +            // before pushing the transparency layer.
    +            for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) {
    +                if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) {
    +                    pushTransparencyLayer = true;
    +                    // We found one image that can be used in rendering, exit the loop
    +                    break;
    +                }
    +            }
    +        }
    +        
    +        compositeOp = CompositeDestinationIn;
    +        if (pushTransparencyLayer) {
    +            paintInfo.context->setCompositeOperation(CompositeDestinationIn);
    +            paintInfo.context->beginTransparencyLayer(1.0f);
    +            compositeOp = CompositeSourceOver;
    +        }
    +    }
    +
    +    if (allMaskImagesLoaded) {
    +        paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp);
    +        paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp);
    +    }
    +    
    +    if (pushTransparencyLayer)
    +        paintInfo.context->endTransparencyLayer();
    +}
    +
    +IntRect RenderBox::maskClipRect()
    +{
    +    IntRect bbox = borderBoxRect();
    +    if (style()->maskBoxImage().image())
    +        return bbox;
    +    
    +    IntRect result;
    +    for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
    +        if (maskLayer->image()) {
    +            IntRect maskRect;
    +            IntPoint phase;
    +            IntSize tileSize;
    +            calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize);
    +            result.unite(maskRect);
    +        }
    +    }
    +    return result;
    +}
    +
    +void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
    +{
    +    if (!fillLayer)
    +        return;
    +
    +    paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject);
    +    paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject);
    +}
    +
    +void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
    +{
    +    paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op, backgroundObject);
    +}
    +
    +#if USE(ACCELERATED_COMPOSITING)
    +static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers)
    +{
    +    for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
    +        if (curLayer->image() && image == curLayer->image()->data())
    +            return true;
    +    }
    +
    +    return false;
    +}
    +#endif
    +
    +void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
    +{
    +    if (!parent())
    +        return;
    +
    +    if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
    +        (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
    +        repaint();
    +        return;
    +    }
    +
    +    bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
    +    if (!didFullRepaint)
    +        repaintLayerRectsForImage(image, style()->maskLayers(), false);
    +
    +
    +#if USE(ACCELERATED_COMPOSITING)
    +    if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers()))
    +        layer()->contentChanged(RenderLayer::MaskImageChanged);
    +#endif
    +}
    +
    +bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
    +{
    +    IntRect rendererRect;
    +    RenderBox* layerRenderer = 0;
    +
    +    for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
    +        if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) {
    +            // Now that we know this image is being used, compute the renderer and the rect
    +            // if we haven't already
    +            if (!layerRenderer) {
    +                bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->hasBackground()));
    +                if (drawingRootBackground) {
    +                    layerRenderer = view();
    +
    +                    int rw;
    +                    int rh;
    +
    +                    if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
    +                        rw = frameView->contentsWidth();
    +                        rh = frameView->contentsHeight();
    +                    } else {
    +                        rw = layerRenderer->width();
    +                        rh = layerRenderer->height();
    +                    }
    +                    rendererRect = IntRect(-layerRenderer->marginLeft(),
    +                        -layerRenderer->marginTop(),
    +                        max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
    +                        max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
    +                } else {
    +                    layerRenderer = this;
    +                    rendererRect = borderBoxRect();
    +                }
    +            }
    +
    +            IntRect repaintRect;
    +            IntPoint phase;
    +            IntSize tileSize;
    +            layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize);
    +            layerRenderer->repaintRectangle(repaintRect);
    +            if (repaintRect == rendererRect)
    +                return true;
    +        }
    +    }
    +    return false;
    +}
    +
    +#if PLATFORM(MAC)
    +
    +void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText)
    +{
    +    Frame* frame = this->frame();
    +    if (!frame)
    +        return;
    +    Page* page = frame->page();
    +    if (!page)
    +        return;
    +
    +    InlineBox* boxWrap = inlineBoxWrapper();
    +    RootInlineBox* r = boxWrap ? boxWrap->root() : 0;
    +    if (r) {
    +        FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->logicalWidth(), r->selectionHeight());
    +        FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height());
    +        page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false);
    +    } else {
    +        FloatRect imageRect(tx + x(), ty + y(), width(), height());
    +        page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false);
    +    }
    +}
    +
    +#endif
    +
    +bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty)
    +{
    +    if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
    +        return false;
    +        
    +    bool isControlClip = hasControlClip();
    +    bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
    +    
    +    if (!isControlClip && !isOverflowClip)
    +        return false;
    +    
    +    if (paintInfo.phase == PaintPhaseOutline)
    +        paintInfo.phase = PaintPhaseChildOutlines;
    +    else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
    +        paintInfo.phase = PaintPhaseBlockBackground;
    +        paintObject(paintInfo, tx, ty);
    +        paintInfo.phase = PaintPhaseChildBlockBackgrounds;
    +    }
    +    IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty));
    +    paintInfo.context->save();
    +    if (style()->hasBorderRadius()) {
    +        IntSize topLeft, topRight, bottomLeft, bottomRight;
    +        IntRect borderRect = IntRect(tx, ty, width(), height());
    +        style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
    +
    +        paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
    +    }
    +    
    +    paintInfo.context->clip(clipRect);
    +    return true;
    +}
    +
    +void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty)
    +{
    +    ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
    +
    +    paintInfo.context->restore();
    +    if (originalPhase == PaintPhaseOutline) {
    +        paintInfo.phase = PaintPhaseSelfOutline;
    +        paintObject(paintInfo, tx, ty);
    +        paintInfo.phase = originalPhase;
    +    } else if (originalPhase == PaintPhaseChildBlockBackground)
    +        paintInfo.phase = originalPhase;
    +}
    +
    +IntRect RenderBox::overflowClipRect(int tx, int ty)
    +{
    +    // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
    +    // here.
    +
    +    int bLeft = borderLeft();
    +    int bTop = borderTop();
    +
    +    int clipX = tx + bLeft;
    +    int clipY = ty + bTop;
    +    int clipWidth = width() - bLeft - borderRight();
    +    int clipHeight = height() - bTop - borderBottom();
    +
    +    // Subtract out scrollbars if we have them.
    +    if (layer()) {
    +        clipWidth -= layer()->verticalScrollbarWidth();
    +        clipHeight -= layer()->horizontalScrollbarHeight();
    +    }
    +
    +    return IntRect(clipX, clipY, clipWidth, clipHeight);
    +}
    +
    +IntRect RenderBox::clipRect(int tx, int ty)
    +{
    +    int clipX = tx;
    +    int clipY = ty;
    +    int clipWidth = width();
    +    int clipHeight = height();
    +
    +    if (!style()->clipLeft().isAuto()) {
    +        int c = style()->clipLeft().calcValue(width());
    +        clipX += c;
    +        clipWidth -= c;
    +    }
    +
    +    if (!style()->clipRight().isAuto())
    +        clipWidth -= width() - style()->clipRight().calcValue(width());
    +
    +    if (!style()->clipTop().isAuto()) {
    +        int c = style()->clipTop().calcValue(height());
    +        clipY += c;
    +        clipHeight -= c;
    +    }
    +
    +    if (!style()->clipBottom().isAuto())
    +        clipHeight -= height() - style()->clipBottom().calcValue(height());
    +
    +    return IntRect(clipX, clipY, clipWidth, clipHeight);
    +}
    +
    +int RenderBox::containingBlockLogicalWidthForContent() const
    +{
    +    RenderBlock* cb = containingBlock();
    +    if (shrinkToAvoidFloats())
    +        return cb->availableLogicalWidthForLine(y(), false);
    +    return cb->availableLogicalWidth();
    +}
    +
    +int RenderBox::perpendicularContainingBlockLogicalHeight() const
    +{
    +    RenderBlock* cb = containingBlock();
    +    RenderStyle* containingBlockStyle = cb->style();
    +    Length logicalHeightLength = containingBlockStyle->logicalHeight();
    +    
    +    // FIXME: For now just support fixed heights.  Eventually should support percentage heights as well.
    +    if (!logicalHeightLength.isFixed()) {
    +        // Rather than making the child be completely unconstrained, WinIE uses the viewport width and height
    +        // as a constraint.  We do that for now as well even though it's likely being unconstrained is what the spec
    +        // will decide.
    +        return containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth();
    +    }
    +    
    +    // Use the content box logical height as specified by the style.
    +    return cb->computeContentBoxLogicalHeight(logicalHeightLength.value());
    +}
    +
    +void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
    +{
    +    if (repaintContainer == this)
    +        return;
    +
    +    if (RenderView* v = view()) {
    +        if (v->layoutStateEnabled() && !repaintContainer) {
    +            LayoutState* layoutState = v->layoutState();
    +            IntSize offset = layoutState->m_paintOffset;
    +            offset.expand(x(), y());
    +            if (style()->position() == RelativePosition && layer())
    +                offset += layer()->relativePositionOffset();
    +            transformState.move(offset);
    +            return;
    +        }
    +    }
    +
    +    bool containerSkipped;
    +    RenderObject* o = container(repaintContainer, &containerSkipped);
    +    if (!o)
    +        return;
    +
    +    bool isFixedPos = style()->position() == FixedPosition;
    +    bool hasTransform = hasLayer() && layer()->transform();
    +    if (hasTransform) {
    +        // If this box has a transform, it acts as a fixed position container for fixed descendants,
    +        // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
    +        fixed &= isFixedPos;
    +    } else
    +        fixed |= isFixedPos;
    +    
    +    IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint()));
    +    
    +    bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
    +    if (useTransforms && shouldUseTransformFromContainer(o)) {
    +        TransformationMatrix t;
    +        getTransformFromContainer(o, containerOffset, t);
    +        transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    +    } else
    +        transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    +
    +    if (containerSkipped) {
    +        // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
    +        // to just subtract the delta between the repaintContainer and o.
    +        IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
    +        transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    +        return;
    +    }
    +    
    +    o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
    +}
    +
    +void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
    +{
    +    // We don't expect absoluteToLocal() to be called during layout (yet)
    +    ASSERT(!view() || !view()->layoutStateEnabled());
    +    
    +    bool isFixedPos = style()->position() == FixedPosition;
    +    bool hasTransform = hasLayer() && layer()->transform();
    +    if (hasTransform) {
    +        // If this box has a transform, it acts as a fixed position container for fixed descendants,
    +        // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
    +        fixed &= isFixedPos;
    +    } else
    +        fixed |= isFixedPos;
    +    
    +    RenderObject* o = container();
    +    if (!o)
    +        return;
    +
    +    o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
    +
    +    IntSize containerOffset = offsetFromContainer(o, IntPoint());
    +
    +    bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
    +    if (useTransforms && shouldUseTransformFromContainer(o)) {
    +        TransformationMatrix t;
    +        getTransformFromContainer(o, containerOffset, t);
    +        transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    +    } else
    +        transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    +}
    +
    +IntSize RenderBox::offsetFromContainer(RenderObject* o, const IntPoint& point) const
    +{
    +    ASSERT(o == container());
    +
    +    IntSize offset;    
    +    if (isRelPositioned())
    +        offset += relativePositionOffset();
    +
    +    if (!isInline() || isReplaced()) {
    +        if (style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
    +            o->adjustForColumns(offset, IntPoint(point.x() + x(), point.y() + y()));
    +            offset += locationOffsetIncludingFlipping();
    +        } else
    +            offset += locationOffset();
    +    }
    +
    +    if (o->hasOverflowClip())
    +        offset -= toRenderBox(o)->layer()->scrolledContentOffset();
    +
    +    if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
    +        offset += toRenderInline(o)->relativePositionedInlineOffset(this);
    +
    +    return offset;
    +}
    +
    +InlineBox* RenderBox::createInlineBox()
    +{
    +    return new (renderArena()) InlineBox(this);
    +}
    +
    +void RenderBox::dirtyLineBoxes(bool fullLayout)
    +{
    +    if (m_inlineBoxWrapper) {
    +        if (fullLayout) {
    +            m_inlineBoxWrapper->destroy(renderArena());
    +            m_inlineBoxWrapper = 0;
    +        } else
    +            m_inlineBoxWrapper->dirtyLineBoxes();
    +    }
    +}
    +
    +void RenderBox::positionLineBox(InlineBox* box)
    +{
    +    if (isPositioned()) {
    +        // Cache the x position only if we were an INLINE type originally.
    +        bool wasInline = style()->isOriginalDisplayInlineType();
    +        if (wasInline && style()->hasStaticX()) {
    +            // The value is cached in the xPos of the box.  We only need this value if
    +            // our object was inline originally, since otherwise it would have ended up underneath
    +            // the inlines.
    +            layer()->setStaticX(box->x());
    +            setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
    +        } else if (!wasInline && style()->hasStaticY()) {
    +            // Our object was a block originally, so we make our normal flow position be
    +            // just below the line box (as though all the inlines that came before us got
    +            // wrapped in an anonymous block, which is what would have happened had we been
    +            // in flow).  This value was cached in the y() of the box.
    +            layer()->setStaticY(box->y());
    +            setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
    +        }
    +
    +        // Nuke the box.
    +        box->remove();
    +        box->destroy(renderArena());
    +    } else if (isReplaced()) {
    +        setLocation(box->x(), box->y());
    +        m_inlineBoxWrapper = box;
    +    }
    +}
    +
    +void RenderBox::deleteLineBoxWrapper()
    +{
    +    if (m_inlineBoxWrapper) {
    +        if (!documentBeingDestroyed())
    +            m_inlineBoxWrapper->remove();
    +        m_inlineBoxWrapper->destroy(renderArena());
    +        m_inlineBoxWrapper = 0;
    +    }
    +}
    +
    +IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
    +{
    +    if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
    +        return IntRect();
    +
    +    IntRect r = visualOverflowRect();
    +
    +    RenderView* v = view();
    +    if (v) {
    +        // FIXME: layoutDelta needs to be applied in parts before/after transforms and
    +        // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
    +        r.move(v->layoutDelta());
    +    }
    +    
    +    if (style()) {
    +        if (style()->hasAppearance())
    +            // The theme may wish to inflate the rect used when repainting.
    +            theme()->adjustRepaintRect(this, r);
    +
    +        // We have to use maximalOutlineSize() because a child might have an outline
    +        // that projects outside of our overflowRect.
    +        if (v) {
    +            ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
    +            r.inflate(v->maximalOutlineSize());
    +        }
    +    }
    +    
    +    computeRectForRepaint(repaintContainer, r);
    +    return r;
    +}
    +
    +void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
    +{
    +    // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
    +    // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
    +    // offset corner for the enclosing container).  This allows for a fully RL or BT document to repaint
    +    // properly even during layout, since the rect remains flipped all the way until the end.
    +    //
    +    // RenderView::computeRectForRepaint then converts the rect to physical coordinates.  We also convert to
    +    // physical when we hit a repaintContainer boundary.  Therefore the final rect returned is always in the
    +    // physical coordinate space of the repaintContainer.
    +    if (RenderView* v = view()) {
    +        // LayoutState is only valid for root-relative repainting
    +        if (v->layoutStateEnabled() && !repaintContainer) {
    +            LayoutState* layoutState = v->layoutState();
    +
    +            if (layer() && layer()->transform())
    +                rect = layer()->transform()->mapRect(rect);
    +
    +            if (style()->position() == RelativePosition && layer())
    +                rect.move(layer()->relativePositionOffset());
    +
    +            rect.move(x(), y());
    +            rect.move(layoutState->m_paintOffset);
    +            if (layoutState->m_clipped)
    +                rect.intersect(layoutState->m_clipRect);
    +            return;
    +        }
    +    }
    +
    +    if (hasReflection())
    +        rect.unite(reflectedRect(rect));
    +
    +    if (repaintContainer == this) {
    +        if (repaintContainer->style()->isFlippedBlocksWritingMode())
    +            flipForWritingMode(rect);
    +        return;
    +    }
    +
    +    bool containerSkipped;
    +    RenderObject* o = container(repaintContainer, &containerSkipped);
    +    if (!o)
    +        return;
    +
    +    if (isWritingModeRoot() && !isPositioned())
    +        flipForWritingMode(rect);
    +    IntPoint topLeft = rect.location();
    +    topLeft.move(x(), y());
    +
    +    EPosition position = style()->position();
    +
    +    // We are now in our parent container's coordinate space.  Apply our transform to obtain a bounding box
    +    // in the parent's coordinate space that encloses us.
    +    if (layer() && layer()->transform()) {
    +        fixed = position == FixedPosition;
    +        rect = layer()->transform()->mapRect(rect);
    +        topLeft = rect.location();
    +        topLeft.move(x(), y());
    +    } else if (position == FixedPosition)
    +        fixed = true;
    +
    +    if (position == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
    +        topLeft += toRenderInline(o)->relativePositionedInlineOffset(this);
    +    else if (position == RelativePosition && layer()) {
    +        // Apply the relative position offset when invalidating a rectangle.  The layer
    +        // is translated, but the render box isn't, so we need to do this to get the
    +        // right dirty rect.  Since this is called from RenderObject::setStyle, the relative position
    +        // flag on the RenderObject has been cleared, so use the one on the style().
    +        topLeft += layer()->relativePositionOffset();
    +    }
    +    
    +    if (o->isBlockFlow() && position != AbsolutePosition && position != FixedPosition) {
    +        RenderBlock* cb = toRenderBlock(o);
    +        if (cb->hasColumns()) {
    +            IntRect repaintRect(topLeft, rect.size());
    +            cb->adjustRectForColumns(repaintRect);
    +            topLeft = repaintRect.location();
    +            rect = repaintRect;
    +        }
    +    }
    +
    +    // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
    +    // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
    +    if (o->hasOverflowClip()) {
    +        RenderBox* containerBox = toRenderBox(o);
    +
    +        // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
    +        // layer's size instead.  Even if the layer's size is wrong, the layer itself will repaint
    +        // anyway if its size does change.
    +        topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
    +
    +        IntRect repaintRect(topLeft, rect.size());
    +        IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
    +        rect = intersection(repaintRect, boxRect);
    +        if (rect.isEmpty())
    +            return;
    +    } else
    +        rect.setLocation(topLeft);
    +
    +    if (containerSkipped) {
    +        // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
    +        IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
    +        rect.move(-containerOffset);
    +        return;
    +    }
    +
    +    o->computeRectForRepaint(repaintContainer, rect, fixed);
    +}
    +
    +void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect)
    +{
    +    int newX = x();
    +    int newY = y();
    +    int newWidth = width();
    +    int newHeight = height();
    +    if (rect.x() != newX || rect.y() != newY) {
    +        // The child moved.  Invalidate the object's old and new positions.  We have to do this
    +        // since the object may not have gotten a layout.
    +        m_frameRect = rect;
    +        repaint();
    +        repaintOverhangingFloats(true);
    +        m_frameRect = IntRect(newX, newY, newWidth, newHeight);
    +        repaint();
    +        repaintOverhangingFloats(true);
    +    }
    +}
    +
    +#ifdef ANDROID_LAYOUT
    +void RenderBox::setVisibleWidth(int newWidth) {
    +    const Settings* settings = document()->settings();
    +    ASSERT(settings);
    +    if (settings->layoutAlgorithm() != Settings::kLayoutFitColumnToScreen
    +        || m_visibleWidth == newWidth)
    +        return;
    +    m_isVisibleWidthChangedBeforeLayout = true;
    +    m_visibleWidth = newWidth;
    +}
    +
    +bool RenderBox::checkAndSetRelayoutChildren(bool* relayoutChildren) {
    +    if (m_isVisibleWidthChangedBeforeLayout) {
    +        m_isVisibleWidthChangedBeforeLayout = false;
    +        *relayoutChildren = true;
    +        return true;
    +    }
    +    return false;
    +}
    +#endif
    +
    +void RenderBox::computeLogicalWidth()
    +{
    +#ifdef ANDROID_LAYOUT
    +    if (view()->frameView())
    +        setVisibleWidth(view()->frameView()->textWrapWidth());
    +#endif
    +
    +    if (isPositioned()) {
    +        // FIXME: This calculation is not patched for block-flow yet.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46500
    +        computePositionedLogicalWidth();
    +        return;
    +    }
    +
    +    // If layout is limited to a subtree, the subtree root's logical width does not change.
    +    if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
    +        return;
    +
    +    // The parent box is flexing us, so it has increased or decreased our
    +    // width.  Use the width from the style context.
    +    // FIXME: Account for block-flow in flexible boxes.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46418
    +    if (hasOverrideSize() &&  parent()->style()->boxOrient() == HORIZONTAL
    +            && parent()->isFlexibleBox() && parent()->isFlexingChildren()) {
    +        setLogicalWidth(overrideSize());
    +        return;
    +    }
    +
    +    // FIXME: Account for block-flow in flexible boxes.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46418
    +    bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
    +    bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
    +    bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
    +
    +    Length logicalWidthLength = (treatAsReplaced) ? Length(computeReplacedLogicalWidth(), Fixed) : style()->logicalWidth();
    +
    +    RenderBlock* cb = containingBlock();
    +    int containerLogicalWidth = max(0, containingBlockLogicalWidthForContent());
    +    bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
    +    int containerWidthInInlineDirection = containerLogicalWidth;
    +    if (hasPerpendicularContainingBlock)
    +        containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
    +    
    +    if (isInline() && !isInlineBlockOrInlineTable()) {
    +        // just calculate margins
    +        setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth));
    +        setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth));
    +#ifdef ANDROID_LAYOUT
    +        if (treatAsReplaced) {
    +#else
    +        if (treatAsReplaced)
    +#endif
    +            setLogicalWidth(max(logicalWidthLength.value() + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth()));
    +
    +#ifdef ANDROID_LAYOUT
    +            // in SSR mode with replaced box, if the box width is wider than the container width,
    +            // it will be shrinked to fit to the container.
    +            if (containerLogicalWidth && (width() + m_marginLeft + m_marginRight) > containerLogicalWidth &&
    +                    document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
    +                m_marginLeft = m_marginRight = 0;
    +                setWidth(containerLogicalWidth);
    +                m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth;
    +            }
    +        }
    +#endif
    +        return;
    +    }
    +
    +    // Width calculations
    +    if (treatAsReplaced)
    +        setLogicalWidth(logicalWidthLength.value() + borderAndPaddingLogicalWidth());
    +    else {
    +        // Calculate LogicalWidth
    +        setLogicalWidth(computeLogicalWidthUsing(LogicalWidth, containerWidthInInlineDirection));
    +
    +        // Calculate MaxLogicalWidth
    +        if (!style()->logicalMaxWidth().isUndefined()) {
    +            int maxLogicalWidth = computeLogicalWidthUsing(MaxLogicalWidth, containerWidthInInlineDirection);
    +            if (logicalWidth() > maxLogicalWidth) {
    +                setLogicalWidth(maxLogicalWidth);
    +                logicalWidthLength = style()->logicalMaxWidth();
    +            }
    +        }
    +
    +        // Calculate MinLogicalWidth
    +        int minLogicalWidth = computeLogicalWidthUsing(MinLogicalWidth, containerWidthInInlineDirection);
    +        if (logicalWidth() < minLogicalWidth) {
    +            setLogicalWidth(minLogicalWidth);
    +            logicalWidthLength = style()->logicalMinWidth();
    +        }
    +    }
    +
    +    // Fieldsets are currently the only objects that stretch to their minimum width.
    +    if (stretchesToMinIntrinsicLogicalWidth()) {
    +        setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth()));
    +        logicalWidthLength = Length(logicalWidth(), Fixed);
    +    }
    +
    +    // Margin calculations.
    +    if (logicalWidthLength.isAuto() || hasPerpendicularContainingBlock || isFloating() || isInline()) {
    +        setMarginStart(style()->marginStart().calcMinValue(containerLogicalWidth));
    +        setMarginEnd(style()->marginEnd().calcMinValue(containerLogicalWidth));
    +    } else
    +        computeInlineDirectionMargins(cb, containerLogicalWidth, logicalWidth());
    +
    +#ifdef ANDROID_LAYOUT
    +    // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin.
    +    // If the box width is wider than the container width, it will be shrinked to fit to the container.
    +    if (containerLogicalWidth && !treatAsReplaced &&
    +            document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
    +        setWidth(width() + m_marginLeft + m_marginRight);
    +        m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft;
    +        m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight;
    +        if (width() > containerLogicalWidth) {
    +            m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = containerLogicalWidth-(m_marginLeft + m_marginRight);
    +            setWidth(m_minPreferredLogicalWidth);
    +        } else
    +            setWidth(width() -(m_marginLeft + m_marginRight));
    +    }
    +#endif
    +
    +    if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (logicalWidth() + marginStart() + marginEnd())
    +            && !isFloating() && !isInline() && !cb->isFlexibleBox())
    +        cb->setMarginEndForChild(this, containerLogicalWidth - logicalWidth() - cb->marginStartForChild(this));
    +}
    +
    +int RenderBox::computeLogicalWidthUsing(LogicalWidthType widthType, int availableLogicalWidth)
    +{
    +    int logicalWidthResult = logicalWidth();
    +    Length logicalWidth;
    +    if (widthType == LogicalWidth)
    +        logicalWidth = style()->logicalWidth();
    +    else if (widthType == MinLogicalWidth)
    +        logicalWidth = style()->logicalMinWidth();
    +    else
    +        logicalWidth = style()->logicalMaxWidth();
    +
    +    if (logicalWidth.isIntrinsicOrAuto()) {
    +        int marginStart = style()->marginStart().calcMinValue(availableLogicalWidth);
    +        int marginEnd = style()->marginEnd().calcMinValue(availableLogicalWidth);
    +        if (availableLogicalWidth)
    +            logicalWidthResult = availableLogicalWidth - marginStart - marginEnd;
    +
    +        if (sizesToIntrinsicLogicalWidth(widthType)) {
    +            logicalWidthResult = max(logicalWidthResult, minPreferredLogicalWidth());
    +            logicalWidthResult = min(logicalWidthResult, maxPreferredLogicalWidth());
    +        }
    +    } else // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
    +        logicalWidthResult = computeBorderBoxLogicalWidth(logicalWidth.calcValue(availableLogicalWidth)); 
    +
    +    return logicalWidthResult;
    +}
    +
    +bool RenderBox::sizesToIntrinsicLogicalWidth(LogicalWidthType widthType) const
    +{
    +    // Marquees in WinIE are like a mixture of blocks and inline-blocks.  They size as though they're blocks,
    +    // but they allow text to sit on the same line as the marquee.
    +    if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee()))
    +        return true;
    +
    +    // This code may look a bit strange.  Basically width:intrinsic should clamp the size when testing both
    +    // min-width and width.  max-width is only clamped if it is also intrinsic.
    +    Length logicalWidth = (widthType == MaxLogicalWidth) ? style()->logicalMaxWidth() : style()->logicalWidth();
    +    if (logicalWidth.type() == Intrinsic)
    +        return true;
    +
    +    // Children of a horizontal marquee do not fill the container by default.
    +    // FIXME: Need to deal with MAUTO value properly.  It could be vertical.
    +    // FIXME: Think about block-flow here.  Need to find out how marquee direction relates to
    +    // block-flow (as well as how marquee overflow should relate to block flow).
    +    // https://bugs.webkit.org/show_bug.cgi?id=46472
    +    if (parent()->style()->overflowX() == OMARQUEE) {
    +        EMarqueeDirection dir = parent()->style()->marqueeDirection();
    +        if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
    +            return true;
    +    }
    +
    +    // Flexible horizontal boxes lay out children at their intrinsic widths.  Also vertical boxes
    +    // that don't stretch their kids lay out their children at their intrinsic widths.
    +    // FIXME: Think about block-flow here.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46473
    +    if (parent()->isFlexibleBox()
    +            && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
    +        return true;
    +
    +    // Button, input, select, textarea, legend and datagrid treat
    +    // width value of 'auto' as 'intrinsic' unless it's in a
    +    // stretching vertical flexbox.
    +    // FIXME: Think about block-flow here.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46473
    +    if (logicalWidth.type() == Auto && !(parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL && parent()->style()->boxAlign() == BSTRETCH) && node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag) || node()->hasTagName(textareaTag) || node()->hasTagName(legendTag) || node()->hasTagName(datagridTag)))
    +        return true;
    +
    +    return false;
    +}
    +
    +void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth)
    +{
    +    const RenderStyle* containingBlockStyle = containingBlock->style();
    +    Length marginStartLength = style()->marginStartUsing(containingBlockStyle);
    +    Length marginEndLength = style()->marginEndUsing(containingBlockStyle);
    +    
    +    // Case One: The object is being centered in the containing block's available logical width.
    +    if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth)
    +        || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) {
    +        containingBlock->setMarginStartForChild(this, max(0, (containerWidth - childWidth) / 2));
    +        containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this));
    +        return;
    +    } 
    +    
    +    // Case Two: The object is being pushed to the start of the containing block's available logical width.
    +    if (marginEndLength.isAuto() && childWidth < containerWidth) {
    +        containingBlock->setMarginStartForChild(this, marginStartLength.calcValue(containerWidth));
    +        containingBlock->setMarginEndForChild(this, containerWidth - childWidth - containingBlock->marginStartForChild(this));
    +        return;
    +    } 
    +    
    +    // Case Three: The object is being pushed to the end of the containing block's available logical width.
    +    bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT)
    +        || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT));
    +    if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) {
    +        containingBlock->setMarginEndForChild(this, marginEndLength.calcValue(containerWidth));
    +        containingBlock->setMarginStartForChild(this, containerWidth - childWidth - containingBlock->marginEndForChild(this));
    +        return;
    +    } 
    +    
    +    // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3).  In that case
    +    // auto margins will just turn into 0.
    +    containingBlock->setMarginStartForChild(this, marginStartLength.calcMinValue(containerWidth));
    +    containingBlock->setMarginEndForChild(this, marginEndLength.calcMinValue(containerWidth));
    +}
    +
    +void RenderBox::computeLogicalHeight()
    +{
    +    // Cell height is managed by the table and inline non-replaced elements do not support a height property.
    +    if (isTableCell() || (isInline() && !isReplaced()))
    +        return;
    +
    +    Length h;
    +    if (isPositioned()) {
    +        // FIXME: This calculation is not patched for block-flow yet.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46500
    +        computePositionedLogicalHeight();
    +    } else {
    +        RenderBlock* cb = containingBlock();
    +        bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
    +    
    +        if (!hasPerpendicularContainingBlock)
    +            computeBlockDirectionMargins(cb);
    +
    +        // For tables, calculate margins only.
    +        if (isTable()) {
    +            if (hasPerpendicularContainingBlock)
    +                computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), logicalHeight());
    +            return;
    +        }
    +
    +        // FIXME: Account for block-flow in flexible boxes.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46418
    +        bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
    +        bool stretching = parent()->style()->boxAlign() == BSTRETCH;
    +        bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
    +        bool checkMinMaxHeight = false;
    +
    +        // The parent box is flexing us, so it has increased or decreased our height.  We have to
    +        // grab our cached flexible height.
    +        // FIXME: Account for block-flow in flexible boxes.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46418
    +        if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
    +                && parent()->isFlexingChildren())
    +            h = Length(overrideSize() - borderAndPaddingLogicalHeight(), Fixed);
    +        else if (treatAsReplaced)
    +            h = Length(computeReplacedLogicalHeight(), Fixed);
    +        else {
    +            h = style()->logicalHeight();
    +            checkMinMaxHeight = true;
    +        }
    +
    +        // Block children of horizontal flexible boxes fill the height of the box.
    +        // FIXME: Account for block-flow in flexible boxes.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46418
    +        if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
    +                && parent()->isStretchingChildren()) {
    +            h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed);
    +            checkMinMaxHeight = false;
    +        }
    +
    +        int heightResult;
    +        if (checkMinMaxHeight) {
    +#ifdef ANDROID_LAYOUT
    +            // in SSR mode, ignore CSS height as layout is so different
    +            if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
    +                heightResult = -1;
    +            else
    +#endif
    +            heightResult = computeLogicalHeightUsing(style()->logicalHeight());
    +            if (heightResult == -1)
    +                heightResult = logicalHeight();
    +            int minH = computeLogicalHeightUsing(style()->logicalMinHeight()); // Leave as -1 if unset.
    +            int maxH = style()->logicalMaxHeight().isUndefined() ? heightResult : computeLogicalHeightUsing(style()->logicalMaxHeight());
    +            if (maxH == -1)
    +                maxH = heightResult;
    +            heightResult = min(maxH, heightResult);
    +            heightResult = max(minH, heightResult);
    +        } else {
    +            // The only times we don't check min/max height are when a fixed length has
    +            // been given as an override.  Just use that.  The value has already been adjusted
    +            // for box-sizing.
    +            heightResult = h.value() + borderAndPaddingLogicalHeight();
    +        }
    +
    +        setLogicalHeight(heightResult);
    +        
    +        if (hasPerpendicularContainingBlock)
    +            computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult);
    +    }
    +
    +    // WinIE quirk: The  block always fills the entire canvas in quirks mode.  The  always fills the
    +    //  block in quirks mode.  Only apply this quirk if the block is normal flow and no height
    +    // is specified. When we're printing, we also need this quirk if the body or root has a percentage 
    +    // height since we don't set a height in RenderView when we're printing. So without this quirk, the 
    +    // height has nothing to be a percentage of, and it ends up being 0. That is bad.
    +    bool paginatedContentNeedsBaseHeight = document()->printing() && h.isPercent()
    +        && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->logicalHeight().isPercent()));
    +    if (stretchesToViewport() || paginatedContentNeedsBaseHeight) {
    +        // FIXME: Finish accounting for block flow here.
    +        // https://bugs.webkit.org/show_bug.cgi?id=46603
    +        int margins = collapsedMarginBefore() + collapsedMarginAfter();
    +        int visHeight;
    +        if (document()->printing())
    +            visHeight = static_cast(view()->pageLogicalHeight());
    +        else  {
    +            if (style()->isHorizontalWritingMode())
    +                visHeight = view()->viewHeight();
    +            else
    +                visHeight = view()->viewWidth();
    +        }
    +        if (isRoot())
    +            setLogicalHeight(max(logicalHeight(), visHeight - margins));
    +        else {
    +            int marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
    +            setLogicalHeight(max(logicalHeight(), visHeight - marginsBordersPadding));
    +        }
    +    }
    +}
    +
    +int RenderBox::computeLogicalHeightUsing(const Length& h)
    +{
    +    int logicalHeight = -1;
    +    if (!h.isAuto()) {
    +        if (h.isFixed())
    +            logicalHeight = h.value();
    +        else if (h.isPercent())
    +            logicalHeight = computePercentageLogicalHeight(h);
    +        if (logicalHeight != -1) {
    +            logicalHeight = computeBorderBoxLogicalHeight(logicalHeight);
    +            return logicalHeight;
    +        }
    +    }
    +    return logicalHeight;
    +}
    +
    +int RenderBox::computePercentageLogicalHeight(const Length& height)
    +{
    +    int result = -1;
    +    bool skippedAutoHeightContainingBlock = false;
    +    RenderBlock* cb = containingBlock();
    +    if (document()->inQuirksMode()) {
    +        // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
    +        // block that may have a specified height and then use it.  In strict mode, this violates the
    +        // specification, which states that percentage heights just revert to auto if the containing
    +        // block has an auto height.
    +        while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->logicalHeight().isAuto()) {
    +            skippedAutoHeightContainingBlock = true;
    +            cb = cb->containingBlock();
    +            cb->addPercentHeightDescendant(this);
    +        }
    +    }
    +
    +    // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
    +    // explicitly specified that can be used for any percentage computations.
    +    // FIXME: We can't just check top/bottom here.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46500
    +    bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
    +
    +    bool includeBorderPadding = isTable();
    +
    +    // Table cells violate what the CSS spec says to do with heights.  Basically we
    +    // don't care if the cell specified a height or not.  We just always make ourselves
    +    // be a percentage of the cell's current content height.
    +    if (cb->isTableCell()) {
    +        if (!skippedAutoHeightContainingBlock) {
    +            result = cb->overrideSize();
    +            if (result == -1) {
    +                // Normally we would let the cell size intrinsically, but scrolling overflow has to be
    +                // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
    +                // While we can't get all cases right, we can at least detect when the cell has a specified
    +                // height or when the table has a specified height.  In these cases we want to initially have
    +                // no size and allow the flexing of the table or the cell to its specified height to cause us
    +                // to grow to fill the space.  This could end up being wrong in some cases, but it is
    +                // preferable to the alternative (sizing intrinsically and making the row end up too big).
    +                RenderTableCell* cell = toRenderTableCell(cb);
    +                if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto()))
    +                    return 0;
    +                return -1;
    +            }
    +            includeBorderPadding = true;
    +        }
    +    }
    +    // Otherwise we only use our percentage height if our containing block had a specified
    +    // height.
    +    else if (cb->style()->logicalHeight().isFixed())
    +        result = cb->computeContentBoxLogicalHeight(cb->style()->logicalHeight().value());
    +    else if (cb->style()->logicalHeight().isPercent() && !isPositionedWithSpecifiedHeight) {
    +        // We need to recur and compute the percentage height for our containing block.
    +        result = cb->computePercentageLogicalHeight(cb->style()->logicalHeight());
    +        if (result != -1)
    +            result = cb->computeContentBoxLogicalHeight(result);
    +    } else if (cb->isRenderView() || (cb->isBody() && document()->inQuirksMode()) || isPositionedWithSpecifiedHeight) {
    +        // Don't allow this to affect the block' height() member variable, since this
    +        // can get called while the block is still laying out its kids.
    +        int oldHeight = cb->logicalHeight();
    +        cb->computeLogicalHeight();
    +        result = cb->contentLogicalHeight();
    +        cb->setLogicalHeight(oldHeight);
    +    } else if (cb->isRoot() && isPositioned())
    +        // Match the positioned objects behavior, which is that positioned objects will fill their viewport
    +        // always.  Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block.
    +        result = cb->computeContentBoxLogicalHeight(cb->availableLogicalHeight());
    +
    +    if (result != -1) {
    +        result = height.calcValue(result);
    +        if (includeBorderPadding) {
    +            // It is necessary to use the border-box to match WinIE's broken
    +            // box model.  This is essential for sizing inside
    +            // table cells using percentage heights.
    +            result -= borderAndPaddingLogicalHeight();
    +            result = max(0, result);
    +        }
    +    }
    +    return result;
    +}
    +
    +int RenderBox::computeReplacedLogicalWidth(bool includeMaxWidth) const
    +{
    +    int logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
    +    int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
    +    int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
    +
    +    return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
    +}
    +
    +int RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const
    +{
    +    switch (logicalWidth.type()) {
    +        case Fixed:
    +            return computeContentBoxLogicalWidth(logicalWidth.value());
    +        case Percent: {
    +            // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the
    +            // containing block's block-flow.
    +            // https://bugs.webkit.org/show_bug.cgi?id=46496
    +            const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent();
    +            if (cw > 0)
    +                return computeContentBoxLogicalWidth(logicalWidth.calcMinValue(cw));
    +        }
    +        // fall through
    +        default:
    +            return intrinsicLogicalWidth();
    +     }
    +}
    +
    +int RenderBox::computeReplacedLogicalHeight() const
    +{
    +    int logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
    +    int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
    +    int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
    +
    +    return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
    +}
    +
    +int RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const
    +{
    +    switch (logicalHeight.type()) {
    +        case Fixed:
    +            return computeContentBoxLogicalHeight(logicalHeight.value());
    +        case Percent:
    +        {
    +            RenderObject* cb = isPositioned() ? container() : containingBlock();
    +            while (cb->isAnonymous()) {
    +                cb = cb->containingBlock();
    +                toRenderBlock(cb)->addPercentHeightDescendant(const_cast(this));
    +            }
    +
    +            // FIXME: This calculation is not patched for block-flow yet.
    +            // https://bugs.webkit.org/show_bug.cgi?id=46500
    +            if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
    +                ASSERT(cb->isRenderBlock());
    +                RenderBlock* block = toRenderBlock(cb);
    +                int oldHeight = block->height();
    +                block->computeLogicalHeight();
    +                int newHeight = block->computeContentBoxLogicalHeight(block->contentHeight());
    +                block->setHeight(oldHeight);
    +                return computeContentBoxLogicalHeight(logicalHeight.calcValue(newHeight));
    +            }
    +            
    +            // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the
    +            // containing block's block-flow.
    +            // https://bugs.webkit.org/show_bug.cgi?id=46496
    +            int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableLogicalHeight();
    +
    +            // It is necessary to use the border-box to match WinIE's broken
    +            // box model.  This is essential for sizing inside
    +            // table cells using percentage heights.
    +            // FIXME: This needs to be made block-flow-aware.  If the cell and image are perpendicular block-flows, this isn't right.
    +            // https://bugs.webkit.org/show_bug.cgi?id=46997
    +            if (cb->isTableCell() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) {
    +                // Don't let table cells squeeze percent-height replaced elements
    +                // 
    +                availableHeight = max(availableHeight, intrinsicLogicalHeight());
    +                return logicalHeight.calcValue(availableHeight - borderAndPaddingLogicalHeight());
    +            }
    +
    +            return computeContentBoxLogicalHeight(logicalHeight.calcValue(availableHeight));
    +        }
    +        default:
    +            return intrinsicLogicalHeight();
    +    }
    +}
    +
    +int RenderBox::availableLogicalHeight() const
    +{
    +    return availableLogicalHeightUsing(style()->logicalHeight());
    +}
    +
    +int RenderBox::availableLogicalHeightUsing(const Length& h) const
    +{
    +    if (h.isFixed())
    +        return computeContentBoxLogicalHeight(h.value());
    +
    +    if (isRenderView())
    +        return style()->isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth();
    +
    +    // We need to stop here, since we don't want to increase the height of the table
    +    // artificially.  We're going to rely on this cell getting expanded to some new
    +    // height, and then when we lay out again we'll use the calculation below.
    +    if (isTableCell() && (h.isAuto() || h.isPercent()))
    +        return overrideSize() - borderAndPaddingLogicalWidth();
    +
    +    if (h.isPercent())
    +       return computeContentBoxLogicalHeight(h.calcValue(containingBlock()->availableLogicalHeight()));
    +
    +    // FIXME: We can't just check top/bottom here.
    +    // https://bugs.webkit.org/show_bug.cgi?id=46500
    +    if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
    +        RenderBlock* block = const_cast(toRenderBlock(this));
    +        int oldHeight = block->logicalHeight();
    +        block->computeLogicalHeight();
    +        int newHeight = block->computeContentBoxLogicalHeight(block->contentLogicalHeight());
    +        block->setLogicalHeight(oldHeight);
    +        return computeContentBoxLogicalHeight(newHeight);
    +    }
    +
    +    return containingBlock()->availableLogicalHeight();
    +}
    +
    +void RenderBox::computeBlockDirectionMargins(RenderBlock* containingBlock)
    +{
    +    if (isTableCell()) {
    +        // FIXME: Not right if we allow cells to have different directionality than the table.  If we do allow this, though,
    +        // we may just do it with an extra anonymous block inside the cell.
    +        setMarginBefore(0);
    +        setMarginAfter(0);
    +        return;
    +    }
    +
    +    // Margins are calculated with respect to the logical width of
    +    // the containing block (8.3)
    +    int cw = containingBlockLogicalWidthForContent();
    +
    +    RenderStyle* containingBlockStyle = containingBlock->style();
    +    containingBlock->setMarginBeforeForChild(this, style()->marginBeforeUsing(containingBlockStyle).calcMinValue(cw));
    +    containingBlock->setMarginAfterForChild(this, style()->marginAfterUsing(containingBlockStyle).calcMinValue(cw));
    +}
    +
    +int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const
    +{
    +#if PLATFORM(ANDROID)
    +    // Fixed element's position should be decided by the visible screen size.
    +    // That is in the doc coordindate.
    +    if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
    +        const RenderView* view = toRenderView(containingBlock);
    +        return PlatformBridge::screenWidthInDocCoord(view->frameView());
    +    }
    +#endif
    +    if (containingBlock->isBox()) {
    +        const RenderBox* containingBlockBox = toRenderBox(containingBlock);
    +        return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth();
    +    }
    +    
    +    ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned());
    +
    +    const RenderInline* flow = toRenderInline(containingBlock);
    +    InlineFlowBox* first = flow->firstLineBox();
    +    InlineFlowBox* last = flow->lastLineBox();
    +
    +    // If the containing block is empty, return a width of 0.
    +    if (!first || !last)
    +        return 0;
    +
    +    int fromLeft;
    +    int fromRight;
    +    if (containingBlock->style()->isLeftToRightDirection()) {
    +        fromLeft = first->logicalLeft() + first->borderLogicalLeft();
    +        fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
    +    } else {
    +        fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
    +        fromLeft = last->logicalLeft() + last->borderLogicalLeft();
    +    }
    +
    +    return max(0, (fromRight - fromLeft));
    +}
    +
    +int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const
    +{
    +#if PLATFORM(ANDROID)
    +    // Fixed element's position should be decided by the visible screen size.
    +    // That is in the doc coordindate.
    +    if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
    +        const RenderView* view = toRenderView(containingBlock);
    +        return PlatformBridge::screenHeightInDocCoord(view->frameView());
    +    }
    +#endif
    +    int heightResult = 0;
    +    if (containingBlock->isBox())
    +         heightResult = toRenderBox(containingBlock)->height();
    +    else if (containingBlock->isRenderInline()) {
    +        ASSERT(containingBlock->isRelPositioned());
    +        heightResult = toRenderInline(containingBlock)->linesBoundingBox().height();
    +    }
    +    return heightResult - containingBlock->borderTop() - containingBlock->borderBottom();
    +}
    +
    +void RenderBox::computePositionedLogicalWidth()
    +{
    +    if (isReplaced()) {
    +        computePositionedLogicalWidthReplaced();
    +        return;
    +    }
    +
    +    // QUESTIONS
    +    // FIXME 1: Which RenderObject's 'direction' property should used: the
    +    // containing block (cb) as the spec seems to imply, the parent (parent()) as
    +    // was previously done in calculating the static distances, or ourself, which
    +    // was also previously done for deciding what to override when you had
    +    // over-constrained margins?  Also note that the container block is used
    +    // in similar situations in other parts of the RenderBox class (see computeLogicalWidth()
    +    // and computeMarginsInContainingBlockInlineDirection()). For now we are using the parent for quirks
    +    // mode and the containing block for strict mode.
    +
    +    // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having
    +    // the type 'static' in determining whether to calculate the static distance?
    +    // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
    +
    +    // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater
    +    // than or less than the computed width().  Be careful of box-sizing and
    +    // percentage issues.
    +
    +    // The following is based off of the W3C Working Draft from April 11, 2006 of
    +    // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
    +    // 
    +    // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
    +    // correspond to text from the spec)
    +
    +
    +    // We don't use containingBlock(), since we may be positioned by an enclosing
    +    // relative positioned inline.
    +    const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
    +    
    +    const int containerWidth = containingBlockWidthForPositioned(containerBlock);
    +
    +    // To match WinIE, in quirks mode use the parent's 'direction' property
    +    // instead of the the container block's.
    +    TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction();
    +
    +    const int bordersPlusPadding = borderAndPaddingWidth();
    +    const Length marginLeft = style()->marginLeft();
    +    const Length marginRight = style()->marginRight();
    +    Length left = style()->left();
    +    Length right = style()->right();
    +
    +    /*---------------------------------------------------------------------------*\
    +     * For the purposes of this section and the next, the term "static position"
    +     * (of an element) refers, roughly, to the position an element would have had
    +     * in the normal flow. More precisely:
    +     *
    +     * * The static position for 'left' is the distance from the left edge of the
    +     *   containing block to the left margin edge of a hypothetical box that would
    +     *   have been the first box of the element if its 'position' property had
    +     *   been 'static' and 'float' had been 'none'. The value is negative if the
    +     *   hypothetical box is to the left of the containing block.
    +     * * The static position for 'right' is the distance from the right edge of the
    +     *   containing block to the right margin edge of the same hypothetical box as
    +     *   above. The value is positive if the hypothetical box is to the left of the
    +     *   containing block's edge.
    +     *
    +     * But rather than actually calculating the dimensions of that hypothetical box,
    +     * user agents are free to make a guess at its probable position.
    +     *
    +     * For the purposes of calculating the static position, the containing block of
    +     * fixed positioned elements is the initial containing block instead of the
    +     * viewport, and all scrollable boxes should be assumed to be scrolled to their
    +     * origin.
    +    \*---------------------------------------------------------------------------*/
    +
    +    // see FIXME 2
    +    // Calculate the static distance if needed.
    +    if (left.isAuto() && right.isAuto()) {
    +        if (containerDirection == LTR) {
    +            // 'staticX' should already have been set through layout of the parent.
    +            int staticPosition = layer()->staticX() - containerBlock->borderLeft();
    +            for (RenderObject* curr = parent(); curr && curr != containerBlock; curr = curr->parent()) {
    +                if (curr->isBox())
    +                    staticPosition += toRenderBox(curr)->x();
    +            }
    +            left.setValue(Fixed, staticPosition);
    +        } else {
    +            RenderBox* enclosingBox = parent()->enclosingBox();
    +            // 'staticX' should already have been set through layout of the parent.
    +            int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
    +            staticPosition -= enclosingBox->width();
    +            for (RenderObject* curr = enclosingBox; curr && curr != containerBlock; curr = curr->parent()) {
    +                if (curr->isBox())
    +                    staticPosition -= toRenderBox(curr)->x();
    +            }
    +            right.setValue(Fixed, staticPosition);
    +        }
    +    }
    +
    +    // Calculate constraint equation values for 'width' case.
    +    int widthResult;
    +    int xResult;
    +    computePositionedLogicalWidthUsing(style()->width(), containerBlock, containerDirection,
    +                                 containerWidth, bordersPlusPadding,
    +                                 left, right, marginLeft, marginRight,
    +                                 widthResult, m_marginLeft, m_marginRight, xResult);
    +    setWidth(widthResult);
    +    setX(xResult);
    +
    +    // Calculate constraint equation values for 'max-width' case.
    +    if (!style()->maxWidth().isUndefined()) {
    +        int maxWidth;
    +        int maxMarginLeft;
    +        int maxMarginRight;
    +        int maxXPos;
    +
    +        computePositionedLogicalWidthUsing(style()->maxWidth(), containerBlock, containerDirection,
    +                                     containerWidth, bordersPlusPadding,
    +                                     left, right, marginLeft, marginRight,
    +                                     maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
    +
    +        if (width() > maxWidth) {
    +            setWidth(maxWidth);
    +            m_marginLeft = maxMarginLeft;
    +            m_marginRight = maxMarginRight;
    +            m_frameRect.setX(maxXPos);
    +        }
    +    }
    +
    +    // Calculate constraint equation values for 'min-width' case.
    +    if (!style()->minWidth().isZero()) {
    +        int minWidth;
    +        int minMarginLeft;
    +        int minMarginRight;
    +        int minXPos;
    +
    +        computePositionedLogicalWidthUsing(style()->minWidth(), containerBlock, containerDirection,
    +                                     containerWidth, bordersPlusPadding,
    +                                     left, right, marginLeft, marginRight,
    +                                     minWidth, minMarginLeft, minMarginRight, minXPos);
    +
    +        if (width() < minWidth) {
    +            setWidth(minWidth);
    +            m_marginLeft = minMarginLeft;
    +            m_marginRight = minMarginRight;
    +            m_frameRect.setX(minXPos);
    +        }
    +    }
    +
    +    if (stretchesToMinIntrinsicLogicalWidth() && width() < minPreferredLogicalWidth() - bordersPlusPadding) {
    +        computePositionedLogicalWidthUsing(Length(minPreferredLogicalWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection,
    +                                     containerWidth, bordersPlusPadding,
    +                                     left, right, marginLeft, marginRight,
    +                                     widthResult, m_marginLeft, m_marginRight, xResult);
    +        setWidth(widthResult);
    +        setX(xResult);
    +    }
    +
    +    // Put width() into correct form.
    +    setWidth(width() + bordersPlusPadding);
    +}
    +
    +void RenderBox::computePositionedLogicalWidthUsing(Length width, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
    +                                             const int containerWidth, const int bordersPlusPadding,
    +                                             const Length left, const Length right, const Length marginLeft, const Length marginRight,
    +                                             int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos)
    +{
    +    // 'left' and 'right' cannot both be 'auto' because one would of been
    +    // converted to the static position already
    +    ASSERT(!(left.isAuto() && right.isAuto()));
    +
    +    int leftValue = 0;
    +
    +    bool widthIsAuto = width.isIntrinsicOrAuto();
    +    bool leftIsAuto = left.isAuto();
    +    bool rightIsAuto = right.isAuto();
    +
    +    if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
    +        /*-----------------------------------------------------------------------*\
    +         * If none of the three is 'auto': If both 'margin-left' and 'margin-
    +         * right' are 'auto', solve the equation under the extra constraint that
    +         * the two margins get equal values, unless this would make them negative,
    +         * in which case when direction of the containing block is 'ltr' ('rtl'),
    +         * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
    +         * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
    +         * solve the equation for that value. If the values are over-constrained,
    +         * ignore the value for 'left' (in case the 'direction' property of the
    +         * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
    +         * and solve for that value.
    +        \*-----------------------------------------------------------------------*/
    +        // NOTE:  It is not necessary to solve for 'right' in the over constrained
    +        // case because the value is not used for any further calculations.
    +
    +        leftValue = left.calcValue(containerWidth);
    +        widthValue = computeContentBoxLogicalWidth(width.calcValue(containerWidth));
    +
    +        const int availableSpace = containerWidth - (leftValue + widthValue + right.calcValue(containerWidth) + bordersPlusPadding);
    +
    +        // Margins are now the only unknown
    +        if (marginLeft.isAuto() && marginRight.isAuto()) {
    +            // Both margins auto, solve for equality
    +            if (availableSpace >= 0) {
    +                marginLeftValue = availableSpace / 2; // split the difference
    +                marginRightValue = availableSpace - marginLeftValue;  // account for odd valued differences
    +            } else {
    +                // see FIXME 1
    +                if (containerDirection == LTR) {
    +                    marginLeftValue = 0;
    +                    marginRightValue = availableSpace; // will be negative
    +                } else {
    +                    marginLeftValue = availableSpace; // will be negative
    +                    marginRightValue = 0;
    +                }
    +            }
    +        } else if (marginLeft.isAuto()) {
    +            // Solve for left margin
    +            marginRightValue = marginRight.calcValue(containerWidth);
    +            marginLeftValue = availableSpace - marginRightValue;
    +        } else if (marginRight.isAuto()) {
    +            // Solve for right margin
    +            marginLeftValue = marginLeft.calcValue(containerWidth);
    +            marginRightValue = availableSpace - marginLeftValue;
    +        } else {
    +            // Over-constrained, solve for left if direction is RTL
    +            marginLeftValue = marginLeft.calcValue(containerWidth);
    +            marginRightValue = marginRight.calcValue(containerWidth);
    +
    +            // see FIXME 1 -- used to be "this->style()->direction()"
    +            if (containerDirection == RTL)
    +                leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
    +        }
    +    } else {
    +        /*--------------------------------------------------------------------*\
    +         * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
    +         * to 0, and pick the one of the following six rules that applies.
    +         *
    +         * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
    +         *    width is shrink-to-fit. Then solve for 'left'
    +         *
    +         *              OMIT RULE 2 AS IT SHOULD NEVER BE HIT
    +         * ------------------------------------------------------------------
    +         * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
    +         *    the 'direction' property of the containing block is 'ltr' set
    +         *    'left' to the static position, otherwise set 'right' to the
    +         *    static position. Then solve for 'left' (if 'direction is 'rtl')
    +         *    or 'right' (if 'direction' is 'ltr').
    +         * ------------------------------------------------------------------
    +         *
    +         * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
    +         *    width is shrink-to-fit . Then solve for 'right'
    +         * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
    +         *    for 'left'
    +         * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
    +         *    for 'width'
    +         * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
    +         *    for 'right'
    +         *
    +         * Calculation of the shrink-to-fit width is similar to calculating the
    +         * width of a table cell using the automatic table layout algorithm.
    +         * Roughly: calculate the preferred width by formatting the content
    +         * without breaking lines other than where explicit line breaks occur,
    +         * and also calculate the preferred minimum width, e.g., by trying all
    +         * possible line breaks. CSS 2.1 does not define the exact algorithm.
    +         * Thirdly, calculate the available width: this is found by solving
    +         * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
    +         * to 0.
    +         *
    +         * Then the shrink-to-fit width is:
    +         * min(max(preferred minimum width, available width), preferred width).
    +        \*--------------------------------------------------------------------*/
    +        // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
    +        // because the value is not used for any further calculations.
    +
    +        // Calculate margins, 'auto' margins are ignored.
    +        marginLeftValue = marginLeft.calcMinValue(containerWidth);
    +        marginRightValue = marginRight.calcMinValue(containerWidth);
    +
    +        const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
    +
    +        // FIXME: Is there a faster way to find the correct case?
    +        // Use rule/case that applies.
    +        if (leftIsAuto && widthIsAuto && !rightIsAuto) {
    +            // RULE 1: (use shrink-to-fit for width, and solve of left)
    +            int rightValue = right.calcValue(containerWidth);
    +
    +            // FIXME: would it be better to have shrink-to-fit in one step?
    +            int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
    +            int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
    +            int availableWidth = availableSpace - rightValue;
    +            widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
    +            leftValue = availableSpace - (widthValue + rightValue);
    +        } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
    +            // RULE 3: (use shrink-to-fit for width, and no need solve of right)
    +            leftValue = left.calcValue(containerWidth);
    +
    +            // FIXME: would it be better to have shrink-to-fit in one step?
    +            int preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
    +            int preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
    +            int availableWidth = availableSpace - leftValue;
    +            widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
    +        } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) {
    +            // RULE 4: (solve for left)
    +            widthValue = computeContentBoxLogicalWidth(width.calcValue(containerWidth));
    +            leftValue = availableSpace - (widthValue + right.calcValue(containerWidth));
    +        } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
    +            // RULE 5: (solve for width)
    +            leftValue = left.calcValue(containerWidth);
    +            widthValue = availableSpace - (leftValue + right.calcValue(containerWidth));
    +        } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) {
    +            // RULE 6: (no need solve for right)
    +            leftValue = left.calcValue(containerWidth);
    +            widthValue = computeContentBoxLogicalWidth(width.calcValue(containerWidth));
    +        }
    +    }
    +
    +    // Use computed values to calculate the horizontal position.
    +
    +    // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
    +    // positioned, inline because right now, it is using the xPos
    +    // of the first line box when really it should use the last line box.  When
    +    // this is fixed elsewhere, this block should be removed.
    +    if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
    +        const RenderInline* flow = toRenderInline(containerBlock);
    +        InlineFlowBox* firstLine = flow->firstLineBox();
    +        InlineFlowBox* lastLine = flow->lastLineBox();
    +        if (firstLine && lastLine && firstLine != lastLine) {
    +            xPos = leftValue + marginLeftValue + lastLine->borderLogicalLeft() + (lastLine->x() - firstLine->x());
    +            return;
    +        }
    +    }
    +
    +    xPos = leftValue + marginLeftValue + containerBlock->borderLeft();
    +}
    +
    +void RenderBox::computePositionedLogicalHeight()
    +{
    +    if (isReplaced()) {
    +        computePositionedLogicalHeightReplaced();
    +        return;
    +    }
    +
    +    // The following is based off of the W3C Working Draft from April 11, 2006 of
    +    // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
    +    // 
    +    // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
    +    // correspond to text from the spec)
    +
    +
    +    // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
    +    const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
    +
    +    const int containerHeight = containingBlockHeightForPositioned(containerBlock);
    +
    +    const int bordersPlusPadding = borderAndPaddingHeight();
    +    const Length marginTop = style()->marginTop();
    +    const Length marginBottom = style()->marginBottom();
    +    Length top = style()->top();
    +    Length bottom = style()->bottom();
    +
    +    /*---------------------------------------------------------------------------*\
    +     * For the purposes of this section and the next, the term "static position"
    +     * (of an element) refers, roughly, to the position an element would have had
    +     * in the normal flow. More precisely, the static position for 'top' is the
    +     * distance from the top edge of the containing block to the top margin edge
    +     * of a hypothetical box that would have been the first box of the element if
    +     * its 'position' property had been 'static' and 'float' had been 'none'. The
    +     * value is negative if the hypothetical box is above the containing block.
    +     *
    +     * But rather than actually calculating the dimensions of that hypothetical
    +     * box, user agents are free to make a guess at its probable position.
    +     *
    +     * For the purposes of calculating the static position, the containing block
    +     * of fixed positioned elements is the initial containing block instead of
    +     * the viewport.
    +    \*---------------------------------------------------------------------------*/
    +
    +    // see FIXME 2
    +    // Calculate the static distance if needed.
    +    if (top.isAuto() && bottom.isAuto()) {
    +        // staticY should already have been set through layout of the parent()
    +        int staticTop = layer()->staticY() - containerBlock->borderTop();
    +        for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
    +            if (po->isBox() && !po->isTableRow())
    +                staticTop += toRenderBox(po)->y();
    +        }
    +        top.setValue(Fixed, staticTop);
    +    }
    +
    +
    +    int h; // Needed to compute overflow.
    +    int y;
    +
    +    // Calculate constraint equation values for 'height' case.
    +    computePositionedLogicalHeightUsing(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
    +                               top, bottom, marginTop, marginBottom,
    +                               h, m_marginTop, m_marginBottom, y);
    +    setY(y);
    +
    +    // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
    +    // see FIXME 3
    +
    +    // Calculate constraint equation values for 'max-height' case.
    +    if (!style()->maxHeight().isUndefined()) {
    +        int maxHeight;
    +        int maxMarginTop;
    +        int maxMarginBottom;
    +        int maxYPos;
    +
    +        computePositionedLogicalHeightUsing(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
    +                                   top, bottom, marginTop, marginBottom,
    +                                   maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
    +
    +        if (h > maxHeight) {
    +            h = maxHeight;
    +            m_marginTop = maxMarginTop;
    +            m_marginBottom = maxMarginBottom;
    +            m_frameRect.setY(maxYPos);
    +        }
    +    }
    +
    +    // Calculate constraint equation values for 'min-height' case.
    +    if (!style()->minHeight().isZero()) {
    +        int minHeight;
    +        int minMarginTop;
    +        int minMarginBottom;
    +        int minYPos;
    +
    +        computePositionedLogicalHeightUsing(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
    +                                   top, bottom, marginTop, marginBottom,
    +                                   minHeight, minMarginTop, minMarginBottom, minYPos);
    +
    +        if (h < minHeight) {
    +            h = minHeight;
    +            m_marginTop = minMarginTop;
    +            m_marginBottom = minMarginBottom;
    +            m_frameRect.setY(minYPos);
    +        }
    +    }
    +
    +    // Set final height value.
    +    setHeight(h + bordersPlusPadding);
    +}
    +
    +void RenderBox::computePositionedLogicalHeightUsing(Length h, const RenderBoxModelObject* containerBlock,
    +                                           const int containerHeight, const int bordersPlusPadding,
    +                                           const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
    +                                           int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos)
    +{
    +    // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
    +    // converted to the static position in computePositionedLogicalHeight()
    +    ASSERT(!(top.isAuto() && bottom.isAuto()));
    +
    +    int contentHeight = height() - bordersPlusPadding;
    +
    +    int topValue = 0;
    +
    +    bool heightIsAuto = h.isAuto();
    +    bool topIsAuto = top.isAuto();
    +    bool bottomIsAuto = bottom.isAuto();
    +
    +    // Height is never unsolved for tables.
    +    if (isTable()) {
    +        h.setValue(Fixed, contentHeight);
    +        heightIsAuto = false;
    +    }
    +
    +    if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
    +        /*-----------------------------------------------------------------------*\
    +         * If none of the three are 'auto': If both 'margin-top' and 'margin-
    +         * bottom' are 'auto', solve the equation under the extra constraint that
    +         * the two margins get equal values. If one of 'margin-top' or 'margin-
    +         * bottom' is 'auto', solve the equation for that value. If the values
    +         * are over-constrained, ignore the value for 'bottom' and solve for that
    +         * value.
    +        \*-----------------------------------------------------------------------*/
    +        // NOTE:  It is not necessary to solve for 'bottom' in the over constrained
    +        // case because the value is not used for any further calculations.
    +
    +        heightValue = computeContentBoxLogicalHeight(h.calcValue(containerHeight));
    +        topValue = top.calcValue(containerHeight);
    +
    +        const int availableSpace = containerHeight - (topValue + heightValue + bottom.calcValue(containerHeight) + bordersPlusPadding);
    +
    +        // Margins are now the only unknown
    +        if (marginTop.isAuto() && marginBottom.isAuto()) {
    +            // Both margins auto, solve for equality
    +            // NOTE: This may result in negative values.
    +            marginTopValue = availableSpace / 2; // split the difference
    +            marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
    +        } else if (marginTop.isAuto()) {
    +            // Solve for top margin
    +            marginBottomValue = marginBottom.calcValue(containerHeight);
    +            marginTopValue = availableSpace - marginBottomValue;
    +        } else if (marginBottom.isAuto()) {
    +            // Solve for bottom margin
    +            marginTopValue = marginTop.calcValue(containerHeight);
    +            marginBottomValue = availableSpace - marginTopValue;
    +        } else {
    +            // Over-constrained, (no need solve for bottom)
    +            marginTopValue = marginTop.calcValue(containerHeight);
    +            marginBottomValue = marginBottom.calcValue(containerHeight);
    +        }
    +    } else {
    +        /*--------------------------------------------------------------------*\
    +         * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
    +         * to 0, and pick the one of the following six rules that applies.
    +         *
    +         * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
    +         *    the height is based on the content, and solve for 'top'.
    +         *
    +         *              OMIT RULE 2 AS IT SHOULD NEVER BE HIT
    +         * ------------------------------------------------------------------
    +         * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
    +         *    set 'top' to the static position, and solve for 'bottom'.
    +         * ------------------------------------------------------------------
    +         *
    +         * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
    +         *    the height is based on the content, and solve for 'bottom'.
    +         * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
    +         *    solve for 'top'.
    +         * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
    +         *    solve for 'height'.
    +         * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
    +         *    solve for 'bottom'.
    +        \*--------------------------------------------------------------------*/
    +        // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
    +        // because the value is not used for any further calculations.
    +
    +        // Calculate margins, 'auto' margins are ignored.
    +        marginTopValue = marginTop.calcMinValue(containerHeight);
    +        marginBottomValue = marginBottom.calcMinValue(containerHeight);
    +
    +        const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
    +
    +        // Use rule/case that applies.
    +        if (topIsAuto && heightIsAuto && !bottomIsAuto) {
    +            // RULE 1: (height is content based, solve of top)
    +            heightValue = contentHeight;
    +            topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
    +        } else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
    +            // RULE 3: (height is content based, no need solve of bottom)
    +            topValue = top.calcValue(containerHeight);
    +            heightValue = contentHeight;
    +        } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
    +            // RULE 4: (solve of top)
    +            heightValue = computeContentBoxLogicalHeight(h.calcValue(containerHeight));
    +            topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
    +        } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
    +            // RULE 5: (solve of height)
    +            topValue = top.calcValue(containerHeight);
    +            heightValue = max(0, availableSpace - (topValue + bottom.calcValue(containerHeight)));
    +        } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
    +            // RULE 6: (no need solve of bottom)
    +            heightValue = computeContentBoxLogicalHeight(h.calcValue(containerHeight));
    +            topValue = top.calcValue(containerHeight);
    +        }
    +    }
    +
    +    // Use computed values to calculate the vertical position.
    +    yPos = topValue + marginTopValue + containerBlock->borderTop();
    +}
    +
    +void RenderBox::computePositionedLogicalWidthReplaced()
    +{
    +    // The following is based off of the W3C Working Draft from April 11, 2006 of
    +    // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
    +    // 
    +    // (block-style-comments in this function correspond to text from the spec and
    +    // the numbers correspond to numbers in spec)
    +
    +    // We don't use containingBlock(), since we may be positioned by an enclosing
    +    // relative positioned inline.
    +    const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
    +
    +    const int containerWidth = containingBlockWidthForPositioned(containerBlock);
    +
    +    // To match WinIE, in quirks mode use the parent's 'direction' property
    +    // instead of the the container block's.
    +    TextDirection containerDirection = (document()->inQuirksMode()) ? parent()->style()->direction() : containerBlock->style()->direction();
    +
    +    // Variables to solve.
    +    Length left = style()->left();
    +    Length right = style()->right();
    +    Length marginLeft = style()->marginLeft();
    +    Length marginRight = style()->marginRight();
    +
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 1. The used value of 'width' is determined as for inline replaced
    +     *    elements.
    +    \*-----------------------------------------------------------------------*/
    +    // NOTE: This value of width is FINAL in that the min/max width calculations
    +    // are dealt with in computeReplacedWidth().  This means that the steps to produce
    +    // correct max/min in the non-replaced version, are not necessary.
    +    setWidth(computeReplacedLogicalWidth() + borderAndPaddingWidth());
    +    const int availableSpace = containerWidth - width();
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
    +     *    of the containing block is 'ltr', set 'left' to the static position;
    +     *    else if 'direction' is 'rtl', set 'right' to the static position.
    +    \*-----------------------------------------------------------------------*/
    +    // see FIXME 2
    +    if (left.isAuto() && right.isAuto()) {
    +        // see FIXME 1
    +        if (containerDirection == LTR) {
    +            // 'staticX' should already have been set through layout of the parent.
    +            int staticPosition = layer()->staticX() - containerBlock->borderLeft();
    +            for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
    +                if (po->isBox())
    +                    staticPosition += toRenderBox(po)->x();
    +            }
    +            left.setValue(Fixed, staticPosition);
    +        } else {
    +            RenderObject* po = parent();
    +            // 'staticX' should already have been set through layout of the parent.
    +            int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
    +            for ( ; po && po != containerBlock; po = po->parent()) {
    +                if (po->isBox())
    +                    staticPosition += toRenderBox(po)->x();
    +            }
    +            right.setValue(Fixed, staticPosition);
    +        }
    +    }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
    +     *    or 'margin-right' with '0'.
    +    \*-----------------------------------------------------------------------*/
    +    if (left.isAuto() || right.isAuto()) {
    +        if (marginLeft.isAuto())
    +            marginLeft.setValue(Fixed, 0);
    +        if (marginRight.isAuto())
    +            marginRight.setValue(Fixed, 0);
    +    }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 4. If at this point both 'margin-left' and 'margin-right' are still
    +     *    'auto', solve the equation under the extra constraint that the two
    +     *    margins must get equal values, unless this would make them negative,
    +     *    in which case when the direction of the containing block is 'ltr'
    +     *    ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
    +     *    'margin-right' ('margin-left').
    +    \*-----------------------------------------------------------------------*/
    +    int leftValue = 0;
    +    int rightValue = 0;
    +
    +    if (marginLeft.isAuto() && marginRight.isAuto()) {
    +        // 'left' and 'right' cannot be 'auto' due to step 3
    +        ASSERT(!(left.isAuto() && right.isAuto()));
    +
    +        leftValue = left.calcValue(containerWidth);
    +        rightValue = right.calcValue(containerWidth);
    +
    +        int difference = availableSpace - (leftValue + rightValue);
    +        if (difference > 0) {
    +            m_marginLeft = difference / 2; // split the difference
    +            m_marginRight = difference - m_marginLeft; // account for odd valued differences
    +        } else {
    +            // see FIXME 1
    +            if (containerDirection == LTR) {
    +                m_marginLeft = 0;
    +                m_marginRight = difference;  // will be negative
    +            } else {
    +                m_marginLeft = difference;  // will be negative
    +                m_marginRight = 0;
    +            }
    +        }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 5. If at this point there is an 'auto' left, solve the equation for
    +     *    that value.
    +    \*-----------------------------------------------------------------------*/
    +    } else if (left.isAuto()) {
    +        m_marginLeft = marginLeft.calcValue(containerWidth);
    +        m_marginRight = marginRight.calcValue(containerWidth);
    +        rightValue = right.calcValue(containerWidth);
    +
    +        // Solve for 'left'
    +        leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
    +    } else if (right.isAuto()) {
    +        m_marginLeft = marginLeft.calcValue(containerWidth);
    +        m_marginRight = marginRight.calcValue(containerWidth);
    +        leftValue = left.calcValue(containerWidth);
    +
    +        // Solve for 'right'
    +        rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
    +    } else if (marginLeft.isAuto()) {
    +        m_marginRight = marginRight.calcValue(containerWidth);
    +        leftValue = left.calcValue(containerWidth);
    +        rightValue = right.calcValue(containerWidth);
    +
    +        // Solve for 'margin-left'
    +        m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
    +    } else if (marginRight.isAuto()) {
    +        m_marginLeft = marginLeft.calcValue(containerWidth);
    +        leftValue = left.calcValue(containerWidth);
    +        rightValue = right.calcValue(containerWidth);
    +
    +        // Solve for 'margin-right'
    +        m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
    +    } else {
    +        // Nothing is 'auto', just calculate the values.
    +        m_marginLeft = marginLeft.calcValue(containerWidth);
    +        m_marginRight = marginRight.calcValue(containerWidth);
    +        rightValue = right.calcValue(containerWidth);
    +        leftValue = left.calcValue(containerWidth);
    +    }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 6. If at this point the values are over-constrained, ignore the value
    +     *    for either 'left' (in case the 'direction' property of the
    +     *    containing block is 'rtl') or 'right' (in case 'direction' is
    +     *    'ltr') and solve for that value.
    +    \*-----------------------------------------------------------------------*/
    +    // NOTE:  It is not necessary to solve for 'right' when the direction is
    +    // LTR because the value is not used.
    +    int totalWidth = width() + leftValue + rightValue +  m_marginLeft + m_marginRight;
    +    if (totalWidth > containerWidth && (containerDirection == RTL))
    +        leftValue = containerWidth - (totalWidth - leftValue);
    +
    +    // Use computed values to calculate the horizontal position.
    +
    +    // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
    +    // positioned, inline containing block because right now, it is using the xPos
    +    // of the first line box when really it should use the last line box.  When
    +    // this is fixed elsewhere, this block should be removed.
    +    if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
    +        const RenderInline* flow = toRenderInline(containerBlock);
    +        InlineFlowBox* firstLine = flow->firstLineBox();
    +        InlineFlowBox* lastLine = flow->lastLineBox();
    +        if (firstLine && lastLine && firstLine != lastLine) {
    +            m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLogicalLeft() + (lastLine->x() - firstLine->x()));
    +            return;
    +        }
    +    }
    +
    +    m_frameRect.setX(leftValue + m_marginLeft + containerBlock->borderLeft());
    +}
    +
    +void RenderBox::computePositionedLogicalHeightReplaced()
    +{
    +    // The following is based off of the W3C Working Draft from April 11, 2006 of
    +    // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
    +    // 
    +    // (block-style-comments in this function correspond to text from the spec and
    +    // the numbers correspond to numbers in spec)
    +
    +    // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
    +    const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
    +
    +    const int containerHeight = containingBlockHeightForPositioned(containerBlock);
    +
    +    // Variables to solve.
    +    Length top = style()->top();
    +    Length bottom = style()->bottom();
    +    Length marginTop = style()->marginTop();
    +    Length marginBottom = style()->marginBottom();
    +
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 1. The used value of 'height' is determined as for inline replaced
    +     *    elements.
    +    \*-----------------------------------------------------------------------*/
    +    // NOTE: This value of height is FINAL in that the min/max height calculations
    +    // are dealt with in computeReplacedHeight().  This means that the steps to produce
    +    // correct max/min in the non-replaced version, are not necessary.
    +    setHeight(computeReplacedLogicalHeight() + borderAndPaddingHeight());
    +    const int availableSpace = containerHeight - height();
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
    +     *    with the element's static position.
    +    \*-----------------------------------------------------------------------*/
    +    // see FIXME 2
    +    if (top.isAuto() && bottom.isAuto()) {
    +        // staticY should already have been set through layout of the parent().
    +        int staticTop = layer()->staticY() - containerBlock->borderTop();
    +        for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
    +            if (po->isBox() && !po->isTableRow())
    +                staticTop += toRenderBox(po)->y();
    +        }
    +        top.setValue(Fixed, staticTop);
    +    }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
    +     *    'margin-bottom' with '0'.
    +    \*-----------------------------------------------------------------------*/
    +    // FIXME: The spec. says that this step should only be taken when bottom is
    +    // auto, but if only top is auto, this makes step 4 impossible.
    +    if (top.isAuto() || bottom.isAuto()) {
    +        if (marginTop.isAuto())
    +            marginTop.setValue(Fixed, 0);
    +        if (marginBottom.isAuto())
    +            marginBottom.setValue(Fixed, 0);
    +    }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 4. If at this point both 'margin-top' and 'margin-bottom' are still
    +     *    'auto', solve the equation under the extra constraint that the two
    +     *    margins must get equal values.
    +    \*-----------------------------------------------------------------------*/
    +    int topValue = 0;
    +    int bottomValue = 0;
    +
    +    if (marginTop.isAuto() && marginBottom.isAuto()) {
    +        // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
    +        ASSERT(!(top.isAuto() || bottom.isAuto()));
    +
    +        topValue = top.calcValue(containerHeight);
    +        bottomValue = bottom.calcValue(containerHeight);
    +
    +        int difference = availableSpace - (topValue + bottomValue);
    +        // NOTE: This may result in negative values.
    +        m_marginTop =  difference / 2; // split the difference
    +        m_marginBottom = difference - m_marginTop; // account for odd valued differences
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 5. If at this point there is only one 'auto' left, solve the equation
    +     *    for that value.
    +    \*-----------------------------------------------------------------------*/
    +    } else if (top.isAuto()) {
    +        m_marginTop = marginTop.calcValue(containerHeight);
    +        m_marginBottom = marginBottom.calcValue(containerHeight);
    +        bottomValue = bottom.calcValue(containerHeight);
    +
    +        // Solve for 'top'
    +        topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
    +    } else if (bottom.isAuto()) {
    +        m_marginTop = marginTop.calcValue(containerHeight);
    +        m_marginBottom = marginBottom.calcValue(containerHeight);
    +        topValue = top.calcValue(containerHeight);
    +
    +        // Solve for 'bottom'
    +        // NOTE: It is not necessary to solve for 'bottom' because we don't ever
    +        // use the value.
    +    } else if (marginTop.isAuto()) {
    +        m_marginBottom = marginBottom.calcValue(containerHeight);
    +        topValue = top.calcValue(containerHeight);
    +        bottomValue = bottom.calcValue(containerHeight);
    +
    +        // Solve for 'margin-top'
    +        m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
    +    } else if (marginBottom.isAuto()) {
    +        m_marginTop = marginTop.calcValue(containerHeight);
    +        topValue = top.calcValue(containerHeight);
    +        bottomValue = bottom.calcValue(containerHeight);
    +
    +        // Solve for 'margin-bottom'
    +        m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
    +    } else {
    +        // Nothing is 'auto', just calculate the values.
    +        m_marginTop = marginTop.calcValue(containerHeight);
    +        m_marginBottom = marginBottom.calcValue(containerHeight);
    +        topValue = top.calcValue(containerHeight);
    +        // NOTE: It is not necessary to solve for 'bottom' because we don't ever
    +        // use the value.
    +     }
    +
    +    /*-----------------------------------------------------------------------*\
    +     * 6. If at this point the values are over-constrained, ignore the value
    +     *    for 'bottom' and solve for that value.
    +    \*-----------------------------------------------------------------------*/
    +    // NOTE: It is not necessary to do this step because we don't end up using
    +    // the value of 'bottom' regardless of whether the values are over-constrained
    +    // or not.
    +
    +    // Use computed values to calculate the vertical position.
    +    m_frameRect.setY(topValue + m_marginTop + containerBlock->borderTop());
    +}
    +
    +IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine)
    +{
    +    // VisiblePositions at offsets inside containers either a) refer to the positions before/after
    +    // those containers (tables and select elements) or b) refer to the position inside an empty block.
    +    // They never refer to children.
    +    // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
    +
    +    // FIXME: What about border and padding?
    +    IntRect rect(x(), y(), caretWidth, height());
    +    bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection();
    +
    +    if ((!caretOffset) ^ ltr)
    +        rect.move(IntSize(width() - caretWidth, 0));
    +
    +    if (box) {
    +        RootInlineBox* rootBox = box->root();
    +        int top = rootBox->lineTop();
    +        rect.setY(top);
    +        rect.setHeight(rootBox->lineBottom() - top);
    +    }
    +
    +    // If height of box is smaller than font height, use the latter one,
    +    // otherwise the caret might become invisible.
    +    //
    +    // Also, if the box is not a replaced element, always use the font height.
    +    // This prevents the "big caret" bug described in:
    +    //  Deleting all content in a document can result in giant tall-as-window insertion point
    +    //
    +    // FIXME: ignoring :first-line, missing good reason to take care of
    +    int fontHeight = style()->font().height();
    +    if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
    +        rect.setHeight(fontHeight);
    +
    +    if (extraWidthToEndOfLine)
    +        *extraWidthToEndOfLine = x() + width() - rect.right();
    +
    +    // Move to local coords
    +    rect.move(-x(), -y());
    +    return rect;
    +}
    +
    +VisiblePosition RenderBox::positionForPoint(const IntPoint& point)
    +{
    +    // no children...return this render object's element, if there is one, and offset 0
    +    if (!firstChild())
    +        return createVisiblePosition(node() ? firstDeepEditingPositionForNode(node()) : Position(0, 0));
    +
    +    int xPos = point.x();
    +    int yPos = point.y();
    +
    +    if (isTable() && node()) {
    +        int right = contentWidth() + borderAndPaddingWidth();
    +        int bottom = contentHeight() + borderAndPaddingHeight();
    +        
    +        if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
    +            if (xPos <= right / 2)
    +                return createVisiblePosition(firstDeepEditingPositionForNode(node()));
    +            return createVisiblePosition(lastDeepEditingPositionForNode(node()));
    +        }
    +    }
    +
    +    // Pass off to the closest child.
    +    int minDist = INT_MAX;
    +    RenderBox* closestRenderer = 0;
    +    int newX = xPos;
    +    int newY = yPos;
    +    if (isTableRow()) {
    +        newX += x();
    +        newY += y();
    +    }
    +    for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
    +        if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() )
    +            || renderObject->style()->visibility() != VISIBLE)
    +            continue;
    +        
    +        if (!renderObject->isBox())
    +            continue;
    +        
    +        RenderBox* renderer = toRenderBox(renderObject);
    +
    +        int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y());
    +        int bottom = top + renderer->contentHeight();
    +        int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x());
    +        int right = left + renderer->contentWidth();
    +        
    +        if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
    +            if (renderer->isTableRow())
    +                return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
    +            return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
    +        }
    +
    +        // Find the distance from (x, y) to the box.  Split the space around the box into 8 pieces
    +        // and use a different compare depending on which piece (x, y) is in.
    +        IntPoint cmp;
    +        if (xPos > right) {
    +            if (yPos < top)
    +                cmp = IntPoint(right, top);
    +            else if (yPos > bottom)
    +                cmp = IntPoint(right, bottom);
    +            else
    +                cmp = IntPoint(right, yPos);
    +        } else if (xPos < left) {
    +            if (yPos < top)
    +                cmp = IntPoint(left, top);
    +            else if (yPos > bottom)
    +                cmp = IntPoint(left, bottom);
    +            else
    +                cmp = IntPoint(left, yPos);
    +        } else {
    +            if (yPos < top)
    +                cmp = IntPoint(xPos, top);
    +            else
    +                cmp = IntPoint(xPos, bottom);
    +        }
    +        
    +        int x1minusx2 = cmp.x() - xPos;
    +        int y1minusy2 = cmp.y() - yPos;
    +        
    +        int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
    +        if (dist < minDist) {
    +            closestRenderer = renderer;
    +            minDist = dist;
    +        }
    +    }
    +    
    +    if (closestRenderer)
    +        return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
    +    
    +    return createVisiblePosition(firstDeepEditingPositionForNode(node()));
    +}
    +
    +bool RenderBox::shrinkToAvoidFloats() const
    +{
    +    // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this
    +    // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the
    +    // current remaining width on a line.
    +    if ((isInline() && !isHTMLMarquee()) || !avoidsFloats())
    +        return false;
    +
    +    // All auto-width objects that avoid floats should always use lineWidth.
    +    return style()->width().isAuto();
    +}
    +
    +bool RenderBox::avoidsFloats() const
    +{
    +    return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot();
    +}
    +
    +void RenderBox::addShadowOverflow()
    +{
    +    int shadowLeft;
    +    int shadowRight;
    +    int shadowTop;
    +    int shadowBottom;
    +    style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
    +    IntRect borderBox = borderBoxRect();
    +    int overflowLeft = borderBox.x() + shadowLeft;
    +    int overflowRight = borderBox.right() + shadowRight;
    +    int overflowTop = borderBox.y() + shadowTop;
    +    int overflowBottom = borderBox.bottom() + shadowBottom;
    +    addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop));
    +}
    +
    +void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta)
    +{
    +    // Only propagate layout overflow from the child if the child isn't clipping its overflow.  If it is, then
    +    // its overflow is internal to it, and we don't care about it.  layoutOverflowRectForPropagation takes care of this
    +    // and just propagates the border box rect instead.
    +    IntRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style());
    +    childLayoutOverflowRect.move(delta);
    +    addLayoutOverflow(childLayoutOverflowRect);
    +            
    +    // Add in visual overflow from the child.  Even if the child clips its overflow, it may still
    +    // have visual overflow of its own set from box shadows or reflections.  It is unnecessary to propagate this
    +    // overflow if we are clipping our own overflow.
    +    if (child->hasSelfPaintingLayer() || hasOverflowClip())
    +        return;
    +    IntRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style());
    +    childVisualOverflowRect.move(delta);
    +    addVisualOverflow(childVisualOverflowRect);
    +}
    +
    +void RenderBox::addLayoutOverflow(const IntRect& rect)
    +{
    +    IntRect clientBox = clientBoxRect();
    +    if (clientBox.contains(rect) || rect.isEmpty())
    +        return;
    +    
    +    // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
    +    IntRect overflowRect(rect);
    +    if (hasOverflowClip() || isRenderView()) {
    +        // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl 
    +        // writing modes.  At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
    +        // and vertical-lr/rl as the same.
    +        bool hasTopOverflow = !style()->isLeftToRightDirection() && !style()->isHorizontalWritingMode();
    +        bool hasLeftOverflow = !style()->isLeftToRightDirection() && style()->isHorizontalWritingMode();
    +        
    +        if (!hasTopOverflow)
    +            overflowRect.shiftTopEdgeTo(max(overflowRect.y(), clientBox.y()));
    +        else
    +            overflowRect.shiftBottomEdgeTo(min(overflowRect.bottom(), clientBox.bottom()));
    +        if (!hasLeftOverflow)
    +            overflowRect.shiftLeftEdgeTo(max(overflowRect.x(), clientBox.x()));
    +        else
    +            overflowRect.shiftRightEdgeTo(min(overflowRect.right(), clientBox.right()));
    +        
    +        // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
    +        // contained.
    +        if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
    +            return;
    +    }
    +
    +    if (!m_overflow)
    +        m_overflow.set(new RenderOverflow(clientBox, borderBoxRect()));
    +    
    +    m_overflow->addLayoutOverflow(overflowRect);
    +}
    +
    +void RenderBox::addVisualOverflow(const IntRect& rect)
    +{
    +    IntRect borderBox = borderBoxRect();
    +    if (borderBox.contains(rect) || rect.isEmpty())
    +        return;
    +        
    +    if (!m_overflow)
    +        m_overflow.set(new RenderOverflow(clientBoxRect(), borderBox));
    +    
    +    m_overflow->addVisualOverflow(rect);
    +}
    +
    +void RenderBox::clearLayoutOverflow()
    +{
    +    if (!m_overflow)
    +        return;
    +    
    +    if (visualOverflowRect() == borderBoxRect()) {
    +        m_overflow.clear();
    +        return;
    +    }
    +    
    +    m_overflow->resetLayoutOverflow(borderBoxRect());
    +}
    +
    +int RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
    +{
    +    if (isReplaced())
    +        return direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft;
    +    return 0;
    +}
    +
    +int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
    +{
    +    if (isReplaced()) {
    +        int result = direction == HorizontalLine ? m_marginTop + height() + m_marginBottom : m_marginRight + width() + m_marginLeft;
    +        if (baselineType == AlphabeticBaseline)
    +            return result;
    +        return result - result / 2;
    +    }
    +    return 0;
    +}
    +
    +
    +RenderLayer* RenderBox::enclosingFloatPaintingLayer() const
    +{
    +    const RenderObject* curr = this;
    +    while (curr) {
    +        RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBoxModelObject(curr)->layer() : 0;
    +        if (layer && layer->isSelfPaintingLayer())
    +            return layer;
    +        curr = curr->parent();
    +    }
    +    return 0;
    +}
    +
    +IntRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const
    +{
    +    IntRect rect = visualOverflowRectForPropagation(parentStyle);
    +    if (!parentStyle->isHorizontalWritingMode())
    +        return rect.transposedRect();
    +    return rect;
    +}
    +
    +IntRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const
    +{
    +    // If the writing modes of the child and parent match, then we don't have to 
    +    // do anything fancy. Just return the result.
    +    IntRect rect = visualOverflowRect();
    +    if (parentStyle->writingMode() == style()->writingMode())
    +        return rect;
    +    
    +    // We are putting ourselves into our parent's coordinate space.  If there is a flipped block mismatch
    +    // in a particular axis, then we have to flip the rect along that axis.
    +    if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
    +        rect.setX(width() - rect.right());
    +    else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
    +        rect.setY(height() - rect.bottom());
    +
    +    return rect;
    +}
    +
    +IntRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const
    +{
    +    IntRect rect = layoutOverflowRectForPropagation(parentStyle);
    +    if (!parentStyle->isHorizontalWritingMode())
    +        return rect.transposedRect();
    +    return rect;
    +}
    +
    +IntRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const
    +{
    +    // Only propagate interior layout overflow if we don't clip it.
    +    IntRect rect = borderBoxRect();
    +    if (!hasOverflowClip())
    +        rect.unite(layoutOverflowRect());
    +
    +    if (isRelPositioned() || hasTransform()) {
    +        // If we are relatively positioned or if we have a transform, then we have to convert
    +        // this rectangle into physical coordinates, apply relative positioning and transforms
    +        // to it, and then convert it back.
    +        flipForWritingMode(rect);
    +        
    +        if (hasTransform())
    +            rect = layer()->currentTransform().mapRect(rect);
    +
    +        if (isRelPositioned())
    +            rect.move(relativePositionOffsetX(), relativePositionOffsetY());
    +        
    +        // Now we need to flip back.
    +        flipForWritingMode(rect);
    +    }
    +    
    +    // If the writing modes of the child and parent match, then we don't have to 
    +    // do anything fancy. Just return the result.
    +    if (parentStyle->writingMode() == style()->writingMode())
    +        return rect;
    +    
    +    // We are putting ourselves into our parent's coordinate space.  If there is a flipped block mismatch
    +    // in a particular axis, then we have to flip the rect along that axis.
    +    if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
    +        rect.setX(width() - rect.right());
    +    else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
    +        rect.setY(height() - rect.bottom());
    +
    +    return rect;
    +}
    +
    +IntPoint RenderBox::flipForWritingMode(const RenderBox* child, const IntPoint& point, FlippingAdjustment adjustment) const
    +{
    +    if (!style()->isFlippedBlocksWritingMode())
    +        return point;
    +    
    +    // The child is going to add in its x() and y(), so we have to make sure it ends up in
    +    // the right place.
    +    if (style()->isHorizontalWritingMode())
    +        return IntPoint(point.x(), point.y() + height() - child->height() - child->y() - (adjustment == ParentToChildFlippingAdjustment ? child->y() : 0));
    +    return IntPoint(point.x() + width() - child->width() - child->x() - (adjustment == ParentToChildFlippingAdjustment ? child->x() : 0), point.y());
    +}
    +
    +void RenderBox::flipForWritingMode(IntRect& rect) const
    +{
    +    if (!style()->isFlippedBlocksWritingMode())
    +        return;
    +
    +    if (style()->isHorizontalWritingMode())
    +        rect.setY(height() - rect.bottom());
    +    else
    +        rect.setX(width() - rect.right());
    +}
    +
    +int RenderBox::flipForWritingMode(int position) const
    +{
    +    if (!style()->isFlippedBlocksWritingMode())
    +        return position;
    +    return logicalHeight() - position;
    +}
    +
    +IntPoint RenderBox::flipForWritingMode(const IntPoint& position) const
    +{
    +    if (!style()->isFlippedBlocksWritingMode())
    +        return position;
    +    return style()->isHorizontalWritingMode() ? IntPoint(position.x(), height() - position.y()) : IntPoint(width() - position.x(), position.y());
    +}
    +
    +IntSize RenderBox::flipForWritingMode(const IntSize& offset) const
    +{
    +    if (!style()->isFlippedBlocksWritingMode())
    +        return offset;
    +    return style()->isHorizontalWritingMode() ? IntSize(offset.width(), height() - offset.height()) : IntSize(width() - offset.width(), offset.height());
    +}
    +
    +IntSize RenderBox::locationOffsetIncludingFlipping() const
    +{
    +    if (!parent() || !parent()->isBox())
    +        return locationOffset();
    +    
    +    IntRect rect(frameRect());
    +    parentBox()->flipForWritingMode(rect);
    +    return IntSize(rect.x(), rect.y());
    +}
    +
    +} // namespace WebCore
    diff --git a/Source/WebCore/rendering/RenderBox.h b/Source/WebCore/rendering/RenderBox.h
    new file mode 100644
    index 0000000..53a4cfe
    --- /dev/null
    +++ b/Source/WebCore/rendering/RenderBox.h
    @@ -0,0 +1,523 @@
    +/*
    + * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
    + *           (C) 1999 Antti Koivisto (koivisto@kde.org)
    + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved.
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Library General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    + * Library General Public License for more details.
    + *
    + * You should have received a copy of the GNU Library General Public License
    + * along with this library; see the file COPYING.LIB.  If not, write to
    + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    + * Boston, MA 02110-1301, USA.
    + *
    + */
    +
    +#ifndef RenderBox_h
    +#define RenderBox_h
    +
    +#include "RenderBoxModelObject.h"
    +#include "RenderOverflow.h"
    +#include "ScrollTypes.h"
    +
    +namespace WebCore {
    +
    +enum LogicalWidthType { LogicalWidth, MinLogicalWidth, MaxLogicalWidth };
    +
    +class RenderBox : public RenderBoxModelObject {
    +public:
    +    RenderBox(Node*);
    +    virtual ~RenderBox();
    +
    +    // Use this with caution! No type checking is done!
    +    RenderBox* firstChildBox() const;
    +    RenderBox* lastChildBox() const;
    +
    +    int x() const { return m_frameRect.x(); }
    +    int y() const { return m_frameRect.y(); }
    +    int width() const { return m_frameRect.width(); }
    +    int height() const { return m_frameRect.height(); }
    +
    +    void setX(int x) { m_frameRect.setX(x); }
    +    void setY(int y) { m_frameRect.setY(y); }
    +    void setWidth(int width) { m_frameRect.setWidth(width); }
    +    void setHeight(int height) { m_frameRect.setHeight(height); }
    +
    +    int logicalLeft() const { return style()->isHorizontalWritingMode() ? x() : y(); }
    +    int logicalRight() const { return logicalLeft() + logicalWidth(); }
    +    int logicalTop() const { return style()->isHorizontalWritingMode() ? y() : x(); }
    +    int logicalBottom() const { return logicalTop() + logicalHeight(); }
    +    int logicalWidth() const { return style()->isHorizontalWritingMode() ? width() : height(); }
    +    int logicalHeight() const { return style()->isHorizontalWritingMode() ? height() : width(); }
    +
    +    void setLogicalLeft(int left)
    +    {
    +        if (style()->isHorizontalWritingMode())
    +            setX(left);
    +        else
    +            setY(left);
    +    }
    +    void setLogicalTop(int top)
    +    {
    +        if (style()->isHorizontalWritingMode())
    +            setY(top);
    +        else
    +            setX(top);
    +    }
    +    void setLogicalWidth(int size)
    +    {
    +        if (style()->isHorizontalWritingMode())
    +            setWidth(size);
    +        else
    +            setHeight(size);
    +    }
    +    void setLogicalHeight(int size)
    +    {
    +        if (style()->isHorizontalWritingMode())
    +            setHeight(size);
    +        else
    +            setWidth(size);
    +    }
    +    void setLogicalLocation(int left, int top)
    +    {
    +        if (style()->isHorizontalWritingMode())
    +            setLocation(left, top);
    +        else
    +            setLocation(top, left);
    +    }
    +
    +    IntPoint location() const { return m_frameRect.location(); }
    +    IntSize locationOffset() const { return IntSize(x(), y()); }
    +    IntSize size() const { return m_frameRect.size(); }
    +
    +    void setLocation(const IntPoint& location) { m_frameRect.setLocation(location); }
    +    void setLocation(int x, int y) { setLocation(IntPoint(x, y)); }
    +    
    +    void setSize(const IntSize& size) { m_frameRect.setSize(size); }
    +    void move(int dx, int dy) { m_frameRect.move(dx, dy); }
    +
    +    IntRect frameRect() const { return m_frameRect; }
    +    void setFrameRect(const IntRect& rect) { m_frameRect = rect; }
    +
    +    IntRect borderBoxRect() const { return IntRect(0, 0, width(), height()); }
    +    virtual IntRect borderBoundingBox() const { return borderBoxRect(); } 
    +
    +    // The content area of the box (excludes padding and border).
    +    IntRect contentBoxRect() const { return IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), contentWidth(), contentHeight()); }
    +    // The content box in absolute coords. Ignores transforms.
    +    IntRect absoluteContentBox() const;
    +    // The content box converted to absolute coords (taking transforms into account).
    +    FloatQuad absoluteContentQuad() const;
    +
    +    // Bounds of the outline box in absolute coords. Respects transforms
    +    virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/, IntPoint* cachedOffsetToRepaintContainer) const;
    +    virtual void addFocusRingRects(Vector&, int tx, int ty);
    +
    +    // Use this with caution! No type checking is done!
    +    RenderBox* previousSiblingBox() const;
    +    RenderBox* nextSiblingBox() const;
    +    RenderBox* parentBox() const;
    +
    +    IntRect layoutOverflowRect() const { return m_overflow ? m_overflow->layoutOverflowRect() : clientBoxRect(); }
    +    int topLayoutOverflow() const { return m_overflow? m_overflow->topLayoutOverflow() : borderTop(); }
    +    int bottomLayoutOverflow() const { return m_overflow ? m_overflow->bottomLayoutOverflow() : borderTop() + clientHeight(); }
    +    int leftLayoutOverflow() const { return m_overflow ? m_overflow->leftLayoutOverflow() : borderLeft(); }
    +    int rightLayoutOverflow() const { return m_overflow ? m_overflow->rightLayoutOverflow() : borderLeft() + clientWidth(); }
    +    int logicalLeftLayoutOverflow() const { return style()->isHorizontalWritingMode() ? leftLayoutOverflow() : topLayoutOverflow(); }
    +    int logicalRightLayoutOverflow() const { return style()->isHorizontalWritingMode() ? rightLayoutOverflow() : bottomLayoutOverflow(); }
    +    
    +    IntRect visualOverflowRect() const { return m_overflow ? m_overflow->visualOverflowRect() : borderBoxRect(); }
    +    int topVisualOverflow() const { return m_overflow? m_overflow->topVisualOverflow() : 0; }
    +    int bottomVisualOverflow() const { return m_overflow ? m_overflow->bottomVisualOverflow() : height(); }
    +    int leftVisualOverflow() const { return m_overflow ? m_overflow->leftVisualOverflow() : 0; }
    +    int rightVisualOverflow() const { return m_overflow ? m_overflow->rightVisualOverflow() : width(); }
    +    int logicalLeftVisualOverflow() const { return style()->isHorizontalWritingMode() ? leftVisualOverflow() : topVisualOverflow(); }
    +    int logicalRightVisualOverflow() const { return style()->isHorizontalWritingMode() ? rightVisualOverflow() : bottomVisualOverflow(); }
    +    
    +    void addLayoutOverflow(const IntRect&);
    +    void addVisualOverflow(const IntRect&);
    +    
    +    void addShadowOverflow();
    +    void addOverflowFromChild(RenderBox* child) { addOverflowFromChild(child, IntSize(child->x(), child->y())); }
    +    void addOverflowFromChild(RenderBox* child, const IntSize& delta);
    +    void clearLayoutOverflow();
    +    
    +    void updateLayerTransform();
    +
    +    void blockDirectionOverflow(bool isLineHorizontal, int& logicalTopLayoutOverflow, int& logicalBottomLayoutOverflow,
    +                                int& logicalTopVisualOverflow, int& logicalBottomVisualOverflow);
    +
    +    int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); }
    +    int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); }
    +    int contentLogicalWidth() const { return style()->isHorizontalWritingMode() ? contentWidth() : contentHeight(); }
    +    int contentLogicalHeight() const { return style()->isHorizontalWritingMode() ? contentHeight() : contentWidth(); }
    +
    +    // IE extensions. Used to calculate offsetWidth/Height.  Overridden by inlines (RenderFlow)
    +    // to return the remaining width on a given line (and the height of a single line).
    +    virtual int offsetWidth() const { return width(); }
    +    virtual int offsetHeight() const { return height(); }
    +
    +    // More IE extensions.  clientWidth and clientHeight represent the interior of an object
    +    // excluding border and scrollbar.  clientLeft/Top are just the borderLeftWidth and borderTopWidth.
    +    int clientLeft() const { return borderLeft(); }
    +    int clientTop() const { return borderTop(); }
    +    int clientWidth() const;
    +    int clientHeight() const;
    +    int clientLogicalBottom() const { return style()->isHorizontalWritingMode() ? clientTop() + clientHeight() : clientLeft() + clientWidth(); }
    +    IntRect clientBoxRect() const { return IntRect(clientLeft(), clientTop(), clientWidth(), clientHeight()); }
    +
    +    // scrollWidth/scrollHeight will be the same as clientWidth/clientHeight unless the
    +    // object has overflow:hidden/scroll/auto specified and also has overflow.
    +    // scrollLeft/Top return the current scroll position.  These methods are virtual so that objects like
    +    // textareas can scroll shadow content (but pretend that they are the objects that are
    +    // scrolling).
    +    virtual int scrollLeft() const;
    +    virtual int scrollTop() const;
    +    virtual int scrollWidth() const;
    +    virtual int scrollHeight() const;
    +    virtual void setScrollLeft(int);
    +    virtual void setScrollTop(int);
    +
    +    virtual int marginTop() const { return m_marginTop; }
    +    virtual int marginBottom() const { return m_marginBottom; }
    +    virtual int marginLeft() const { return m_marginLeft; }
    +    virtual int marginRight() const { return m_marginRight; }
    +    void setMarginTop(int margin) { m_marginTop = margin; }
    +    void setMarginBottom(int margin) { m_marginBottom = margin; }
    +    void setMarginLeft(int margin) { m_marginLeft = margin; }
    +    void setMarginRight(int margin) { m_marginRight = margin; }
    +    virtual int marginBefore() const;
    +    virtual int marginAfter() const;
    +    virtual int marginStart() const;
    +    virtual int marginEnd() const;
    +    void setMarginStart(int);
    +    void setMarginEnd(int);
    +    void setMarginBefore(int);
    +    void setMarginAfter(int);
    +
    +    // The following five functions are used to implement collapsing margins.
    +    // All objects know their maximal positive and negative margins.  The
    +    // formula for computing a collapsed margin is |maxPosMargin| - |maxNegmargin|.
    +    // For a non-collapsing box, such as a leaf element, this formula will simply return
    +    // the margin of the element.  Blocks override the maxMarginBefore and maxMarginAfter
    +    // methods.
    +    enum MarginSign { PositiveMargin, NegativeMargin };
    +    virtual bool isSelfCollapsingBlock() const { return false; }
    +    virtual int collapsedMarginBefore() const { return marginBefore(); }
    +    virtual int collapsedMarginAfter() const { return marginAfter(); }
    +
    +    virtual void absoluteRects(Vector&, int tx, int ty);
    +    virtual void absoluteQuads(Vector&);
    +    
    +    IntRect reflectionBox() const;
    +    int reflectionOffset() const;
    +    // Given a rect in the object's coordinate space, returns the corresponding rect in the reflection.
    +    IntRect reflectedRect(const IntRect&) const;
    +
    +    virtual void layout();
    +    virtual void paint(PaintInfo&, int tx, int ty);
    +    virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
    +
    +    virtual void destroy();
    +
    +    virtual int minPreferredLogicalWidth() const;
    +    virtual int maxPreferredLogicalWidth() const;
    +
    +    int overrideSize() const;
    +    int overrideWidth() const;
    +    int overrideHeight() const;
    +    virtual void setOverrideSize(int);
    +
    +    virtual IntSize offsetFromContainer(RenderObject*, const IntPoint&) const;
    +    
    +    int computeBorderBoxLogicalWidth(int width) const;
    +    int computeBorderBoxLogicalHeight(int height) const;
    +    int computeContentBoxLogicalWidth(int width) const;
    +    int computeContentBoxLogicalHeight(int height) const;
    +
    +    virtual void borderFitAdjust(int& /*x*/, int& /*w*/) const { } // Shrink the box in which the border paints if border-fit is set.
    +
    +    // Resolve auto margins in the inline direction of the containing block so that objects can be pushed to the start, middle or end
    +    // of the containing block.
    +    void computeInlineDirectionMargins(RenderBlock* containingBlock, int containerWidth, int childWidth);
    +
    +    // Used to resolve margins in the containing block's block-flow direction.
    +    void computeBlockDirectionMargins(RenderBlock* containingBlock);
    +
    +    void positionLineBox(InlineBox*);
    +
    +    virtual InlineBox* createInlineBox();
    +    void dirtyLineBoxes(bool fullLayout);
    +
    +    // For inline replaced elements, this function returns the inline box that owns us.  Enables
    +    // the replaced RenderObject to quickly determine what line it is contained on and to easily
    +    // iterate over structures on the line.
    +    InlineBox* inlineBoxWrapper() const { return m_inlineBoxWrapper; }
    +    void setInlineBoxWrapper(InlineBox* boxWrapper) { m_inlineBoxWrapper = boxWrapper; }
    +    void deleteLineBoxWrapper();
    +
    +    virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
    +    virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
    +
    +    virtual void repaintDuringLayoutIfMoved(const IntRect&);
    +
    +    virtual int containingBlockLogicalWidthForContent() const;
    +    int perpendicularContainingBlockLogicalHeight() const;
    +    
    +    virtual void computeLogicalWidth();
    +    virtual void computeLogicalHeight();
    +
    +    bool stretchesToViewport() const
    +    {
    +        return document()->inQuirksMode() && style()->logicalHeight().isAuto() && !isFloatingOrPositioned() && (isRoot() || isBody());
    +    }
    +
    +    virtual IntSize intrinsicSize() const { return IntSize(); }
    +    int intrinsicLogicalWidth() const { return style()->isHorizontalWritingMode() ? intrinsicSize().width() : intrinsicSize().height(); }
    +    int intrinsicLogicalHeight() const { return style()->isHorizontalWritingMode() ? intrinsicSize().height() : intrinsicSize().width(); }
    +
    +    // Whether or not the element shrinks to its intrinsic width (rather than filling the width
    +    // of a containing block).  HTML4 buttons, s, legends, and floating/compact elements do this.
    +    bool sizesToIntrinsicLogicalWidth(LogicalWidthType) const;
    +    virtual bool stretchesToMinIntrinsicLogicalWidth() const { return false; }
    +
    +    int computeLogicalWidthUsing(LogicalWidthType, int availableLogicalWidth);
    +    int computeLogicalHeightUsing(const Length& height);
    +    int computeReplacedLogicalWidthUsing(Length width) const;
    +    int computeReplacedLogicalHeightUsing(Length height) const;
    +
    +    virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const;
    +    virtual int computeReplacedLogicalHeight() const;
    +
    +    int computePercentageLogicalHeight(const Length& height);
    +
    +    // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.)
    +    virtual int availableLogicalWidth() const { return contentLogicalWidth(); }
    +    int availableLogicalHeight() const;
    +    int availableLogicalHeightUsing(const Length&) const;
    +    
    +    // There are a few cases where we need to refer specifically to the available physical width and available physical height.
    +    // Relative positioning is one of those cases, since left/top offsets are physical.
    +    int availableWidth() const { return style()->isHorizontalWritingMode() ? availableLogicalWidth() : availableLogicalHeight(); }
    +    int availableHeight() const { return style()->isHorizontalWritingMode() ? availableLogicalHeight() : availableLogicalWidth(); }
    +
    +    virtual int verticalScrollbarWidth() const;
    +    int horizontalScrollbarHeight() const;
    +    int scrollbarLogicalHeight() const { return style()->isHorizontalWritingMode() ? horizontalScrollbarHeight() : verticalScrollbarWidth(); }
    +    virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0);
    +    virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0);
    +    bool canBeScrolledAndHasScrollableArea() const;
    +    virtual bool canBeProgramaticallyScrolled(bool) const;
    +    virtual void autoscroll();
    +    virtual void stopAutoscroll() { }
    +    virtual void panScroll(const IntPoint&);
    +    bool hasAutoVerticalScrollbar() const { return hasOverflowClip() && (style()->overflowY() == OAUTO || style()->overflowY() == OOVERLAY); }
    +    bool hasAutoHorizontalScrollbar() const { return hasOverflowClip() && (style()->overflowX() == OAUTO || style()->overflowX() == OOVERLAY); }
    +    bool scrollsOverflow() const { return scrollsOverflowX() || scrollsOverflowY(); }
    +    bool scrollsOverflowX() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || hasAutoHorizontalScrollbar()); }
    +    bool scrollsOverflowY() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || hasAutoVerticalScrollbar()); }
    +    
    +    virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
    +
    +    virtual IntRect overflowClipRect(int tx, int ty);
    +    IntRect clipRect(int tx, int ty);
    +    virtual bool hasControlClip() const { return false; }
    +    virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const { return IntRect(); }
    +    bool pushContentsClip(PaintInfo&, int tx, int ty);
    +    void popContentsClip(PaintInfo&, PaintPhase originalPhase, int tx, int ty);
    +
    +    virtual void paintObject(PaintInfo&, int /*tx*/, int /*ty*/) { ASSERT_NOT_REACHED(); }
    +    virtual void paintBoxDecorations(PaintInfo&, int tx, int ty);
    +    virtual void paintMask(PaintInfo&, int tx, int ty);
    +    virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
    +
    +    // Called when a positioned object moves but doesn't necessarily change size.  A simplified layout is attempted
    +    // that just updates the object's position. If the size does change, the object remains dirty.
    +    void tryLayoutDoingPositionedMovementOnly()
    +    {
    +        int oldWidth = width();
    +        computeLogicalWidth();
    +        // If we shrink to fit our width may have changed, so we still need full layout.
    +        if (oldWidth != width())
    +            return;
    +        computeLogicalHeight();
    +        setNeedsLayout(false);
    +    }
    +
    +    IntRect maskClipRect();
    +
    +    virtual VisiblePosition positionForPoint(const IntPoint&);
    +
    +    void removeFloatingOrPositionedChildFromBlockLists();
    +    
    +    RenderLayer* enclosingFloatPaintingLayer() const;
    +    
    +    virtual int firstLineBoxBaseline() const { return -1; }
    +    virtual int lastLineBoxBaseline() const { return -1; }
    +
    +    bool shrinkToAvoidFloats() const;
    +    virtual bool avoidsFloats() const;
    +
    +    virtual void markForPaginationRelayoutIfNeeded() { }
    +
    +    bool isWritingModeRoot() const { return !parent() || parent()->style()->writingMode() != style()->writingMode(); }
    +    
    +    virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
    +    virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
    +
    +    enum FlippingAdjustment { ChildToParentFlippingAdjustment, ParentToChildFlippingAdjustment };
    +    IntPoint flipForWritingMode(const RenderBox* child, const IntPoint&, FlippingAdjustment) const;
    +    int flipForWritingMode(int position) const; // The offset is in the block direction (y for horizontal writing modes, x for vertical writing modes).
    +    IntPoint flipForWritingMode(const IntPoint&) const;
    +    IntSize flipForWritingMode(const IntSize&) const;
    +    void flipForWritingMode(IntRect&) const;
    +    IntSize locationOffsetIncludingFlipping() const;
    +
    +    IntRect logicalVisualOverflowRectForPropagation(RenderStyle*) const;
    +    IntRect visualOverflowRectForPropagation(RenderStyle*) const;
    +    IntRect logicalLayoutOverflowRectForPropagation(RenderStyle*) const;
    +    IntRect layoutOverflowRectForPropagation(RenderStyle*) const;
    +
    +#ifdef ANDROID_LAYOUT
    +    int getVisibleWidth() const { return m_visibleWidth; }
    +#endif
    +
    +protected:
    +    virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
    +    virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
    +    virtual void updateBoxModelInfoFromStyle();
    +
    +    void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject);
    +    void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0);
    +
    +    void paintBoxDecorationsWithSize(PaintInfo&, int tx, int ty, int width, int height);
    +    void paintMaskImages(const PaintInfo&, int tx, int ty, int width, int height);
    +
    +#if PLATFORM(MAC)
    +    void paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText);
    +#endif
    +
    +    void computePositionedLogicalWidth();
    +    
    +    virtual bool shouldComputeSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); }
    +
    +    virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState&) const;
    +    virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const;
    +
    +private:
    +    bool includeVerticalScrollbarSize() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); }
    +    bool includeHorizontalScrollbarSize() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); }
    +
    +    void paintRootBoxDecorations(PaintInfo&, int tx, int ty);
    +    // Returns true if we did a full repaint
    +    bool repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground);
    +   
    +    int containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const;
    +    int containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const;
    +
    +    void computePositionedLogicalHeight();
    +    void computePositionedLogicalWidthUsing(Length width, const RenderBoxModelObject* cb, TextDirection containerDirection,
    +                                      int containerWidth, int bordersPlusPadding,
    +                                      Length left, Length right, Length marginLeft, Length marginRight,
    +                                      int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos);
    +    void computePositionedLogicalHeightUsing(Length height, const RenderBoxModelObject* cb,
    +                                    int containerHeight, int bordersPlusPadding,
    +                                    Length top, Length bottom, Length marginTop, Length marginBottom,
    +                                    int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos);
    +
    +    void computePositionedLogicalHeightReplaced();
    +    void computePositionedLogicalWidthReplaced();
    +
    +    // This function calculates the minimum and maximum preferred widths for an object.
    +    // These values are used in shrink-to-fit layout systems.
    +    // These include tables, positioned objects, floats and flexible boxes.
    +    virtual void computePreferredLogicalWidths() { setPreferredLogicalWidthsDirty(false); }
    +
    +private:
    +    // The width/height of the contents + borders + padding.  The x/y location is relative to our container (which is not always our parent).
    +    IntRect m_frameRect;
    +
    +protected:
    +
    +#ifdef ANDROID_LAYOUT
    +    void setVisibleWidth(int newWidth);
    +    bool checkAndSetRelayoutChildren(bool* relayoutChildren);
    +#endif
    +
    +    int m_marginLeft;
    +    int m_marginRight;
    +    int m_marginTop;
    +    int m_marginBottom;
    +
    +    // The preferred logical width of the element if it were to break its lines at every possible opportunity.
    +    int m_minPreferredLogicalWidth;
    +    
    +    // The preferred logical width of the element if it never breaks any lines at all.
    +    int m_maxPreferredLogicalWidth;
    +
    +    // For inline replaced elements, the inline box that owns us.
    +    InlineBox* m_inlineBoxWrapper;
    +
    +    // Our overflow information.
    +    OwnPtr m_overflow;
    +
    +private:
    +    // Used to store state between styleWillChange and styleDidChange
    +    static bool s_hadOverflowClip;
    +
    +#ifdef ANDROID_LAYOUT
    +    int m_visibleWidth;
    +    bool m_isVisibleWidthChangedBeforeLayout;
    +#endif
    +};
    +
    +inline RenderBox* toRenderBox(RenderObject* object)
    +{ 
    +    ASSERT(!object || object->isBox());
    +    return static_cast(object);
    +}
    +
    +inline const RenderBox* toRenderBox(const RenderObject* object)
    +{ 
    +    ASSERT(!object || object->isBox());
    +    return static_cast(object);
    +}
    +
    +// This will catch anyone doing an unnecessary cast.
    +void toRenderBox(const RenderBox*);
    +
    +inline RenderBox* RenderBox::previousSiblingBox() const
    +{
    +    return toRenderBox(previousSibling());
    +}
    +
    +inline RenderBox* RenderBox::nextSiblingBox() const
    +{ 
    +    return toRenderBox(nextSibling());
    +}
    +
    +inline RenderBox* RenderBox::parentBox() const
    +{
    +    return toRenderBox(parent());
    +}
    +
    +inline RenderBox* RenderBox::firstChildBox() const
    +{
    +    return toRenderBox(firstChild());
    +}
    +
    +inline RenderBox* RenderBox::lastChildBox() const
    +{
    +    return toRenderBox(lastChild());
    +}
    +
    +} // namespace WebCore
    +
    +#endif // RenderBox_h
    diff --git a/Source/WebCore/rendering/RenderBoxModelObject.cpp b/Source/WebCore/rendering/RenderBoxModelObject.cpp
    new file mode 100644
    index 0000000..5098306
    --- /dev/null
    +++ b/Source/WebCore/rendering/RenderBoxModelObject.cpp
    @@ -0,0 +1,1858 @@
    +/*
    + * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
    + *           (C) 1999 Antti Koivisto (koivisto@kde.org)
    + *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
    + *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
    + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
    + * Copyright (C) 2010 Google Inc. All rights reserved.
    + *
    + * This library is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU Library General Public
    + * License as published by the Free Software Foundation; either
    + * version 2 of the License, or (at your option) any later version.
    + *
    + * This library is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    + * Library General Public License for more details.
    + *
    + * You should have received a copy of the GNU Library General Public License
    + * along with this library; see the file COPYING.LIB.  If not, write to
    + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    + * Boston, MA 02110-1301, USA.
    + *
    + */
    +
    +#include "config.h"
    +#include "RenderBoxModelObject.h"
    +
    +#include "GraphicsContext.h"
    +#include "HTMLFrameOwnerElement.h"
    +#include "HTMLNames.h"
    +#include "ImageBuffer.h"
    +#include "Path.h"
    +#include "RenderBlock.h"
    +#include "RenderInline.h"
    +#include "RenderLayer.h"
    +#include "RenderView.h"
    +#include 
    +
    +using namespace std;
    +
    +namespace WebCore {
    +
    +using namespace HTMLNames;
    +
    +bool RenderBoxModelObject::s_wasFloating = false;
    +bool RenderBoxModelObject::s_hadLayer = false;
    +bool RenderBoxModelObject::s_layerWasSelfPainting = false;
    +
    +static const double cInterpolationCutoff = 800. * 800.;
    +static const double cLowQualityTimeThreshold = 0.500; // 500 ms
    +
    +typedef pair LastPaintSizeMapKey;
    +typedef HashMap LastPaintSizeMap;
    +
    +// The HashMap for storing continuation pointers.
    +// An inline can be split with blocks occuring in between the inline content.
    +// When this occurs we need a pointer to the next object. We can basically be
    +// split into a sequence of inlines and blocks. The continuation will either be
    +// an anonymous block (that houses other blocks) or it will be an inline flow.
    +// 

    Hello

    . In this example the will have a block as +// its continuation but the will just have an inline as its continuation. +typedef HashMap ContinuationMap; +static ContinuationMap* continuationMap = 0; + +class ImageQualityController : public Noncopyable { +public: + ImageQualityController(); + bool shouldPaintAtLowQuality(GraphicsContext*, RenderBoxModelObject*, Image*, const void* layer, const IntSize&); + void keyDestroyed(LastPaintSizeMapKey key); + void objectDestroyed(RenderBoxModelObject*); + +private: + void highQualityRepaintTimerFired(Timer*); + void restartTimer(); + + LastPaintSizeMap m_lastPaintSizeMap; + Timer m_timer; + bool m_animatedResizeIsActive; +}; + +ImageQualityController::ImageQualityController() + : m_timer(this, &ImageQualityController::highQualityRepaintTimerFired) + , m_animatedResizeIsActive(false) +{ +} + +void ImageQualityController::keyDestroyed(LastPaintSizeMapKey key) +{ + m_lastPaintSizeMap.remove(key); + if (m_lastPaintSizeMap.isEmpty()) { + m_animatedResizeIsActive = false; + m_timer.stop(); + } +} + +void ImageQualityController::objectDestroyed(RenderBoxModelObject* object) +{ + Vector keysToDie; + for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it) + if (it->first.first == object) + keysToDie.append(it->first); + for (Vector::iterator it = keysToDie.begin(); it != keysToDie.end(); ++it) + keyDestroyed(*it); +} + +void ImageQualityController::highQualityRepaintTimerFired(Timer*) +{ + if (m_animatedResizeIsActive) { + m_animatedResizeIsActive = false; + for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it) + it->first.first->repaint(); + } +} + +void ImageQualityController::restartTimer() +{ + m_timer.startOneShot(cLowQualityTimeThreshold); +} + +bool ImageQualityController::shouldPaintAtLowQuality(GraphicsContext* context, RenderBoxModelObject* object, Image* image, const void *layer, const IntSize& size) +{ + // If the image is not a bitmap image, then none of this is relevant and we just paint at high + // quality. + if (!image || !image->isBitmapImage() || context->paintingDisabled()) + return false; + + // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image + // is actually being scaled. + IntSize imageSize(image->width(), image->height()); + + // Look ourselves up in the hashtable. + LastPaintSizeMapKey key(object, layer); + LastPaintSizeMap::iterator i = m_lastPaintSizeMap.find(key); + + const AffineTransform& currentTransform = context->getCTM(); + bool contextIsScaled = !currentTransform.isIdentityOrTranslationOrFlipped(); + if (!contextIsScaled && imageSize == size) { + // There is no scale in effect. If we had a scale in effect before, we can just remove this object from the list. + if (i != m_lastPaintSizeMap.end()) + m_lastPaintSizeMap.remove(key); + + return false; + } + + // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. + if (object->document()->page()->inLowQualityImageInterpolationMode()) { + double totalPixels = static_cast(image->width()) * static_cast(image->height()); + if (totalPixels > cInterpolationCutoff) + return true; + } + // If an animated resize is active, paint in low quality and kick the timer ahead. + if (m_animatedResizeIsActive) { + m_lastPaintSizeMap.set(key, size); + restartTimer(); + return true; + } + // If this is the first time resizing this image, or its size is the + // same as the last resize, draw at high res, but record the paint + // size and set the timer. + if (i == m_lastPaintSizeMap.end() || size == i->second) { + restartTimer(); + m_lastPaintSizeMap.set(key, size); + return false; + } + // If the timer is no longer active, draw at high quality and don't + // set the timer. + if (!m_timer.isActive()) { + keyDestroyed(key); + return false; + } + // This object has been resized to two different sizes while the timer + // is active, so draw at low quality, set the flag for animated resizes and + // the object to the list for high quality redraw. + m_lastPaintSizeMap.set(key, size); + m_animatedResizeIsActive = true; + restartTimer(); + return true; +} + +static ImageQualityController* imageQualityController() +{ + static ImageQualityController* controller = new ImageQualityController; + return controller; +} + +void RenderBoxModelObject::setSelectionState(SelectionState s) +{ + if (selectionState() == s) + return; + + if (s == SelectionInside && selectionState() != SelectionNone) + return; + + if ((s == SelectionStart && selectionState() == SelectionEnd) + || (s == SelectionEnd && selectionState() == SelectionStart)) + RenderObject::setSelectionState(SelectionBoth); + else + RenderObject::setSelectionState(s); + + // FIXME: + // We should consider whether it is OK propagating to ancestor RenderInlines. + // This is a workaround for http://webkit.org/b/32123 + RenderBlock* cb = containingBlock(); + if (cb && !cb->isRenderView()) + cb->setSelectionState(s); +} + +bool RenderBoxModelObject::shouldPaintAtLowQuality(GraphicsContext* context, Image* image, const void* layer, const IntSize& size) +{ + return imageQualityController()->shouldPaintAtLowQuality(context, this, image, layer, size); +} + +RenderBoxModelObject::RenderBoxModelObject(Node* node) + : RenderObject(node) + , m_layer(0) +{ +} + +RenderBoxModelObject::~RenderBoxModelObject() +{ + // Our layer should have been destroyed and cleared by now + ASSERT(!hasLayer()); + ASSERT(!m_layer); + imageQualityController()->objectDestroyed(this); +} + +void RenderBoxModelObject::destroyLayer() +{ + ASSERT(!hasLayer()); // Callers should have already called setHasLayer(false) + ASSERT(m_layer); + m_layer->destroy(renderArena()); + m_layer = 0; +} + +void RenderBoxModelObject::destroy() +{ + // This must be done before we destroy the RenderObject. + if (m_layer) + m_layer->clearClipRects(); + + // A continuation of this RenderObject should be destroyed at subclasses. + ASSERT(!continuation()); + + // RenderObject::destroy calls back to destroyLayer() for layer destruction + RenderObject::destroy(); +} + +bool RenderBoxModelObject::hasSelfPaintingLayer() const +{ + return m_layer && m_layer->isSelfPaintingLayer(); +} + +void RenderBoxModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +{ + s_wasFloating = isFloating(); + s_hadLayer = hasLayer(); + if (s_hadLayer) + s_layerWasSelfPainting = layer()->isSelfPaintingLayer(); + + // If our z-index changes value or our visibility changes, + // we need to dirty our stacking context's z-order list. + if (style() && newStyle) { + if (parent()) { + // Do a repaint with the old style first, e.g., for example if we go from + // having an outline to not having an outline. + if (diff == StyleDifferenceRepaintLayer) { + layer()->repaintIncludingDescendants(); + if (!(style()->clip() == newStyle->clip())) + layer()->clearClipRectsIncludingDescendants(); + } else if (diff == StyleDifferenceRepaint || newStyle->outlineSize() < style()->outlineSize()) + repaint(); + } + + if (diff == StyleDifferenceLayout) { + // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could + // end up being destroyed. + if (hasLayer()) { + if (style()->position() != newStyle->position() || + style()->zIndex() != newStyle->zIndex() || + style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || + !(style()->clip() == newStyle->clip()) || + style()->hasClip() != newStyle->hasClip() || + style()->opacity() != newStyle->opacity() || + style()->transform() != newStyle->transform()) + layer()->repaintIncludingDescendants(); + } else if (newStyle->hasTransform() || newStyle->opacity() < 1) { + // If we don't have a layer yet, but we are going to get one because of transform or opacity, + // then we need to repaint the old position of the object. + repaint(); + } + } + + if (hasLayer() && (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || + style()->zIndex() != newStyle->zIndex() || + style()->visibility() != newStyle->visibility())) { + layer()->dirtyStackingContextZOrderLists(); + if (style()->hasAutoZIndex() != newStyle->hasAutoZIndex() || style()->visibility() != newStyle->visibility()) + layer()->dirtyZOrderLists(); + } + } + + RenderObject::styleWillChange(diff, newStyle); +} + +void RenderBoxModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderObject::styleDidChange(diff, oldStyle); + updateBoxModelInfoFromStyle(); + + if (requiresLayer()) { + if (!layer()) { + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + m_layer = new (renderArena()) RenderLayer(this); + setHasLayer(true); + m_layer->insertOnlyThisLayer(); + if (parent() && !needsLayout() && containingBlock()) + m_layer->updateLayerPositions(); + } + } else if (layer() && layer()->parent()) { + setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. + setHasReflection(false); + m_layer->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + } + + if (layer()) { + layer()->styleChanged(diff, oldStyle); + if (s_hadLayer && layer()->isSelfPaintingLayer() != s_layerWasSelfPainting) + setChildNeedsLayout(true); + } +} + +void RenderBoxModelObject::updateBoxModelInfoFromStyle() +{ + // Set the appropriate bits for a box model object. Since all bits are cleared in styleWillChange, + // we only check for bits that could possibly be set to true. + setHasBoxDecorations(hasBackground() || style()->hasBorder() || style()->hasAppearance() || style()->boxShadow()); + setInline(style()->isDisplayInlineType()); + setRelPositioned(style()->position() == RelativePosition); +} + +int RenderBoxModelObject::relativePositionOffsetX() const +{ + // Objects that shrink to avoid floats normally use available line width when computing containing block width. However + // in the case of relative positioning using percentages, we can't do this. The offset should always be resolved using the + // available width of the containing block. Therefore we don't use containingBlockLogicalWidthForContent() here, but instead explicitly + // call availableWidth on our containing block. + if (!style()->left().isAuto()) { + RenderBlock* cb = containingBlock(); + if (!style()->right().isAuto() && !containingBlock()->style()->isLeftToRightDirection()) + return -style()->right().calcValue(cb->availableWidth()); + return style()->left().calcValue(cb->availableWidth()); + } + if (!style()->right().isAuto()) { + RenderBlock* cb = containingBlock(); + return -style()->right().calcValue(cb->availableWidth()); + } + return 0; +} + +int RenderBoxModelObject::relativePositionOffsetY() const +{ + RenderBlock* containingBlock = this->containingBlock(); + + // If the containing block of a relatively positioned element does not + // specify a height, a percentage top or bottom offset should be resolved as + // auto. An exception to this is if the containing block has the WinIE quirk + // where and assume the size of the viewport. In this case, + // calculate the percent offset based on this height. + // See . + if (!style()->top().isAuto() + && (!containingBlock->style()->height().isAuto() + || !style()->top().isPercent() + || containingBlock->stretchesToViewport())) + return style()->top().calcValue(containingBlock->availableHeight()); + + if (!style()->bottom().isAuto() + && (!containingBlock->style()->height().isAuto() + || !style()->bottom().isPercent() + || containingBlock->stretchesToViewport())) + return -style()->bottom().calcValue(containingBlock->availableHeight()); + + return 0; +} + +int RenderBoxModelObject::offsetLeft() const +{ + // If the element is the HTML body element or does not have an associated box + // return 0 and stop this algorithm. + if (isBody()) + return 0; + + RenderBoxModelObject* offsetPar = offsetParent(); + int xPos = (isBox() ? toRenderBox(this)->x() : 0); + + // If the offsetParent of the element is null, or is the HTML body element, + // return the distance between the canvas origin and the left border edge + // of the element and stop this algorithm. + if (offsetPar) { + if (offsetPar->isBox() && !offsetPar->isBody()) + xPos -= toRenderBox(offsetPar)->borderLeft(); + if (!isPositioned()) { + if (isRelPositioned()) + xPos += relativePositionOffsetX(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + // FIXME: What are we supposed to do inside SVG content? + if (curr->isBox() && !curr->isTableRow()) + xPos += toRenderBox(curr)->x(); + curr = curr->parent(); + } + if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + xPos += toRenderBox(offsetPar)->x(); + } + } + + return xPos; +} + +int RenderBoxModelObject::offsetTop() const +{ + // If the element is the HTML body element or does not have an associated box + // return 0 and stop this algorithm. + if (isBody()) + return 0; + + RenderBoxModelObject* offsetPar = offsetParent(); + int yPos = (isBox() ? toRenderBox(this)->y() : 0); + + // If the offsetParent of the element is null, or is the HTML body element, + // return the distance between the canvas origin and the top border edge + // of the element and stop this algorithm. + if (offsetPar) { + if (offsetPar->isBox() && !offsetPar->isBody()) + yPos -= toRenderBox(offsetPar)->borderTop(); + if (!isPositioned()) { + if (isRelPositioned()) + yPos += relativePositionOffsetY(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + // FIXME: What are we supposed to do inside SVG content? + if (curr->isBox() && !curr->isTableRow()) + yPos += toRenderBox(curr)->y(); + curr = curr->parent(); + } + if (offsetPar->isBox() && offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + yPos += toRenderBox(offsetPar)->y(); + } + } + return yPos; +} + +int RenderBoxModelObject::paddingTop(bool) const +{ + int w = 0; + Length padding = style()->paddingTop(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingBottom(bool) const +{ + int w = 0; + Length padding = style()->paddingBottom(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingLeft(bool) const +{ + int w = 0; + Length padding = style()->paddingLeft(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingRight(bool) const +{ + int w = 0; + Length padding = style()->paddingRight(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingBefore(bool) const +{ + int w = 0; + Length padding = style()->paddingBefore(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingAfter(bool) const +{ + int w = 0; + Length padding = style()->paddingAfter(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingStart(bool) const +{ + int w = 0; + Length padding = style()->paddingStart(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +int RenderBoxModelObject::paddingEnd(bool) const +{ + int w = 0; + Length padding = style()->paddingEnd(); + if (padding.isPercent()) + w = containingBlock()->availableLogicalWidth(); + return padding.calcMinValue(w); +} + +void RenderBoxModelObject::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op, RenderObject* backgroundObject) +{ + GraphicsContext* context = paintInfo.context; + if (context->paintingDisabled()) + return; + + bool includeLeftEdge = box ? box->includeLogicalLeftEdge() : true; + bool includeRightEdge = box ? box->includeLogicalRightEdge() : true; + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; + int pLeft = includeLeftEdge ? paddingLeft() : 0; + int pRight = includeRightEdge ? paddingRight() : 0; + + bool clippedToBorderRadius = false; + if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { + IntRect borderRect(tx, ty, w, h); + + if (borderRect.isEmpty()) + return; + + context->save(); + + IntSize topLeft, topRight, bottomLeft, bottomRight; + style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); + + if (!includeLeftEdge) { + topLeft = IntSize(); + if (box->isHorizontal()) + bottomLeft = IntSize(); + else + topRight = IntSize(); + } + + if (!includeRightEdge) { + if (box->isHorizontal()) + topRight = IntSize(); + else + bottomLeft = IntSize(); + bottomRight = IntSize(); + } + + context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); + clippedToBorderRadius = true; + } + + bool clippedWithLocalScrolling = hasOverflowClip() && bgLayer->attachment() == LocalBackgroundAttachment; + if (clippedWithLocalScrolling) { + // Clip to the overflow area. + context->save(); + context->clip(toRenderBox(this)->overflowClipRect(tx, ty)); + + // Now adjust our tx, ty, w, h to reflect a scrolled content box with borders at the ends. + IntSize offset = layer()->scrolledContentOffset(); + tx -= offset.width(); + ty -= offset.height(); + w = bLeft + layer()->scrollWidth() + bRight; + h = borderTop() + layer()->scrollHeight() + borderBottom(); + } + + if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) { + // Clip to the padding or content boxes as necessary. + bool includePadding = bgLayer->clip() == ContentFillBox; + int x = tx + bLeft + (includePadding ? pLeft : 0); + int y = ty + borderTop() + (includePadding ? paddingTop() : 0); + int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); + int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); + context->save(); + context->clip(IntRect(x, y, width, height)); + } else if (bgLayer->clip() == TextFillBox) { + // We have to draw our text into a mask that can then be used to clip background drawing. + // First figure out how big the mask has to be. It should be no bigger than what we need + // to actually render, so we should intersect the dirty rect with the border box of the background. + IntRect maskRect(tx, ty, w, h); + maskRect.intersect(paintInfo.rect); + + // Now create the mask. + OwnPtr maskImage = ImageBuffer::create(maskRect.size()); + if (!maskImage) + return; + + GraphicsContext* maskImageContext = maskImage->context(); + maskImageContext->translate(-maskRect.x(), -maskRect.y()); + + // Now add the text to the clip. We do this by painting using a special paint phase that signals to + // InlineTextBoxes that they should just add their contents to the clip. + PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); + if (box) + box->paint(info, tx - box->x(), ty - box->y()); + else { + int x = isBox() ? toRenderBox(this)->x() : 0; + int y = isBox() ? toRenderBox(this)->y() : 0; + paint(info, tx - x, ty - y); + } + + // The mask has been created. Now we just need to clip to it. + context->save(); + context->clipToImageBuffer(maskImage.get(), maskRect); + } + + StyleImage* bg = bgLayer->image(); + bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); + Color bgColor = c; + + // When this style flag is set, change existing background colors and images to a solid white background. + // If there's no bg color or image, leave it untouched to avoid affecting transparency. + // We don't try to avoid loading the background images, because this style flag is only set + // when printing, and at that point we've already loaded the background images anyway. (To avoid + // loading the background images we'd have to do this check when applying styles rather than + // while rendering.) + if (style()->forceBackgroundsToWhite()) { + // Note that we can't reuse this variable below because the bgColor might be changed + bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; + if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { + bgColor = Color::white; + shouldPaintBackgroundImage = false; + } + } + + bool isRoot = this->isRoot(); + + // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with + // no background in the child document should show the parent's background. + bool isOpaqueRoot = false; + if (isRoot) { + isOpaqueRoot = true; + if (!bgLayer->next() && !(bgColor.isValid() && bgColor.alpha() == 255) && view()->frameView()) { + Element* ownerElement = document()->ownerElement(); + if (ownerElement) { + if (!ownerElement->hasTagName(frameTag)) { + // Locate the element using the DOM. This is easier than trying + // to crawl around a render tree with potential :before/:after content and + // anonymous blocks created by inline tags etc. We can locate the + // render object very easily via the DOM. + HTMLElement* body = document()->body(); + if (body) { + // Can't scroll a frameset document anyway. + isOpaqueRoot = body->hasLocalName(framesetTag); + } +#if ENABLE(SVG) + else { + // SVG documents and XML documents with SVG root nodes are transparent. + isOpaqueRoot = !document()->hasSVGRootNode(); + } +#endif + } + } else + isOpaqueRoot = !view()->frameView()->isTransparent(); + } + view()->frameView()->setContentIsOpaque(isOpaqueRoot); + } + + // Paint the color first underneath all images. + if (!bgLayer->next()) { + IntRect rect(tx, ty, w, h); + rect.intersect(paintInfo.rect); + // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. + if (isOpaqueRoot) { + Color baseColor = view()->frameView()->baseBackgroundColor(); + if (baseColor.alpha() > 0) { + CompositeOperator previousOperator = context->compositeOperation(); + context->setCompositeOperation(CompositeCopy); + context->fillRect(rect, baseColor, style()->colorSpace()); + context->setCompositeOperation(previousOperator); + } else + context->clearRect(rect); + } + + if (bgColor.isValid() && bgColor.alpha() > 0) + context->fillRect(rect, bgColor, style()->colorSpace()); + } + + // no progressive loading of the background image + if (shouldPaintBackgroundImage) { + IntRect destRect; + IntPoint phase; + IntSize tileSize; + + calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize); + IntPoint destOrigin = destRect.location(); + destRect.intersect(paintInfo.rect); + if (!destRect.isEmpty()) { + phase += destRect.location() - destOrigin; + CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; + RenderObject* clientForBackgroundImage = backgroundObject ? backgroundObject : this; + Image* image = bg->image(clientForBackgroundImage, tileSize); + bool useLowQualityScaling = shouldPaintAtLowQuality(context, image, bgLayer, tileSize); + context->drawTiledImage(image, style()->colorSpace(), destRect, phase, tileSize, compositeOp, useLowQualityScaling); + } + } + + if (bgLayer->clip() != BorderFillBox) + // Undo the background clip + context->restore(); + + if (clippedToBorderRadius) + // Undo the border radius clip + context->restore(); + + if (clippedWithLocalScrolling) // Undo the clip for local background attachments. + context->restore(); +} + +IntSize RenderBoxModelObject::calculateFillTileSize(const FillLayer* fillLayer, IntSize positioningAreaSize) const +{ + StyleImage* image = fillLayer->image(); + image->setImageContainerSize(positioningAreaSize); // Use the box established by background-origin. + + EFillSizeType type = fillLayer->size().type; + + switch (type) { + case SizeLength: { + int w = positioningAreaSize.width(); + int h = positioningAreaSize.height(); + + Length layerWidth = fillLayer->size().size.width(); + Length layerHeight = fillLayer->size().size.height(); + + if (layerWidth.isFixed()) + w = layerWidth.value(); + else if (layerWidth.isPercent()) + w = layerWidth.calcValue(positioningAreaSize.width()); + + if (layerHeight.isFixed()) + h = layerHeight.value(); + else if (layerHeight.isPercent()) + h = layerHeight.calcValue(positioningAreaSize.height()); + + // If one of the values is auto we have to use the appropriate + // scale to maintain our aspect ratio. + if (layerWidth.isAuto() && !layerHeight.isAuto()) { + IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); + if (imageIntrinsicSize.height()) + w = imageIntrinsicSize.width() * h / imageIntrinsicSize.height(); + } else if (!layerWidth.isAuto() && layerHeight.isAuto()) { + IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); + if (imageIntrinsicSize.width()) + h = imageIntrinsicSize.height() * w / imageIntrinsicSize.width(); + } else if (layerWidth.isAuto() && layerHeight.isAuto()) { + // If both width and height are auto, use the image's intrinsic size. + IntSize imageIntrinsicSize = image->imageSize(this, style()->effectiveZoom()); + w = imageIntrinsicSize.width(); + h = imageIntrinsicSize.height(); + } + + return IntSize(max(1, w), max(1, h)); + } + case Contain: + case Cover: { + IntSize imageIntrinsicSize = image->imageSize(this, 1); + float horizontalScaleFactor = imageIntrinsicSize.width() + ? static_cast(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1; + float verticalScaleFactor = imageIntrinsicSize.height() + ? static_cast(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1; + float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor); + return IntSize(max(1, imageIntrinsicSize.width() * scaleFactor), max(1, imageIntrinsicSize.height() * scaleFactor)); + } + case SizeNone: + break; + } + + return image->imageSize(this, style()->effectiveZoom()); +} + +void RenderBoxModelObject::calculateBackgroundImageGeometry(const FillLayer* fillLayer, int tx, int ty, int w, int h, + IntRect& destRect, IntPoint& phase, IntSize& tileSize) +{ + int left = 0; + int top = 0; + IntSize positioningAreaSize; + + // Determine the background positioning area and set destRect to the background painting area. + // destRect will be adjusted later if the background is non-repeating. + bool fixedAttachment = fillLayer->attachment() == FixedBackgroundAttachment; + +#if ENABLE(FAST_MOBILE_SCROLLING) + if (view()->frameView() && view()->frameView()->canBlitOnScroll()) { + // As a side effect of an optimization to blit on scroll, we do not honor the CSS + // property "background-attachment: fixed" because it may result in rendering + // artifacts. Note, these artifacts only appear if we are blitting on scroll of + // a page that has fixed background images. + fixedAttachment = false; + } +#endif + + if (!fixedAttachment) { + destRect = IntRect(tx, ty, w, h); + + int right = 0; + int bottom = 0; + // Scroll and Local. + if (fillLayer->origin() != BorderFillBox) { + left = borderLeft(); + right = borderRight(); + top = borderTop(); + bottom = borderBottom(); + if (fillLayer->origin() == ContentFillBox) { + left += paddingLeft(); + right += paddingRight(); + top += paddingTop(); + bottom += paddingBottom(); + } + } + + // The background of the box generated by the root element covers the entire canvas including + // its margins. Since those were added in already, we have to factor them out when computing + // the background positioning area. + if (isRoot()) { + positioningAreaSize = IntSize(toRenderBox(this)->width() - left - right, toRenderBox(this)->height() - top - bottom); + left += marginLeft(); + top += marginTop(); + } else + positioningAreaSize = IntSize(w - left - right, h - top - bottom); + } else { + destRect = viewRect(); + positioningAreaSize = destRect.size(); + } + + tileSize = calculateFillTileSize(fillLayer, positioningAreaSize); + + EFillRepeat backgroundRepeatX = fillLayer->repeatX(); + EFillRepeat backgroundRepeatY = fillLayer->repeatY(); + + int xPosition = fillLayer->xPosition().calcMinValue(positioningAreaSize.width() - tileSize.width(), true); + if (backgroundRepeatX == RepeatFill) + phase.setX(tileSize.width() ? tileSize.width() - (xPosition + left) % tileSize.width() : 0); + else { + destRect.move(max(xPosition + left, 0), 0); + phase.setX(-min(xPosition + left, 0)); + destRect.setWidth(tileSize.width() + min(xPosition + left, 0)); + } + + int yPosition = fillLayer->yPosition().calcMinValue(positioningAreaSize.height() - tileSize.height(), true); + if (backgroundRepeatY == RepeatFill) + phase.setY(tileSize.height() ? tileSize.height() - (yPosition + top) % tileSize.height() : 0); + else { + destRect.move(0, max(yPosition + top, 0)); + phase.setY(-min(yPosition + top, 0)); + destRect.setHeight(tileSize.height() + min(yPosition + top, 0)); + } + + if (fixedAttachment) + phase.move(max(tx - destRect.x(), 0), max(ty - destRect.y(), 0)); + + destRect.intersect(IntRect(tx, ty, w, h)); +} + +bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, + const NinePieceImage& ninePieceImage, CompositeOperator op) +{ + StyleImage* styleImage = ninePieceImage.image(); + if (!styleImage) + return false; + + if (!styleImage->isLoaded()) + return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. + + if (!styleImage->canRender(style->effectiveZoom())) + return false; + + // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function + // doesn't have any understanding of the zoom that is in effect on the tile. + styleImage->setImageContainerSize(IntSize(w, h)); + IntSize imageSize = styleImage->imageSize(this, 1.0f); + int imageWidth = imageSize.width(); + int imageHeight = imageSize.height(); + + int topSlice = min(imageHeight, ninePieceImage.slices().top().calcValue(imageHeight)); + int bottomSlice = min(imageHeight, ninePieceImage.slices().bottom().calcValue(imageHeight)); + int leftSlice = min(imageWidth, ninePieceImage.slices().left().calcValue(imageWidth)); + int rightSlice = min(imageWidth, ninePieceImage.slices().right().calcValue(imageWidth)); + + ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); + ENinePieceImageRule vRule = ninePieceImage.verticalRule(); + + bool fitToBorder = style->borderImage() == ninePieceImage; + + int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; + int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; + int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; + int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; + + bool drawLeft = leftSlice > 0 && leftWidth > 0; + bool drawTop = topSlice > 0 && topWidth > 0; + bool drawRight = rightSlice > 0 && rightWidth > 0; + bool drawBottom = bottomSlice > 0 && bottomWidth > 0; + bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && + (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; + + Image* image = styleImage->image(this, imageSize); + ColorSpace colorSpace = style->colorSpace(); + + if (drawLeft) { + // Paint the top and bottom left corners. + + // The top left corner rect is (tx, ty, leftWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty, leftWidth, topWidth), + IntRect(0, 0, leftSlice, topSlice), op); + + // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) + // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) + if (drawBottom) + graphicsContext->drawImage(image, colorSpace, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), + IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); + + // Paint the left edge. + // Have to scale and tile into the border rect. + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx, ty + topWidth, leftWidth, + h - topWidth - bottomWidth), + IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + if (drawRight) { + // Paint the top and bottom right corners + // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), + IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); + + // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) + // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) + if (drawBottom) + graphicsContext->drawImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), + IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); + + // Paint the right edge. + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, + h - topWidth - bottomWidth), + IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + // Paint the top edge. + if (drawTop) + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), + IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the bottom edge. + if (drawBottom) + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + h - bottomWidth, + w - leftWidth - rightWidth, bottomWidth), + IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the middle. + if (drawMiddle) + graphicsContext->drawTiledImage(image, colorSpace, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, + h - topWidth - bottomWidth), + IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), + (Image::TileRule)hRule, (Image::TileRule)vRule, op); + + return true; +} + +#if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING) +static bool borderWillArcInnerEdge(const IntSize& firstRadius, const IntSize& secondRadius, int firstBorderWidth, int secondBorderWidth, int middleBorderWidth) +{ + // FIXME: This test is insufficient. We need to take border style into account. + return (!firstRadius.width() || firstRadius.width() >= firstBorderWidth) + && (!firstRadius.height() || firstRadius.height() >= middleBorderWidth) + && (!secondRadius.width() || secondRadius.width() >= secondBorderWidth) + && (!secondRadius.height() || secondRadius.height() >= middleBorderWidth); +} + +void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) + return; + + if (graphicsContext->paintingDisabled()) + return; + + const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor); + const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor); + const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor); + const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor); + + bool topTransparent = style->borderTopIsTransparent(); + bool bottomTransparent = style->borderBottomIsTransparent(); + bool rightTransparent = style->borderRightIsTransparent(); + bool leftTransparent = style->borderLeftIsTransparent(); + + EBorderStyle topStyle = style->borderTopStyle(); + EBorderStyle bottomStyle = style->borderBottomStyle(); + EBorderStyle leftStyle = style->borderLeftStyle(); + EBorderStyle rightStyle = style->borderRightStyle(); + + bool horizontal = style->isHorizontalWritingMode(); + + bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge); + bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge); + bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge); + bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge); + + bool renderRadii = false; + Path roundedPath; + IntSize topLeft, topRight, bottomLeft, bottomRight; + IntRect borderRect(tx, ty, w, h); + + if (style->hasBorderRadius()) { + IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; + style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; + int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; + int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; + int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; + + IntRect innerBorderRect = borderInnerRect(borderRect, topWidth, bottomWidth, leftWidth, rightWidth); + IntSize innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius; + + style->getInnerBorderRadiiForRectWithBorderWidths(innerBorderRect, topWidth, bottomWidth, leftWidth, rightWidth, innerTopLeftRadius, innerTopRightRadius, innerBottomLeftRadius, innerBottomRightRadius); + + IntSize innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight; + + if (includeLogicalLeftEdge) { + topLeft = topLeftRadius; + innerTopLeft = innerTopLeftRadius; + if (horizontal) { + bottomLeft = bottomLeftRadius; + innerBottomLeft = innerBottomLeftRadius; + } else { + topRight = topRightRadius; + innerTopRight = innerTopRightRadius; + } + } + + if (includeLogicalRightEdge) { + if (horizontal) { + topRight = topRightRadius; + innerTopRight = innerTopRightRadius; + } else { + bottomLeft = bottomLeftRadius; + innerBottomLeft = innerBottomLeftRadius; + } + bottomRight = bottomRightRadius; + innerBottomRight = innerBottomRightRadius; + } + + renderRadii = !topLeft.isZero() || !topRight.isZero() || !bottomLeft.isZero() || !bottomRight.isZero(); + + if (renderRadii) { + // Clip to the inner and outer radii rects. + graphicsContext->save(); + graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); + graphicsContext->clipOutRoundedRect(innerBorderRect, innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight); + roundedPath.addRoundedRect(borderRect, topLeft, topRight, bottomLeft, bottomRight); + } + } + + bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor); + bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE); + bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE); + bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor); + + if (renderTop) { + int x = tx; + int x2 = tx + w; + + if (renderRadii && borderWillArcInnerEdge(topLeft, topRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderTopWidth())) { + graphicsContext->save(); + clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSTop, upperLeftBorderStylesMatch, upperRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); + float thickness = max(max(style->borderTopWidth(), style->borderLeftWidth()), style->borderRightWidth()); + drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderTopWidth(), thickness, BSTop, style, topColor, topStyle); + graphicsContext->restore(); + } else { + bool ignoreLeft = (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET + && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); + bool ignoreRight = (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET + && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + + drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle, + ignoreLeft ? 0 : style->borderLeftWidth(), ignoreRight ? 0 : style->borderRightWidth()); + } + } + + if (renderBottom) { + int x = tx; + int x2 = tx + w; + + if (renderRadii && borderWillArcInnerEdge(bottomLeft, bottomRight, style->borderLeftWidth(), style->borderRightWidth(), style->borderBottomWidth())) { + graphicsContext->save(); + clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSBottom, lowerLeftBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); + float thickness = max(max(style->borderBottomWidth(), style->borderLeftWidth()), style->borderRightWidth()); + drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderBottomWidth(), thickness, BSBottom, style, bottomColor, bottomStyle); + graphicsContext->restore(); + } else { + bool ignoreLeft = (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET + && (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); + + bool ignoreRight = (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET + && (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + + drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, + bottomStyle, ignoreLeft ? 0 : style->borderLeftWidth(), + ignoreRight ? 0 : style->borderRightWidth()); + } + } + + if (renderLeft) { + int y = ty; + int y2 = ty + h; + + if (renderRadii && borderWillArcInnerEdge(bottomLeft, topLeft, style->borderBottomWidth(), style->borderTopWidth(), style->borderLeftWidth())) { + graphicsContext->save(); + clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSLeft, upperLeftBorderStylesMatch, lowerLeftBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); + float thickness = max(max(style->borderLeftWidth(), style->borderTopWidth()), style->borderBottomWidth()); + drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderLeftWidth(), thickness, BSLeft, style, leftColor, leftStyle); + graphicsContext->restore(); + } else { + bool ignoreTop = (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET + && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + + bool ignoreBottom = (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET + && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + + drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, + leftStyle, ignoreTop ? 0 : style->borderTopWidth(), ignoreBottom ? 0 : style->borderBottomWidth()); + } + } + + if (renderRight) { + if (renderRadii && borderWillArcInnerEdge(bottomRight, topRight, style->borderBottomWidth(), style->borderTopWidth(), style->borderRightWidth())) { + graphicsContext->save(); + clipBorderSidePolygon(graphicsContext, borderRect, topLeft, topRight, bottomLeft, bottomRight, BSRight, upperRightBorderStylesMatch, lowerRightBorderStylesMatch, style, includeLogicalLeftEdge, includeLogicalRightEdge); + float thickness = max(max(style->borderRightWidth(), style->borderTopWidth()), style->borderBottomWidth()); + drawBoxSideFromPath(graphicsContext, borderRect, roundedPath, style->borderRightWidth(), thickness, BSRight, style, rightColor, rightStyle); + graphicsContext->restore(); + } else { + bool ignoreTop = ((topColor == rightColor) && (topTransparent == rightTransparent) + && (rightStyle >= DOTTED || rightStyle == INSET) + && (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + + bool ignoreBottom = ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) + && (rightStyle >= DOTTED || rightStyle == INSET) + && (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + + int y = ty; + int y2 = ty + h; + + drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, + rightStyle, ignoreTop ? 0 : style->borderTopWidth(), + ignoreBottom ? 0 : style->borderBottomWidth()); + } + } + + if (renderRadii) + graphicsContext->restore(); +} +#else +void RenderBoxModelObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + // FIXME: This old version of paintBorder should be removed when all ports implement + // GraphicsContext::clipConvexPolygon()!! This should happen soon. + if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) + return; + + const Color& topColor = style->visitedDependentColor(CSSPropertyBorderTopColor); + const Color& bottomColor = style->visitedDependentColor(CSSPropertyBorderBottomColor); + const Color& leftColor = style->visitedDependentColor(CSSPropertyBorderLeftColor); + const Color& rightColor = style->visitedDependentColor(CSSPropertyBorderRightColor); + + bool topTransparent = style->borderTopIsTransparent(); + bool bottomTransparent = style->borderBottomIsTransparent(); + bool rightTransparent = style->borderRightIsTransparent(); + bool leftTransparent = style->borderLeftIsTransparent(); + + EBorderStyle topStyle = style->borderTopStyle(); + EBorderStyle bottomStyle = style->borderBottomStyle(); + EBorderStyle leftStyle = style->borderLeftStyle(); + EBorderStyle rightStyle = style->borderRightStyle(); + + bool horizontal = style->isHorizontalWritingMode(); + bool renderTop = topStyle > BHIDDEN && !topTransparent && (horizontal || includeLogicalLeftEdge); + bool renderLeft = leftStyle > BHIDDEN && !leftTransparent && (!horizontal || includeLogicalLeftEdge); + bool renderRight = rightStyle > BHIDDEN && !rightTransparent && (!horizontal || includeLogicalRightEdge); + bool renderBottom = bottomStyle > BHIDDEN && !bottomTransparent && (horizontal || includeLogicalRightEdge); + + bool renderRadii = false; + IntSize topLeft, topRight, bottomLeft, bottomRight; + + if (style->hasBorderRadius()) { + IntRect borderRect = IntRect(tx, ty, w, h); + + IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; + style->getBorderRadiiForRect(borderRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + if (includeLogicalLeftEdge) { + topLeft = topLeftRadius; + if (horizontal) + bottomLeft = bottomLeftRadius; + else + topRight = topRightRadius; + } + + if (includeLogicalRightEdge) { + if (horizontal) + topRight = topRightRadius; + else + bottomLeft = bottomLeftRadius; + bottomRight = bottomRightRadius; + } + + renderRadii = !topLeft.isZero() || !topRight.isZero() || !bottomLeft.isZero() || !bottomRight.isZero(); + + if (renderRadii) { + // Clip to the rounded rectangle. + graphicsContext->save(); + graphicsContext->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight); + } + } + + int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; + float thickness; + bool upperLeftBorderStylesMatch = renderLeft && (topStyle == leftStyle) && (topColor == leftColor); + bool upperRightBorderStylesMatch = renderRight && (topStyle == rightStyle) && (topColor == rightColor) && (topStyle != OUTSET) && (topStyle != RIDGE) && (topStyle != INSET) && (topStyle != GROOVE); + bool lowerLeftBorderStylesMatch = renderLeft && (bottomStyle == leftStyle) && (bottomColor == leftColor) && (bottomStyle != OUTSET) && (bottomStyle != RIDGE) && (bottomStyle != INSET) && (bottomStyle != GROOVE); + bool lowerRightBorderStylesMatch = renderRight && (bottomStyle == rightStyle) && (bottomColor == rightColor); + + if (renderTop) { + bool ignore_left = (renderRadii && topLeft.width() > 0) || + (topColor == leftColor && topTransparent == leftTransparent && topStyle >= OUTSET && + (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); + + bool ignore_right = (renderRadii && topRight.width() > 0) || + (topColor == rightColor && topTransparent == rightTransparent && topStyle >= OUTSET && + (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += topLeft.width(); + x2 -= topRight.width(); + } + + drawLineForBoxSide(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, topColor, topStyle, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + int leftY = ty; + + // We make the arc double thick and let the clip rect take care of clipping the extra off. + // We're doing this because it doesn't seem possible to match the curve of the clip exactly + // with the arc-drawing function. + thickness = style->borderTopWidth() * 2; + + if (topLeft.width()) { + int leftX = tx; + // The inner clip clips inside the arc. This is especially important for 1px borders. + bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (topStyle != DOUBLE || style->borderTopWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), + style->borderTopWidth()); + } + + firstAngleStart = 90; + firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; + + // Draw upper left arc + drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSTop, topColor, topStyle, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (topRight.width()) { + int rightX = tx + w - topRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (topStyle != DOUBLE || style->borderTopWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), + style->borderTopWidth()); + } + + if (upperRightBorderStylesMatch) { + secondAngleStart = 0; + secondAngleSpan = 90; + } else { + secondAngleStart = 45; + secondAngleSpan = 45; + } + + // Draw upper right arc + drawArcForBoxSide(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, + BSTop, topColor, topStyle, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderBottom) { + bool ignore_left = (renderRadii && bottomLeft.width() > 0) || + (bottomColor == leftColor && bottomTransparent == leftTransparent && bottomStyle >= OUTSET && + (leftStyle == DOTTED || leftStyle == DASHED || leftStyle == SOLID || leftStyle == OUTSET)); + + bool ignore_right = (renderRadii && bottomRight.width() > 0) || + (bottomColor == rightColor && bottomTransparent == rightTransparent && bottomStyle >= OUTSET && + (rightStyle == DOTTED || rightStyle == DASHED || rightStyle == SOLID || rightStyle == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += bottomLeft.width(); + x2 -= bottomRight.width(); + } + + drawLineForBoxSide(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bottomColor, bottomStyle, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + thickness = style->borderBottomWidth() * 2; + + if (bottomLeft.width()) { + int leftX = tx; + int leftY = ty + h - bottomLeft.height() * 2; + bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderBottomWidth()); + } + + if (lowerLeftBorderStylesMatch) { + firstAngleStart = 180; + firstAngleSpan = 90; + } else { + firstAngleStart = 225; + firstAngleSpan = 45; + } + + // Draw lower left arc + drawArcForBoxSide(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, + BSBottom, bottomColor, bottomStyle, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (bottomRight.width()) { + int rightY = ty + h - bottomRight.height() * 2; + int rightX = tx + w - bottomRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (bottomStyle != DOUBLE || style->borderBottomWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderBottomWidth()); + } + + secondAngleStart = 270; + secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; + + // Draw lower right arc + drawArcForBoxSide(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSBottom, bottomColor, bottomStyle, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderLeft) { + bool ignore_top = (renderRadii && topLeft.height() > 0) || + (topColor == leftColor && topTransparent == leftTransparent && leftStyle >= OUTSET && + (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || + (bottomColor == leftColor && bottomTransparent == leftTransparent && leftStyle >= OUTSET && + (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topLeft.height(); + y2 -= bottomLeft.height(); + } + + drawLineForBoxSide(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, leftColor, leftStyle, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { + int topX = tx; + thickness = style->borderLeftWidth() * 2; + + if (!upperLeftBorderStylesMatch && topLeft.width()) { + int topY = ty; + bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (leftStyle != DOUBLE || style->borderLeftWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), + style->borderLeftWidth()); + } + + firstAngleStart = 135; + firstAngleSpan = 45; + + // Draw top left arc + drawArcForBoxSide(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSLeft, leftColor, leftStyle, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { + int bottomY = ty + h - bottomLeft.height() * 2; + bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (leftStyle != DOUBLE || style->borderLeftWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderLeftWidth()); + } + + secondAngleStart = 180; + secondAngleSpan = 45; + + // Draw bottom left arc + drawArcForBoxSide(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, + BSLeft, leftColor, leftStyle, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRight) { + bool ignore_top = (renderRadii && topRight.height() > 0) || + ((topColor == rightColor) && (topTransparent == rightTransparent) && + (rightStyle >= DOTTED || rightStyle == INSET) && + (topStyle == DOTTED || topStyle == DASHED || topStyle == SOLID || topStyle == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || + ((bottomColor == rightColor) && (bottomTransparent == rightTransparent) && + (rightStyle >= DOTTED || rightStyle == INSET) && + (bottomStyle == DOTTED || bottomStyle == DASHED || bottomStyle == SOLID || bottomStyle == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topRight.height(); + y2 -= bottomRight.height(); + } + + drawLineForBoxSide(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rightColor, rightStyle, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { + thickness = style->borderRightWidth() * 2; + + if (!upperRightBorderStylesMatch && topRight.width()) { + int topX = tx + w - topRight.width() * 2; + int topY = ty; + bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (rightStyle != DOUBLE || style->borderRightWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), + style->borderRightWidth()); + } + + firstAngleStart = 0; + firstAngleSpan = 45; + + // Draw top right arc + drawArcForBoxSide(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, + BSRight, rightColor, rightStyle, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerRightBorderStylesMatch && bottomRight.width()) { + int bottomX = tx + w - bottomRight.width() * 2; + int bottomY = ty + h - bottomRight.height() * 2; + bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (rightStyle != DOUBLE || style->borderRightWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderRightWidth()); + } + + secondAngleStart = 315; + secondAngleSpan = 45; + + // Draw bottom right arc + drawArcForBoxSide(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSRight, rightColor, rightStyle, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRadii) + graphicsContext->restore(); +} +#endif + +void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContext, const IntRect& box, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, + const BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches, const RenderStyle* style, + bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + FloatPoint quad[4]; + int tx = box.x(); + int ty = box.y(); + int w = box.width(); + int h = box.height(); + + bool horizontal = style->isHorizontalWritingMode(); + int leftWidth = (!horizontal || includeLogicalLeftEdge) ? style->borderLeftWidth() : 0; + int rightWidth = (!horizontal || includeLogicalRightEdge) ? style->borderRightWidth() : 0; + int topWidth = (horizontal || includeLogicalLeftEdge) ? style->borderTopWidth() : 0; + int bottomWidth = (horizontal || includeLogicalRightEdge) ? style->borderBottomWidth() : 0; + + // For each side, create an array of FloatPoints where each point is based on whichever value in each corner + // is larger -- the radius width/height or the border width/height -- as appropriate. + switch (side) { + case BSTop: + quad[0] = FloatPoint(tx, ty); + quad[1] = FloatPoint(tx + max(topLeft.width(), leftWidth), ty + max(topLeft.height(), topWidth)); + quad[2] = FloatPoint(tx + w - max(topRight.width(), rightWidth), ty + max(topRight.height(), topWidth)); + quad[3] = FloatPoint(tx + w, ty); + break; + case BSLeft: + quad[0] = FloatPoint(tx, ty); + quad[1] = FloatPoint(tx + max(topLeft.width(), leftWidth), ty + max(topLeft.height(), topWidth)); + quad[2] = FloatPoint(tx + max(bottomLeft.width(), leftWidth), ty + h - max(bottomLeft.height(), bottomWidth)); + quad[3] = FloatPoint(tx, ty + h); + break; + case BSBottom: + quad[0] = FloatPoint(tx, ty + h); + quad[1] = FloatPoint(tx + max(bottomLeft.width(), leftWidth), ty + h - max(bottomLeft.height(), bottomWidth)); + quad[2] = FloatPoint(tx + w - max(bottomRight.width(), rightWidth), ty + h - max(bottomRight.height(), bottomWidth)); + quad[3] = FloatPoint(tx + w, ty + h); + break; + case BSRight: + quad[0] = FloatPoint(tx + w, ty); + quad[1] = FloatPoint(tx + w - max(topRight.width(), rightWidth), ty + max(topRight.height(), topWidth)); + quad[2] = FloatPoint(tx + w - max(bottomRight.width(), rightWidth), ty + h - max(bottomRight.height(), bottomWidth)); + quad[3] = FloatPoint(tx + w, ty + h); + break; + default: + break; + } + + // If the border matches both of its adjacent sides, don't anti-alias the clip, and + // if neither side matches, anti-alias the clip. + if (firstEdgeMatches == secondEdgeMatches) { + graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); + return; + } + + FloatPoint firstQuad[4]; + firstQuad[0] = quad[0]; + firstQuad[1] = quad[1]; + firstQuad[2] = side == BSTop || side == BSBottom ? FloatPoint(quad[3].x(), quad[2].y()) + : FloatPoint(quad[2].x(), quad[3].y()); + firstQuad[3] = quad[3]; + graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); + + FloatPoint secondQuad[4]; + secondQuad[0] = quad[0]; + secondQuad[1] = side == BSTop || side == BSBottom ? FloatPoint(quad[0].x(), quad[1].y()) + : FloatPoint(quad[1].x(), quad[0].y()); + secondQuad[2] = quad[2]; + secondQuad[3] = quad[3]; + graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); +} + +static inline void uniformlyExpandBorderRadii(int delta, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight) +{ + topLeft.expand(delta, delta); + topLeft.clampNegativeToZero(); + topRight.expand(delta, delta); + topRight.clampNegativeToZero(); + bottomLeft.expand(delta, delta); + bottomLeft.clampNegativeToZero(); + bottomRight.expand(delta, delta); + bottomRight.clampNegativeToZero(); +} + +void RenderBoxModelObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogicalLeftEdge, bool includeLogicalRightEdge) +{ + // FIXME: Deal with border-image. Would be great to use border-image as a mask. + + if (context->paintingDisabled()) + return; + + IntRect rect(tx, ty, w, h); + IntSize topLeft; + IntSize topRight; + IntSize bottomLeft; + IntSize bottomRight; + + bool hasBorderRadius = s->hasBorderRadius(); + bool isHorizontal = s->isHorizontalWritingMode(); + if (hasBorderRadius && (includeLogicalLeftEdge || includeLogicalRightEdge)) { + IntSize topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius; + s->getBorderRadiiForRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + if (includeLogicalLeftEdge) { + if (shadowStyle == Inset) { + topLeftRadius.expand(-borderLeft(), -borderTop()); + topLeftRadius.clampNegativeToZero(); + if (isHorizontal) { + bottomLeftRadius.expand(-borderLeft(), -borderBottom()); + bottomLeftRadius.clampNegativeToZero(); + } else { + topRightRadius.expand(-borderRight(), -borderTop()); + topRightRadius.clampNegativeToZero(); + } + } + topLeft = topLeftRadius; + if (isHorizontal) + bottomLeft = bottomLeftRadius; + else + topRight = topRightRadius; + } + if (includeLogicalRightEdge) { + if (shadowStyle == Inset) { + if (isHorizontal) { + topRightRadius.expand(-borderRight(), -borderTop()); + topRightRadius.clampNegativeToZero(); + } else { + bottomLeftRadius.expand(-borderLeft(), -borderBottom()); + bottomLeftRadius.clampNegativeToZero(); + } + bottomRightRadius.expand(-borderRight(), -borderBottom()); + bottomRightRadius.clampNegativeToZero(); + } + if (isHorizontal) + topRight = topRightRadius; + else + bottomLeft = bottomLeftRadius; + bottomRight = bottomRightRadius; + } + } + + if (shadowStyle == Inset) { + rect.move(includeLogicalLeftEdge || !isHorizontal ? borderLeft() : 0, includeLogicalLeftEdge || isHorizontal ? borderTop() : 0); + rect.setWidth(rect.width() - ((includeLogicalLeftEdge || !isHorizontal) ? borderLeft() : 0) - ((includeLogicalRightEdge || !isHorizontal) ? borderRight() : 0)); + rect.setHeight(rect.height() - ((includeLogicalLeftEdge || isHorizontal) ? borderTop() : 0) - ((includeLogicalRightEdge || isHorizontal) ? borderBottom() : 0)); + } + + bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && s->visitedDependentColor(CSSPropertyBackgroundColor).alpha() == 255; + for (const ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next()) { + if (shadow->style() != shadowStyle) + continue; + + IntSize shadowOffset(shadow->x(), shadow->y()); + int shadowBlur = shadow->blur(); + int shadowSpread = shadow->spread(); + const Color& shadowColor = shadow->color(); + + if (shadow->style() == Normal) { + IntRect fillRect(rect); + fillRect.inflate(shadowSpread); + if (fillRect.isEmpty()) + continue; + + IntRect shadowRect(rect); + shadowRect.inflate(shadowBlur + shadowSpread); + shadowRect.move(shadowOffset); + + context->save(); + context->clip(shadowRect); + + // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not + // bleed in (due to antialiasing) if the context is transformed. + IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 2 * shadowSpread + 1, 0); + shadowOffset -= extraOffset; + fillRect.move(extraOffset); + + context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + if (hasBorderRadius) { + IntRect rectToClipOut = rect; + IntSize topLeftToClipOut = topLeft; + IntSize topRightToClipOut = topRight; + IntSize bottomLeftToClipOut = bottomLeft; + IntSize bottomRightToClipOut = bottomRight; + + IntSize topLeftToFill = topLeft; + IntSize topRightToFill = topRight; + IntSize bottomLeftToFill = bottomLeft; + IntSize bottomRightToFill = bottomRight; + if (shadowSpread < 0) + uniformlyExpandBorderRadii(shadowSpread, topLeftToFill, topRightToFill, bottomLeftToFill, bottomRightToFill); + + // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time + // when painting the shadow. On the other hand, it introduces subpixel gaps along the + // corners. Those are avoided by insetting the clipping path by one pixel. + if (hasOpaqueBackground) { + rectToClipOut.inflate(-1); + uniformlyExpandBorderRadii(-1, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut); + } + + if (!rectToClipOut.isEmpty()) + context->clipOutRoundedRect(rectToClipOut, topLeftToClipOut, topRightToClipOut, bottomLeftToClipOut, bottomRightToClipOut); + context->fillRoundedRect(fillRect, topLeftToFill, topRightToFill, bottomLeftToFill, bottomRightToFill, Color::black, s->colorSpace()); + } else { + IntRect rectToClipOut = rect; + + // If the box is opaque, it is unnecessary to clip it out. However, doing so saves time + // when painting the shadow. On the other hand, it introduces subpixel gaps along the + // edges if they are not pixel-aligned. Those are avoided by insetting the clipping path + // by one pixel. + if (hasOpaqueBackground) { + AffineTransform currentTransformation = context->getCTM(); + if (currentTransformation.a() != 1 || (currentTransformation.d() != 1 && currentTransformation.d() != -1) + || currentTransformation.b() || currentTransformation.c()) + rectToClipOut.inflate(-1); + } + + if (!rectToClipOut.isEmpty()) + context->clipOut(rectToClipOut); + context->fillRect(fillRect, Color::black, s->colorSpace()); + } + + context->restore(); + } else { + // Inset shadow. + IntRect holeRect(rect); + holeRect.inflate(-shadowSpread); + + if (holeRect.isEmpty()) { + if (hasBorderRadius) + context->fillRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight, shadowColor, s->colorSpace()); + else + context->fillRect(rect, shadowColor, s->colorSpace()); + continue; + } + + if (!includeLogicalLeftEdge) { + if (isHorizontal) { + holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0); + holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur); + } else { + holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur); + holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur); + } + } + if (!includeLogicalRightEdge) { + if (isHorizontal) + holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur); + else + holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur); + } + + Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255); + + IntRect outerRect(rect); + outerRect.inflateX(w - 2 * shadowSpread); + outerRect.inflateY(h - 2 * shadowSpread); + + context->save(); + + Path path; + if (hasBorderRadius) { + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + context->clip(path); + path.clear(); + } else + context->clip(rect); + + IntSize extraOffset(2 * w + max(0, shadowOffset.width()) + shadowBlur - 2 * shadowSpread + 1, 0); + context->translate(extraOffset.width(), extraOffset.height()); + shadowOffset -= extraOffset; + + path.addRect(outerRect); + + if (hasBorderRadius) { + if (shadowSpread > 0) + uniformlyExpandBorderRadii(-shadowSpread, topLeft, topRight, bottomLeft, bottomRight); + path.addRoundedRect(holeRect, topLeft, topRight, bottomLeft, bottomRight); + } else + path.addRect(holeRect); + + context->setFillRule(RULE_EVENODD); + context->setFillColor(fillColor, s->colorSpace()); + context->setShadow(shadowOffset, shadowBlur, shadowColor, s->colorSpace()); + context->fillPath(path); + + context->restore(); + } + } +} + +int RenderBoxModelObject::containingBlockLogicalWidthForContent() const +{ + return containingBlock()->availableLogicalWidth(); +} + +RenderBoxModelObject* RenderBoxModelObject::continuation() const +{ + if (!continuationMap) + return 0; + return continuationMap->get(this); +} + +void RenderBoxModelObject::setContinuation(RenderBoxModelObject* continuation) +{ + if (continuation) { + if (!continuationMap) + continuationMap = new ContinuationMap; + continuationMap->set(this, continuation); + } else { + if (continuationMap) + continuationMap->remove(this); + } +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderBoxModelObject.h b/Source/WebCore/rendering/RenderBoxModelObject.h new file mode 100644 index 0000000..98e386b --- /dev/null +++ b/Source/WebCore/rendering/RenderBoxModelObject.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderBoxModelObject_h +#define RenderBoxModelObject_h + +#include "RenderObject.h" + +namespace WebCore { + +// Modes for some of the line-related functions. +enum LinePositionMode { PositionOnContainingLine, PositionOfInteriorLineBoxes }; +enum LineDirectionMode { HorizontalLine, VerticalLine }; + +// This class is the base for all objects that adhere to the CSS box model as described +// at http://www.w3.org/TR/CSS21/box.html + +class RenderBoxModelObject : public RenderObject { +public: + RenderBoxModelObject(Node*); + virtual ~RenderBoxModelObject(); + + virtual void destroy(); + + int relativePositionOffsetX() const; + int relativePositionOffsetY() const; + IntSize relativePositionOffset() const { return IntSize(relativePositionOffsetX(), relativePositionOffsetY()); } + IntSize relativePositionLogicalOffset() const { return style()->isHorizontalWritingMode() ? relativePositionOffset() : relativePositionOffset().transposedSize(); } + + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) + // to return the remaining width on a given line (and the height of a single line). + virtual int offsetLeft() const; + virtual int offsetTop() const; + virtual int offsetWidth() const = 0; + virtual int offsetHeight() const = 0; + + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + virtual void updateBoxModelInfoFromStyle(); + + bool hasSelfPaintingLayer() const; + RenderLayer* layer() const { return m_layer; } + virtual bool requiresLayer() const { return isRoot() || isPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection() || style()->specifiesColumns(); } + + // This will work on inlines to return the bounding box of all of the lines' border boxes. + virtual IntRect borderBoundingBox() const = 0; + + // Virtual since table cells override + virtual int paddingTop(bool includeIntrinsicPadding = true) const; + virtual int paddingBottom(bool includeIntrinsicPadding = true) const; + virtual int paddingLeft(bool includeIntrinsicPadding = true) const; + virtual int paddingRight(bool includeIntrinsicPadding = true) const; + virtual int paddingBefore(bool includeIntrinsicPadding = true) const; + virtual int paddingAfter(bool includeIntrinsicPadding = true) const; + virtual int paddingStart(bool includeIntrinsicPadding = true) const; + virtual int paddingEnd(bool includeIntrinsicPadding = true) const; + + virtual int borderTop() const { return style()->borderTopWidth(); } + virtual int borderBottom() const { return style()->borderBottomWidth(); } + virtual int borderLeft() const { return style()->borderLeftWidth(); } + virtual int borderRight() const { return style()->borderRightWidth(); } + virtual int borderBefore() const { return style()->borderBeforeWidth(); } + virtual int borderAfter() const { return style()->borderAfterWidth(); } + virtual int borderStart() const { return style()->borderStartWidth(); } + virtual int borderEnd() const { return style()->borderEndWidth(); } + + int borderAndPaddingHeight() const { return borderTop() + borderBottom() + paddingTop() + paddingBottom(); } + int borderAndPaddingWidth() const { return borderLeft() + borderRight() + paddingLeft() + paddingRight(); } + int borderAndPaddingLogicalHeight() const { return borderBefore() + borderAfter() + paddingBefore() + paddingAfter(); } + int borderAndPaddingLogicalWidth() const { return borderStart() + borderEnd() + paddingStart() + paddingEnd(); } + + virtual int marginTop() const = 0; + virtual int marginBottom() const = 0; + virtual int marginLeft() const = 0; + virtual int marginRight() const = 0; + virtual int marginBefore() const = 0; + virtual int marginAfter() const = 0; + virtual int marginStart() const = 0; + virtual int marginEnd() const = 0; + + bool hasInlineDirectionBordersPaddingOrMargin() const { return hasInlineDirectionBordersOrPadding() || marginStart()|| marginEnd(); } + bool hasInlineDirectionBordersOrPadding() const { return borderStart() || borderEnd() || paddingStart()|| paddingEnd(); } + + virtual int containingBlockLogicalWidthForContent() const; + + virtual void childBecameNonInline(RenderObject* /*child*/) { } + + void paintBorder(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true); + bool paintNinePieceImage(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, const NinePieceImage&, CompositeOperator = CompositeSourceOver); + void paintBoxShadow(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, ShadowStyle, bool includeLogicalLeftEdge = true, bool includeLogicalRightEdge = true); + void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver, RenderObject* backgroundObject = 0); + + // Overridden by subclasses to determine line height and baseline position. + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; + virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const = 0; + + // Called by RenderObject::destroy() (and RenderWidget::destroy()) and is the only way layers should ever be destroyed + void destroyLayer(); + + void highQualityRepaintTimerFired(Timer*); + + virtual void setSelectionState(SelectionState s); +protected: + void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); + + bool shouldPaintAtLowQuality(GraphicsContext*, Image*, const void*, const IntSize&); + + RenderBoxModelObject* continuation() const; + void setContinuation(RenderBoxModelObject*); + +private: + virtual bool isBoxModelObject() const { return true; } + + IntSize calculateFillTileSize(const FillLayer*, IntSize scaledSize) const; + + void clipBorderSidePolygon(GraphicsContext*, const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, + const IntSize& bottomRight, const BoxSide side, bool firstEdgeMatches, bool secondEdgeMatches, const RenderStyle* style, + bool includeLogicalLeftEdge, bool includeLogicalRightEdge); + + friend class RenderView; + + RenderLayer* m_layer; + + // Used to store state between styleWillChange and styleDidChange + static bool s_wasFloating; + static bool s_hadLayer; + static bool s_layerWasSelfPainting; +}; + +inline RenderBoxModelObject* toRenderBoxModelObject(RenderObject* object) +{ + ASSERT(!object || object->isBoxModelObject()); + return static_cast(object); +} + +inline const RenderBoxModelObject* toRenderBoxModelObject(const RenderObject* object) +{ + ASSERT(!object || object->isBoxModelObject()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderBoxModelObject(const RenderBoxModelObject*); + +} // namespace WebCore + +#endif // RenderBoxModelObject_h diff --git a/Source/WebCore/rendering/RenderButton.cpp b/Source/WebCore/rendering/RenderButton.cpp new file mode 100644 index 0000000..2642f23 --- /dev/null +++ b/Source/WebCore/rendering/RenderButton.cpp @@ -0,0 +1,197 @@ +/** + * Copyright (C) 2005 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderButton.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "RenderTextFragment.h" +#include "RenderTheme.h" + +#if ENABLE(WML) +#include "WMLDoElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +RenderButton::RenderButton(Node* node) + : RenderFlexibleBox(node) + , m_buttonText(0) + , m_inner(0) + , m_default(false) +{ +} + +RenderButton::~RenderButton() +{ +} + +void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (!m_inner) { + // Create an anonymous block. + ASSERT(!firstChild()); + bool isFlexibleBox = style()->display() == BOX || style()->display() == INLINE_BOX; + m_inner = createAnonymousBlock(isFlexibleBox); + setupInnerStyle(m_inner->style()); + RenderFlexibleBox::addChild(m_inner); + } + + m_inner->addChild(newChild, beforeChild); +} + +void RenderButton::removeChild(RenderObject* oldChild) +{ + if (oldChild == m_inner || !m_inner) { + RenderFlexibleBox::removeChild(oldChild); + m_inner = 0; + } else + m_inner->removeChild(oldChild); +} + +void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) +{ + if (m_inner) { + // RenderBlock::setStyle is going to apply a new style to the inner block, which + // will have the initial box flex value, 0. The current value is 1, because we set + // it right below. Here we change it back to 0 to avoid getting a spurious layout hint + // because of the difference. + m_inner->style()->setBoxFlex(0); + } + RenderBlock::styleWillChange(diff, newStyle); +} + +void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (m_buttonText) + m_buttonText->setStyle(style()); + if (m_inner) // RenderBlock handled updating the anonymous block's style. + setupInnerStyle(m_inner->style()); + setReplaced(isInline()); + + if (!m_default && theme()->isDefault(this)) { + if (!m_timer) + m_timer.set(new Timer(this, &RenderButton::timerFired)); + m_timer->startRepeating(0.03); + m_default = true; + } else if (m_default && !theme()->isDefault(this)) { + m_default = false; + m_timer.clear(); + } +} + +void RenderButton::setupInnerStyle(RenderStyle* innerStyle) +{ + ASSERT(innerStyle->refCount() == 1); + // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is + // safe to modify. + innerStyle->setBoxFlex(1.0f); + innerStyle->setBoxOrient(style()->boxOrient()); +} + +void RenderButton::updateFromElement() +{ + // If we're an input element, we may need to change our button text. + if (node()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast(node()); + String value = input->valueWithDefault(); + setText(value); + } + + +#if ENABLE(WML) + else if (node()->hasTagName(WMLNames::doTag)) { + WMLDoElement* doElement = static_cast(node()); + + String value = doElement->label(); + if (value.isEmpty()) + value = doElement->name(); + + setText(value); + } +#endif +} + +bool RenderButton::canHaveChildren() const +{ + // Input elements can't have children, but button elements can. We'll + // write the code assuming any other button types that might emerge in the future + // can also have children. + return !node()->hasTagName(inputTag); +} + +void RenderButton::setText(const String& str) +{ + if (str.isEmpty()) { + if (m_buttonText) { + m_buttonText->destroy(); + m_buttonText = 0; + } + } else { + if (m_buttonText) + m_buttonText->setText(str.impl()); + else { + m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl()); + m_buttonText->setStyle(style()); + addChild(m_buttonText); + } + } +} + +String RenderButton::text() const +{ + return m_buttonText ? m_buttonText->text() : 0; +} + +void RenderButton::updateBeforeAfterContent(PseudoId type) +{ + if (m_inner) + m_inner->children()->updateBeforeAfterContent(m_inner, type, this); + else + children()->updateBeforeAfterContent(this, type); +} + +IntRect RenderButton::controlClipRect(int tx, int ty) const +{ + // Clip to the padding box to at least give content the extra padding space. + return IntRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom()); +} + +void RenderButton::timerFired(Timer*) +{ + // FIXME Bug 25110: Ideally we would stop our timer when our Document + // enters the page cache. But we currently have no way of being notified + // when that happens, so we'll just ignore the timer firing as long as + // we're in the cache. + if (document()->inPageCache()) + return; + + repaint(); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderButton.h b/Source/WebCore/rendering/RenderButton.h new file mode 100644 index 0000000..252edb4 --- /dev/null +++ b/Source/WebCore/rendering/RenderButton.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005 Apple Computer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderButton_h +#define RenderButton_h + +#include "RenderFlexibleBox.h" +#include "Timer.h" +#include + +namespace WebCore { + +class RenderTextFragment; + +// RenderButtons are just like normal flexboxes except that they will generate an anonymous block child. +// For inputs, they will also generate an anonymous RenderText and keep its style and content up +// to date as the button changes. +class RenderButton : public RenderFlexibleBox { +public: + explicit RenderButton(Node*); + virtual ~RenderButton(); + + virtual const char* renderName() const { return "RenderButton"; } + virtual bool isRenderButton() const { return true; } + + virtual void addChild(RenderObject* newChild, RenderObject *beforeChild = 0); + virtual void removeChild(RenderObject*); + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + virtual bool createsAnonymousWrapper() const { return true; } + + void setupInnerStyle(RenderStyle*); + virtual void updateFromElement(); + + virtual void updateBeforeAfterContent(PseudoId); + + virtual bool hasControlClip() const { return true; } + virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const; + + void setText(const String&); + String text() const; + + virtual bool canHaveChildren() const; + +private: + virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle); + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual bool hasLineIfEmpty() const { return true; } + + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + + void timerFired(Timer*); + + RenderTextFragment* m_buttonText; + RenderBlock* m_inner; + + OwnPtr > m_timer; + bool m_default; +}; + +inline RenderButton* toRenderButton(RenderObject* object) +{ + ASSERT(!object || object->isRenderButton()); + return static_cast(object); +} + +inline const RenderButton* toRenderButton(const RenderObject* object) +{ + ASSERT(!object || object->isRenderButton()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderButton(const RenderButton*); + +} // namespace WebCore + +#endif // RenderButton_h diff --git a/Source/WebCore/rendering/RenderCounter.cpp b/Source/WebCore/rendering/RenderCounter.cpp new file mode 100644 index 0000000..57c54f8 --- /dev/null +++ b/Source/WebCore/rendering/RenderCounter.cpp @@ -0,0 +1,496 @@ +/** + * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderCounter.h" + +#include "CounterNode.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "RenderListItem.h" +#include "RenderListMarker.h" +#include "RenderStyle.h" +#include + +namespace WebCore { + +using namespace HTMLNames; + +typedef HashMap, RefPtr > CounterMap; +typedef HashMap CounterMaps; + +static CounterNode* makeCounterNode(RenderObject*, const AtomicString& identifier, bool alwaysCreateCounter); + +static CounterMaps& counterMaps() +{ + DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ()); + return staticCounterMaps; +} + +static inline RenderObject* previousSiblingOrParent(RenderObject* object) +{ + if (RenderObject* sibling = object->previousSibling()) + return sibling; + return object->parent(); +} + +static bool planCounter(RenderObject* object, const AtomicString& identifier, bool& isReset, int& value) +{ + ASSERT(object); + + // Real text nodes don't have their own style so they can't have counters. + // We can't even look at their styles or we'll see extra resets and increments! + if (object->isText() && !object->isBR()) + return false; + + RenderStyle* style = object->style(); + ASSERT(style); + + if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { + CounterDirectives directives = directivesMap->get(identifier.impl()); + if (directives.m_reset) { + value = directives.m_resetValue; + if (directives.m_increment) + value += directives.m_incrementValue; + isReset = true; + return true; + } + if (directives.m_increment) { + value = directives.m_incrementValue; + isReset = false; + return true; + } + } + + if (identifier == "list-item") { + if (object->isListItem()) { + if (toRenderListItem(object)->hasExplicitValue()) { + value = toRenderListItem(object)->explicitValue(); + isReset = true; + return true; + } + value = 1; + isReset = false; + return true; + } + if (Node* e = object->node()) { + if (e->hasTagName(olTag)) { + value = static_cast(e)->start(); + isReset = true; + return true; + } + if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) { + value = 0; + isReset = true; + return true; + } + } + } + + return false; +} + +// - Finds the insertion point for the counter described by counterOwner, isReset and +// identifier in the CounterNode tree for identifier and sets parent and +// previousSibling accordingly. +// - The function returns true if the counter whose insertion point is searched is NOT +// the root of the tree. +// - The root of the tree is a counter reference that is not in the scope of any other +// counter with the same identifier. +// - All the counter references with the same identifier as this one that are in +// children or subsequent siblings of the renderer that owns the root of the tree +// form the rest of of the nodes of the tree. +// - The root of the tree is always a reset type reference. +// - A subtree rooted at any reset node in the tree is equivalent to all counter +// references that are in the scope of the counter or nested counter defined by that +// reset node. +// - Non-reset CounterNodes cannot have descendants. + +static bool findPlaceForCounter(RenderObject* counterOwner, const AtomicString& identifier, bool isReset, CounterNode*& parent, CounterNode*& previousSibling) +{ + // We cannot stop searching for counters with the same identifier before we also + // check this renderer, because it may affect the positioning in the tree of our counter. + RenderObject* searchEndRenderer = previousSiblingOrParent(counterOwner); + // We check renderers in preOrder from the renderer that our counter is attached to + // towards the begining of the document for counters with the same identifier as the one + // we are trying to find a place for. This is the next renderer to be checked. + RenderObject* currentRenderer = counterOwner->previousInPreOrder(); + previousSibling = 0; + while (currentRenderer) { + // A sibling without a parent means that the counter node tree was not constructed correctly so we stop + // traversing. In the future RenderCounter should handle RenderObjects that are not connected to the + // render tree at counter node creation. See bug 43812. + if (previousSibling && !previousSibling->parent()) + return false; + CounterNode* currentCounter = makeCounterNode(currentRenderer, identifier, false); + if (searchEndRenderer == currentRenderer) { + // We may be at the end of our search. + if (currentCounter) { + // We have a suitable counter on the EndSearchRenderer. + if (previousSibling) { // But we already found another counter that we come after. + if (currentCounter->actsAsReset()) { + // We found a reset counter that is on a renderer that is a sibling of ours or a parent. + if (isReset && currentRenderer->parent() == counterOwner->parent()) { + // We are also a reset counter and the previous reset was on a sibling renderer + // hence we are the next sibling of that counter if that reset is not a root or + // we are a root node if that reset is a root. + parent = currentCounter->parent(); + previousSibling = parent ? currentCounter : 0; + return parent; + } + // We are not a reset node or the previous reset must be on an ancestor of our renderer + // hence we must be a child of that reset counter. + parent = currentCounter; + ASSERT(previousSibling->parent() == currentCounter); + return true; + } + // CurrentCounter, the counter at the EndSearchRenderer, is not reset. + if (!isReset || currentRenderer->parent() != counterOwner->parent()) { + // If the node we are placing is not reset or we have found a counter that is attached + // to an ancestor of the placed counter's renderer we know we are a sibling of that node. + ASSERT(currentCounter->parent() == previousSibling->parent()); + parent = currentCounter->parent(); + return true; + } + } else { + // We are at the potential end of the search, but we had no previous sibling candidate + // In this case we follow pretty much the same logic as above but no ASSERTs about + // previousSibling, and when we are a sibling of the end counter we must set previousSibling + // to currentCounter. + if (currentCounter->actsAsReset()) { + if (isReset && currentRenderer->parent() == counterOwner->parent()) { + parent = currentCounter->parent(); + previousSibling = currentCounter; + return parent; + } + parent = currentCounter; + return true; + } + if (!isReset || currentRenderer->parent() != counterOwner->parent()) { + parent = currentCounter->parent(); + previousSibling = currentCounter; + return true; + } + previousSibling = currentCounter; + } + } + // We come here if the previous sibling or parent of our renderer had no + // good counter, or we are a reset node and the counter on the previous sibling + // of our renderer was not a reset counter. + // Set a new goal for the end of the search. + searchEndRenderer = previousSiblingOrParent(currentRenderer); + } else { + // We are searching descendants of a previous sibling of the renderer that the + // counter being placed is attached to. + if (currentCounter) { + // We found a suitable counter. + if (previousSibling) { + // Since we had a suitable previous counter before, we should only consider this one as our + // previousSibling if it is a reset counter and hence the current previousSibling is its child. + if (currentCounter->actsAsReset()) { + previousSibling = currentCounter; + // We are no longer interested in previous siblings of the currentRenderer or their children + // as counters they may have attached cannot be the previous sibling of the counter we are placing. + currentRenderer = currentRenderer->parent(); + continue; + } + } else + previousSibling = currentCounter; + currentRenderer = previousSiblingOrParent(currentRenderer); + continue; + } + } + // This function is designed so that the same test is not done twice in an iteration, except for this one + // which may be done twice in some cases. Rearranging the decision points though, to accommodate this + // performance improvement would create more code duplication than is worthwhile in my oppinion and may further + // impede the readability of this already complex algorithm. + if (previousSibling) + currentRenderer = previousSiblingOrParent(currentRenderer); + else + currentRenderer = currentRenderer->previousInPreOrder(); + } + return false; +} + +static CounterNode* makeCounterNode(RenderObject* object, const AtomicString& identifier, bool alwaysCreateCounter) +{ + ASSERT(object); + + if (object->m_hasCounterNodeMap) { + if (CounterMap* nodeMap = counterMaps().get(object)) { + if (CounterNode* node = nodeMap->get(identifier.impl()).get()) + return node; + } + } + + bool isReset = false; + int value = 0; + if (!planCounter(object, identifier, isReset, value) && !alwaysCreateCounter) + return 0; + + CounterNode* newParent = 0; + CounterNode* newPreviousSibling = 0; + RefPtr newNode = CounterNode::create(object, isReset, value); + if (findPlaceForCounter(object, identifier, isReset, newParent, newPreviousSibling)) + newParent->insertAfter(newNode.get(), newPreviousSibling, identifier); + CounterMap* nodeMap; + if (object->m_hasCounterNodeMap) + nodeMap = counterMaps().get(object); + else { + nodeMap = new CounterMap; + counterMaps().set(object, nodeMap); + object->m_hasCounterNodeMap = true; + } + nodeMap->set(identifier.impl(), newNode); + if (newNode->parent() || !object->nextInPreOrder(object->parent())) + return newNode.get(); + // Checking if some nodes that were previously counter tree root nodes + // should become children of this node now. + CounterMaps& maps = counterMaps(); + RenderObject* stayWithin = object->parent(); + for (RenderObject* currentRenderer = object->nextInPreOrder(stayWithin); currentRenderer; currentRenderer = currentRenderer->nextInPreOrder(stayWithin)) { + if (!currentRenderer->m_hasCounterNodeMap) + continue; + CounterNode* currentCounter = maps.get(currentRenderer)->get(identifier.impl()).get(); + if (!currentCounter) + continue; + if (currentCounter->parent()) { + ASSERT(newNode->firstChild()); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + continue; + } + if (stayWithin != currentRenderer->parent() || !currentCounter->hasResetType()) + newNode->insertAfter(currentCounter, newNode->lastChild(), identifier); + if (currentRenderer->lastChild()) + currentRenderer = currentRenderer->lastChild(); + } + return newNode.get(); +} + +RenderCounter::RenderCounter(Document* node, const CounterContent& counter) + : RenderText(node, StringImpl::empty()) + , m_counter(counter) + , m_counterNode(0) +{ +} + +RenderCounter::~RenderCounter() +{ +} + +const char* RenderCounter::renderName() const +{ + return "RenderCounter"; +} + +bool RenderCounter::isCounter() const +{ + return true; +} + +PassRefPtr RenderCounter::originalText() const +{ + if (!parent()) + return 0; + + if (!m_counterNode) + m_counterNode = makeCounterNode(parent(), m_counter.identifier(), true); + + CounterNode* child = m_counterNode; + int value = child->actsAsReset() ? child->value() : child->countInParent(); + + String text = listMarkerText(m_counter.listStyle(), value); + + if (!m_counter.separator().isNull()) { + if (!child->actsAsReset()) + child = child->parent(); + while (CounterNode* parent = child->parent()) { + text = listMarkerText(m_counter.listStyle(), child->countInParent()) + + m_counter.separator() + text; + child = parent; + } + } + + return text.impl(); +} + +void RenderCounter::computePreferredLogicalWidths(int lead) +{ + setTextInternal(originalText()); + RenderText::computePreferredLogicalWidths(lead); +} + +void RenderCounter::invalidate(const AtomicString& identifier) +{ + if (m_counter.identifier() != identifier) + return; + m_counterNode = 0; + setNeedsLayoutAndPrefWidthsRecalc(); +} + +static void destroyCounterNodeWithoutMapRemoval(const AtomicString& identifier, CounterNode* node) +{ + CounterNode* previous; + for (RefPtr child = node->lastDescendant(); child && child != node; child = previous) { + previous = child->previousInPreOrder(); + child->parent()->removeChild(child.get(), identifier); + ASSERT(counterMaps().get(child->renderer())->get(identifier.impl()) == child); + counterMaps().get(child->renderer())->remove(identifier.impl()); + if (!child->renderer()->documentBeingDestroyed()) { + RenderObjectChildList* children = child->renderer()->virtualChildren(); + if (children) + children->invalidateCounters(child->renderer(), identifier); + } + } + RenderObject* renderer = node->renderer(); + if (!renderer->documentBeingDestroyed()) { + if (RenderObjectChildList* children = renderer->virtualChildren()) + children->invalidateCounters(renderer, identifier); + } + if (CounterNode* parent = node->parent()) + parent->removeChild(node, identifier); +} + +void RenderCounter::destroyCounterNodes(RenderObject* renderer) +{ + CounterMaps& maps = counterMaps(); + CounterMaps::iterator mapsIterator = maps.find(renderer); + if (mapsIterator == maps.end()) + return; + CounterMap* map = mapsIterator->second; + CounterMap::const_iterator end = map->end(); + for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { + AtomicString identifier(it->first.get()); + destroyCounterNodeWithoutMapRemoval(identifier, it->second.get()); + } + maps.remove(mapsIterator); + delete map; + renderer->m_hasCounterNodeMap = false; +} + +void RenderCounter::destroyCounterNode(RenderObject* renderer, const AtomicString& identifier) +{ + CounterMap* map = counterMaps().get(renderer); + if (!map) + return; + CounterMap::iterator mapIterator = map->find(identifier.impl()); + if (mapIterator == map->end()) + return; + destroyCounterNodeWithoutMapRemoval(identifier, mapIterator->second.get()); + map->remove(mapIterator); + // We do not delete "map" here even if empty because we expect to reuse + // it soon. In order for a renderer to lose all its counters permanently, + // a style change for the renderer involving removal of all counter + // directives must occur, in which case, RenderCounter::destroyCounterNodes() + // must be called. + // The destruction of the Renderer (possibly caused by the removal of its + // associated DOM node) is the other case that leads to the permanent + // destruction of all counters attached to a Renderer. In this case + // RenderCounter::destroyCounterNodes() must be and is now called, too. + // RenderCounter::destroyCounterNodes() handles destruction of the counter + // map associated with a renderer, so there is no risk in leaking the map. +} + +static void updateCounters(RenderObject* renderer) +{ + ASSERT(renderer->style()); + const CounterDirectiveMap* directiveMap = renderer->style()->counterDirectives(); + if (!directiveMap) + return; + CounterDirectiveMap::const_iterator end = directiveMap->end(); + if (!renderer->m_hasCounterNodeMap) { + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) + makeCounterNode(renderer, AtomicString(it->first.get()), false); + return; + } + CounterMap* counterMap = counterMaps().get(renderer); + ASSERT(counterMap); + for (CounterDirectiveMap::const_iterator it = directiveMap->begin(); it != end; ++it) { + RefPtr node = counterMap->get(it->first.get()); + if (!node) { + makeCounterNode(renderer, AtomicString(it->first.get()), false); + continue; + } + CounterNode* newParent = 0; + CounterNode* newPreviousSibling; + + findPlaceForCounter(renderer, AtomicString(it->first.get()), node->hasResetType(), newParent, newPreviousSibling); + if (node != counterMap->get(it->first.get())) + continue; + CounterNode* parent = node->parent(); + if (newParent == parent && newPreviousSibling == node->previousSibling()) + continue; + if (parent) + parent->removeChild(node.get(), it->first.get()); + if (newParent) + newParent->insertAfter(node.get(), newPreviousSibling, it->first.get()); + } +} + +void RenderCounter::rendererSubtreeAttached(RenderObject* renderer) +{ + for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) + updateCounters(descendant); +} + +void RenderCounter::rendererStyleChanged(RenderObject* renderer, const RenderStyle* oldStyle, const RenderStyle* newStyle) +{ + const CounterDirectiveMap* newCounterDirectives; + const CounterDirectiveMap* oldCounterDirectives; + if (oldStyle && (oldCounterDirectives = oldStyle->counterDirectives())) { + if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + CounterDirectiveMap::const_iterator oldMapEnd = oldCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + CounterDirectiveMap::const_iterator oldMapIt = oldCounterDirectives->find(it->first); + if (oldMapIt != oldMapEnd) { + if (oldMapIt->second == it->second) + continue; + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + // We must create this node here, because the changed node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + // Destroying old counters that do not exist in the new counterDirective map. + for (CounterDirectiveMap::const_iterator it = oldCounterDirectives->begin(); it !=oldMapEnd; ++it) { + if (!newCounterDirectives->contains(it->first)) + RenderCounter::destroyCounterNode(renderer, it->first.get()); + } + } else { + if (renderer->m_hasCounterNodeMap) + RenderCounter::destroyCounterNodes(renderer); + } + } else if (newStyle && (newCounterDirectives = newStyle->counterDirectives())) { + CounterDirectiveMap::const_iterator newMapEnd = newCounterDirectives->end(); + for (CounterDirectiveMap::const_iterator it = newCounterDirectives->begin(); it != newMapEnd; ++it) { + // We must create this node here, because the added node may be a node with no display such as + // as those created by the increment or reset directives and the re-layout that will happen will + // not catch the change if the node had no children. + makeCounterNode(renderer, it->first.get(), false); + } + } +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderCounter.h b/Source/WebCore/rendering/RenderCounter.h new file mode 100644 index 0000000..9373193 --- /dev/null +++ b/Source/WebCore/rendering/RenderCounter.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderCounter_h +#define RenderCounter_h + +#include "CounterContent.h" +#include "RenderText.h" + +namespace WebCore { + +class CounterNode; + +class RenderCounter : public RenderText { +public: + RenderCounter(Document*, const CounterContent&); + virtual ~RenderCounter(); + + // Removes the reference to the CounterNode associated with this renderer + // if its identifier matches the argument. + // This is used to cause a counter display update when the CounterNode + // tree for identifier changes. + void invalidate(const AtomicString& identifier); + + static void destroyCounterNodes(RenderObject*); + static void destroyCounterNode(RenderObject*, const AtomicString& identifier); + static void rendererSubtreeAttached(RenderObject*); + static void rendererStyleChanged(RenderObject*, const RenderStyle* oldStyle, const RenderStyle* newStyle); + +private: + virtual const char* renderName() const; + virtual bool isCounter() const; + virtual PassRefPtr originalText() const; + + virtual void computePreferredLogicalWidths(int leadWidth); + + CounterContent m_counter; + mutable CounterNode* m_counterNode; +}; + +inline RenderCounter* toRenderCounter(RenderObject* object) +{ + ASSERT(!object || object->isCounter()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderCounter(const RenderCounter*); + +} // namespace WebCore + +#endif // RenderCounter_h diff --git a/Source/WebCore/rendering/RenderDataGrid.cpp b/Source/WebCore/rendering/RenderDataGrid.cpp new file mode 100644 index 0000000..c322389 --- /dev/null +++ b/Source/WebCore/rendering/RenderDataGrid.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(DATAGRID) + +#include "RenderDataGrid.h" + +#include "CSSStyleSelector.h" +#include "FocusController.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "Page.h" +#include "RenderView.h" +#include "Scrollbar.h" + +using std::min; + +namespace WebCore { + +static const int cDefaultWidth = 300; + +RenderDataGrid::RenderDataGrid(Element* elt) + : RenderBlock(elt) +{ +} + +RenderDataGrid::~RenderDataGrid() +{ +} + +void RenderDataGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + recalcStyleForColumns(); +} + +void RenderDataGrid::recalcStyleForColumns() +{ + DataGridColumnList* columns = gridElement()->columns(); + unsigned length = columns->length(); + for (unsigned i = 0; i < length; ++i) + recalcStyleForColumn(columns->item(i)); +} + +void RenderDataGrid::recalcStyleForColumn(DataGridColumn* column) +{ + if (!column->columnStyle()) + column->setColumnStyle(document()->styleSelector()->pseudoStyleForDataGridColumn(column, style())); + if (!column->headerStyle()) + column->setHeaderStyle(document()->styleSelector()->pseudoStyleForDataGridColumnHeader(column, style())); +} + +RenderStyle* RenderDataGrid::columnStyle(DataGridColumn* column) +{ + if (!column->columnStyle()) + recalcStyleForColumn(column); + return column->columnStyle(); +} + +RenderStyle* RenderDataGrid::headerStyle(DataGridColumn* column) +{ + if (!column->headerStyle()) + recalcStyleForColumn(column); + return column->headerStyle(); +} + +void RenderDataGrid::computePreferredLogicalWidths() +{ + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); + else + m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(cDefaultWidth); + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPreferredLogicalWidth = 0; + else + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + } + + int toAdd = borderAndPaddingWidth(); + m_minPreferredLogicalWidth += toAdd; + m_maxPreferredLogicalWidth += toAdd; + + setPreferredLogicalWidthsDirty(false); +} + +void RenderDataGrid::layout() +{ + RenderBlock::layout(); + layoutColumns(); +} + +void RenderDataGrid::layoutColumns() +{ + // FIXME: Implement. +} + +void RenderDataGrid::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE) + return; + + // Paint our background and border. + RenderBlock::paintObject(paintInfo, tx, ty); + + if (paintInfo.phase != PaintPhaseForeground) + return; + + // Paint our column headers first. + paintColumnHeaders(paintInfo, tx, ty); +} + +void RenderDataGrid::paintColumnHeaders(PaintInfo& paintInfo, int tx, int ty) +{ + DataGridColumnList* columns = gridElement()->columns(); + unsigned length = columns->length(); + for (unsigned i = 0; i < length; ++i) { + DataGridColumn* column = columns->item(i); + RenderStyle* columnStyle = headerStyle(column); + + // Don't render invisible columns. + if (!columnStyle || columnStyle->display() == NONE || columnStyle->visibility() != VISIBLE) + continue; + + // Paint the column header if it intersects the dirty rect. + IntRect columnRect(column->rect()); + columnRect.move(tx, ty); + if (columnRect.intersects(paintInfo.rect)) + paintColumnHeader(column, paintInfo, tx, ty); + } +} + +void RenderDataGrid::paintColumnHeader(DataGridColumn*, PaintInfo&, int, int) +{ + // FIXME: Implement. +} + +// Scrolling implementation functions +int RenderDataGrid::scrollSize(ScrollbarOrientation orientation) const +{ + return ((orientation == VerticallScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0; +} + +void RenderDataGrid::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_vBar) + m_vBar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + +void RenderDataGrid::valueChanged(Scrollbar*) +{ + // FIXME: Implement. +} + +void RenderDataGrid::invalidateScrollbarRect(Scrollbar*, const IntRect&) +{ + // FIXME: Implement. +} + +bool RenderDataGrid::isActive() const +{ + Page* page = frame()->page(); + return page && page->focusController()->isActive(); +} + + +IntRect RenderDataGrid::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const +{ + RenderView* view = this->view(); + if (!view) + return scrollbarRect; + + IntRect rect = scrollbarRect; + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + rect.move(scrollbarLeft, scrollbarTop); + + return view->frameView()->convertFromRenderer(this, rect); +} + +IntRect RenderDataGrid::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const +{ + RenderView* view = this->view(); + if (!view) + return parentRect; + + IntRect rect = view->frameView()->convertToRenderer(this, parentRect); + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + rect.move(-scrollbarLeft, -scrollbarTop); + return rect; +} + +IntPoint RenderDataGrid::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const +{ + RenderView* view = this->view(); + if (!view) + return scrollbarPoint; + + IntPoint point = scrollbarPoint; + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + point.move(scrollbarLeft, scrollbarTop); + + return view->frameView()->convertFromRenderer(this, point); +} + +IntPoint RenderDataGrid::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const +{ + RenderView* view = this->view(); + if (!view) + return parentPoint; + + IntPoint point = view->frameView()->convertToRenderer(this, parentPoint); + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + point.move(-scrollbarLeft, -scrollbarTop); + return point; +} + +} + +#endif diff --git a/Source/WebCore/rendering/RenderDataGrid.h b/Source/WebCore/rendering/RenderDataGrid.h new file mode 100644 index 0000000..1492d26 --- /dev/null +++ b/Source/WebCore/rendering/RenderDataGrid.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderDataGrid_h +#define RenderDataGrid_h + +#if ENABLE(DATAGRID) + +#include "HTMLDataGridElement.h" +#include "RenderBlock.h" +#include "ScrollbarClient.h" +#include "StyleImage.h" +#include +#include + +namespace WebCore { + +class RenderDataGrid : public RenderBlock, private ScrollbarClient { +public: + RenderDataGrid(Element*); + ~RenderDataGrid(); + + virtual const char* renderName() const { return "RenderDataGrid"; } + virtual bool canHaveChildren() const { return false; } + virtual void computePreferredLogicalWidths(); + virtual void layout(); + virtual void paintObject(PaintInfo&, int tx, int ty); + + void columnsChanged(); + +private: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + + RenderStyle* columnStyle(DataGridColumn*); + RenderStyle* headerStyle(DataGridColumn*); + void recalcStyleForColumns(); + void recalcStyleForColumn(DataGridColumn*); + + void layoutColumns(); + void paintColumnHeaders(PaintInfo&, int tx, int ty); + void paintColumnHeader(DataGridColumn*, PaintInfo&, int tx, int ty); + + HTMLDataGridElement* gridElement() const { return static_cast(node()); } + + // ScrollbarClient interface. + virtual int scrollSize(ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const IntPoint&); + virtual void valueChanged(Scrollbar*); + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual bool isActive() const; + virtual bool scrollbarCornerPresent() const { return false; } // We don't support resize on data grids yet. If we did this would have to change. + virtual IntRect convertFromScrollbarToContainingView(const Scrollbar*, const IntRect&) const; + virtual IntRect convertFromContainingViewToScrollbar(const Scrollbar*, const IntRect&) const; + virtual IntPoint convertFromScrollbarToContainingView(const Scrollbar*, const IntPoint&) const; + virtual IntPoint convertFromContainingViewToScrollbar(const Scrollbar*, const IntPoint&) const; + + RefPtr m_vBar; +}; + +} + +#endif + +#endif // RenderDataGrid_h diff --git a/Source/WebCore/rendering/RenderDetails.cpp b/Source/WebCore/rendering/RenderDetails.cpp new file mode 100644 index 0000000..a1039f9 --- /dev/null +++ b/Source/WebCore/rendering/RenderDetails.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderDetails.h" + +namespace WebCore { + +RenderDetails::RenderDetails(Node* element) + : RenderBlock(element) +{ +} + +void RenderDetails::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + // Ensure that if we ended up being inline that we set our replaced flag + // so that we're treated like an inline-block. + setReplaced(isInline()); +} + +} diff --git a/Source/WebCore/rendering/RenderDetails.h b/Source/WebCore/rendering/RenderDetails.h new file mode 100644 index 0000000..b8aebab --- /dev/null +++ b/Source/WebCore/rendering/RenderDetails.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderDetails_h +#define RenderDetails_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderDetails : public RenderBlock { +public: + explicit RenderDetails(Node*); + +private: + virtual const char* renderName() const { return "RenderDetails"; } + virtual bool isDetails() const { return true; } + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); +}; + +inline RenderDetails* toRenderDetails(RenderObject* object) +{ + ASSERT(!object || object->isDetails()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderDetails(const RenderDetails*); + +} + +#endif // RenderDetails_h diff --git a/Source/WebCore/rendering/RenderDetailsMarker.cpp b/Source/WebCore/rendering/RenderDetailsMarker.cpp new file mode 100644 index 0000000..26e49d9 --- /dev/null +++ b/Source/WebCore/rendering/RenderDetailsMarker.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderDetailsMarker.h" + +namespace WebCore { + +RenderDetailsMarker::RenderDetailsMarker(Node* element) + : RenderBlock(element) +{ +} + +} diff --git a/Source/WebCore/rendering/RenderDetailsMarker.h b/Source/WebCore/rendering/RenderDetailsMarker.h new file mode 100644 index 0000000..08bdbd8 --- /dev/null +++ b/Source/WebCore/rendering/RenderDetailsMarker.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderDetailsMarker_h +#define RenderDetailsMarker_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderDetailsMarker : public RenderBlock { +public: + explicit RenderDetailsMarker(Node*); + +private: + virtual const char* renderName() const { return "RenderDetailsMarker"; } + virtual bool isDetailsMarker() const { return true; } +}; + +inline RenderDetailsMarker* toRenderDetailsMarker(RenderObject* object) +{ + ASSERT(!object || object->isDetails()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderDetailsMarker(const RenderDetailsMarker*); + +} + +#endif // RenderDetailsMarker_h + diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.cpp b/Source/WebCore/rendering/RenderEmbeddedObject.cpp new file mode 100644 index 0000000..fa31ddf --- /dev/null +++ b/Source/WebCore/rendering/RenderEmbeddedObject.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderEmbeddedObject.h" + +#include "Chrome.h" +#include "ChromeClient.h" +#include "CSSValueKeywords.h" +#include "Font.h" +#include "FontSelector.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "GraphicsContext.h" +#include "HTMLEmbedElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParamElement.h" +#include "LocalizedStrings.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "Page.h" +#include "Path.h" +#include "PluginViewBase.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "RenderWidgetProtector.h" +#include "Settings.h" +#include "Text.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLVideoElement.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +static const float replacementTextRoundedRectHeight = 18; +static const float replacementTextRoundedRectLeftRightTextMargin = 6; +static const float replacementTextRoundedRectOpacity = 0.20f; +static const float replacementTextPressedRoundedRectOpacity = 0.65f; +static const float replacementTextRoundedRectRadius = 5; +static const float replacementTextTextOpacity = 0.55f; +static const float replacementTextPressedTextOpacity = 0.65f; + +static const Color& replacementTextRoundedRectPressedColor() +{ + static const Color lightGray(205, 205, 205); + return lightGray; +} + +RenderEmbeddedObject::RenderEmbeddedObject(Element* element) + : RenderPart(element) + , m_hasFallbackContent(false) + , m_showsMissingPluginIndicator(false) + , m_missingPluginIndicatorIsPressed(false) + , m_mouseDownWasInMissingPluginIndicator(false) +{ + view()->frameView()->setIsVisuallyNonEmpty(); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (element->hasTagName(videoTag) || element->hasTagName(audioTag)) + setHasIntrinsicSize(); +#endif +} + +RenderEmbeddedObject::~RenderEmbeddedObject() +{ + if (frameView()) + frameView()->removeWidgetToUpdate(this); +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderEmbeddedObject::requiresLayer() const +{ + if (RenderPart::requiresLayer()) + return true; + + return allowsAcceleratedCompositing(); +} + +bool RenderEmbeddedObject::allowsAcceleratedCompositing() const +{ + return widget() && widget()->isPluginViewBase() && static_cast(widget())->platformLayer(); +} +#endif + +void RenderEmbeddedObject::setShowsMissingPluginIndicator() +{ + ASSERT(m_replacementText.isEmpty()); + m_replacementText = missingPluginText(); + m_showsMissingPluginIndicator = true; +} + +void RenderEmbeddedObject::setShowsCrashedPluginIndicator() +{ + ASSERT(m_replacementText.isEmpty()); + m_replacementText = crashedPluginText(); +} + +bool RenderEmbeddedObject::pluginCrashedOrWasMissing() const +{ + return !m_replacementText.isNull(); +} + +void RenderEmbeddedObject::setMissingPluginIndicatorIsPressed(bool pressed) +{ + if (m_missingPluginIndicatorIsPressed == pressed) + return; + + m_missingPluginIndicatorIsPressed = pressed; + repaint(); +} + +void RenderEmbeddedObject::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (pluginCrashedOrWasMissing()) { + RenderReplaced::paint(paintInfo, tx, ty); + return; + } + + RenderPart::paint(paintInfo, tx, ty); +} + +void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + if (!pluginCrashedOrWasMissing()) + return; + + if (paintInfo.phase == PaintPhaseSelection) + return; + + GraphicsContext* context = paintInfo.context; + if (context->paintingDisabled()) + return; + + FloatRect contentRect; + Path path; + FloatRect replacementTextRect; + Font font; + TextRun run(""); + float textWidth; + if (!getReplacementTextGeometry(tx, ty, contentRect, path, replacementTextRect, font, run, textWidth)) + return; + + context->save(); + context->clip(contentRect); + context->setAlpha(m_missingPluginIndicatorIsPressed ? replacementTextPressedRoundedRectOpacity : replacementTextRoundedRectOpacity); + context->setFillColor(m_missingPluginIndicatorIsPressed ? replacementTextRoundedRectPressedColor() : Color::white, style()->colorSpace()); + context->fillPath(path); + + float labelX = roundf(replacementTextRect.location().x() + (replacementTextRect.size().width() - textWidth) / 2); + float labelY = roundf(replacementTextRect.location().y() + (replacementTextRect.size().height() - font.height()) / 2 + font.ascent()); + context->setAlpha(m_missingPluginIndicatorIsPressed ? replacementTextPressedTextOpacity : replacementTextTextOpacity); + context->setFillColor(Color::black, style()->colorSpace()); + context->drawBidiText(font, run, FloatPoint(labelX, labelY)); + context->restore(); +} + +bool RenderEmbeddedObject::getReplacementTextGeometry(int tx, int ty, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, Font& font, TextRun& run, float& textWidth) +{ + contentRect = contentBoxRect(); + contentRect.move(tx, ty); + + FontDescription fontDescription; + RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); + fontDescription.setWeight(FontWeightBold); + Settings* settings = document()->settings(); + ASSERT(settings); + if (!settings) + return false; + fontDescription.setRenderingMode(settings->fontRenderingMode()); + fontDescription.setComputedSize(fontDescription.specifiedSize()); + font = Font(fontDescription, 0, 0); + font.update(0); + + run = TextRun(m_replacementText.characters(), m_replacementText.length()); + run.disableRoundingHacks(); + textWidth = font.floatWidth(run); + + replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); + float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); + float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); + replacementTextRect.setLocation(FloatPoint(x, y)); + + path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); + + return true; +} + +void RenderEmbeddedObject::layout() +{ + ASSERT(needsLayout()); + + computeLogicalWidth(); + computeLogicalHeight(); + + RenderPart::layout(); + + m_overflow.clear(); + addShadowOverflow(); + + updateLayerTransform(); + + if (!widget() && frameView()) + frameView()->addWidgetToUpdate(this); + + setNeedsLayout(false); +} + +void RenderEmbeddedObject::viewCleared() +{ + // This is required for elements whose contents are rendered by WebCore (e.g. src="foo.html"). + if (node() && widget() && widget()->isFrameView()) { + FrameView* view = static_cast(widget()); + int marginWidth = -1; + int marginHeight = -1; + if (node()->hasTagName(iframeTag)) { + HTMLIFrameElement* frame = static_cast(node()); + marginWidth = frame->marginWidth(); + marginHeight = frame->marginHeight(); + } + if (marginWidth != -1) + view->setMarginWidth(marginWidth); + if (marginHeight != -1) + view->setMarginHeight(marginHeight); + } +} + +bool RenderEmbeddedObject::isInMissingPluginIndicator(MouseEvent* event) +{ + FloatRect contentRect; + Path path; + FloatRect replacementTextRect; + Font font; + TextRun run(""); + float textWidth; + if (!getReplacementTextGeometry(0, 0, contentRect, path, replacementTextRect, font, run, textWidth)) + return false; + + return path.contains(absoluteToLocal(event->absoluteLocation(), false, true)); +} + +void RenderEmbeddedObject::handleMissingPluginIndicatorEvent(Event* event) +{ + if (Page* page = document()->page()) { + if (!page->chrome()->client()->shouldMissingPluginMessageBeButton()) + return; + } + + if (!event->isMouseEvent()) + return; + + MouseEvent* mouseEvent = static_cast(event); + HTMLPlugInElement* element = static_cast(node()); + if (event->type() == eventNames().mousedownEvent && static_cast(event)->button() == LeftButton) { + m_mouseDownWasInMissingPluginIndicator = isInMissingPluginIndicator(mouseEvent); + if (m_mouseDownWasInMissingPluginIndicator) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(element); + element->setIsCapturingMouseEvents(true); + } + setMissingPluginIndicatorIsPressed(true); + } + event->setDefaultHandled(); + } + if (event->type() == eventNames().mouseupEvent && static_cast(event)->button() == LeftButton) { + if (m_missingPluginIndicatorIsPressed) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + element->setIsCapturingMouseEvents(false); + } + setMissingPluginIndicatorIsPressed(false); + } + if (m_mouseDownWasInMissingPluginIndicator && isInMissingPluginIndicator(mouseEvent)) { + if (Page* page = document()->page()) + page->chrome()->client()->missingPluginButtonClicked(element); + } + m_mouseDownWasInMissingPluginIndicator = false; + event->setDefaultHandled(); + } + if (event->type() == eventNames().mousemoveEvent) { + setMissingPluginIndicatorIsPressed(m_mouseDownWasInMissingPluginIndicator && isInMissingPluginIndicator(mouseEvent)); + event->setDefaultHandled(); + } +} + +} diff --git a/Source/WebCore/rendering/RenderEmbeddedObject.h b/Source/WebCore/rendering/RenderEmbeddedObject.h new file mode 100644 index 0000000..8d09ede --- /dev/null +++ b/Source/WebCore/rendering/RenderEmbeddedObject.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderEmbeddedObject_h +#define RenderEmbeddedObject_h + +#include "RenderPart.h" + +namespace WebCore { + +class MouseEvent; + +// Renderer for embeds and objects, often, but not always, rendered via plug-ins. +// For example, does not invoke a plug-in. +class RenderEmbeddedObject : public RenderPart { +public: + RenderEmbeddedObject(Element*); + virtual ~RenderEmbeddedObject(); + + bool pluginCrashedOrWasMissing() const; + + void setShowsMissingPluginIndicator(); + void setShowsCrashedPluginIndicator(); + bool showsMissingPluginIndicator() const { return m_showsMissingPluginIndicator; } + + // FIXME: This belongs on HTMLObjectElement. + bool hasFallbackContent() const { return m_hasFallbackContent; } + void setHasFallbackContent(bool hasFallbackContent) { m_hasFallbackContent = hasFallbackContent; } + + void handleMissingPluginIndicatorEvent(Event*); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool allowsAcceleratedCompositing() const; +#endif + +private: + virtual const char* renderName() const { return "RenderEmbeddedObject"; } + virtual bool isEmbeddedObject() const { return true; } + + virtual void paintReplaced(PaintInfo&, int, int); + virtual void paint(PaintInfo& paintInfo, int, int); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool requiresLayer() const; +#endif + + virtual void layout(); + virtual void viewCleared(); + + void setMissingPluginIndicatorIsPressed(bool); + bool isInMissingPluginIndicator(MouseEvent*); + bool getReplacementTextGeometry(int tx, int ty, FloatRect& contentRect, Path&, FloatRect& replacementTextRect, Font&, TextRun&, float& textWidth); + + String m_replacementText; + bool m_hasFallbackContent; // FIXME: This belongs on HTMLObjectElement. + bool m_showsMissingPluginIndicator; + bool m_missingPluginIndicatorIsPressed; + bool m_mouseDownWasInMissingPluginIndicator; +}; + +inline RenderEmbeddedObject* toRenderEmbeddedObject(RenderObject* object) +{ + ASSERT(!object || !strcmp(object->renderName(), "RenderEmbeddedObject")); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderEmbeddedObject(const RenderEmbeddedObject*); + +} // namespace WebCore + +#endif // RenderEmbeddedObject_h diff --git a/Source/WebCore/rendering/RenderFieldset.cpp b/Source/WebCore/rendering/RenderFieldset.cpp new file mode 100644 index 0000000..c83396c --- /dev/null +++ b/Source/WebCore/rendering/RenderFieldset.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderFieldset.h" + +#include "CSSPropertyNames.h" +#include "HTMLNames.h" +#include "GraphicsContext.h" + +#if ENABLE(WML) +#include "WMLNames.h" +#endif + +using std::min; +using std::max; + +namespace WebCore { + +using namespace HTMLNames; + +RenderFieldset::RenderFieldset(Node* element) + : RenderBlock(element) +{ +} + +void RenderFieldset::computePreferredLogicalWidths() +{ + RenderBlock::computePreferredLogicalWidths(); + if (RenderBox* legend = findLegend()) { + int legendMinWidth = legend->minPreferredLogicalWidth(); + + Length legendMarginLeft = legend->style()->marginLeft(); + Length legendMarginRight = legend->style()->marginLeft(); + + if (legendMarginLeft.isFixed()) + legendMinWidth += legendMarginLeft.value(); + + if (legendMarginRight.isFixed()) + legendMinWidth += legendMarginRight.value(); + + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, legendMinWidth + borderAndPaddingWidth()); + } +} + +RenderObject* RenderFieldset::layoutSpecialExcludedChild(bool relayoutChildren) +{ + RenderBox* legend = findLegend(); + if (legend) { + if (relayoutChildren) + legend->setNeedsLayout(true); + legend->layoutIfNeeded(); + + int logicalLeft; + if (style()->isLeftToRightDirection()) { + switch (legend->style()->textAlign()) { + case CENTER: + logicalLeft = (logicalWidth() - logicalWidthForChild(legend)) / 2; + break; + case RIGHT: + logicalLeft = logicalWidth() - borderEnd() - paddingEnd() - logicalWidthForChild(legend); + break; + default: + logicalLeft = borderStart() + paddingStart() + marginStartForChild(legend); + break; + } + } else { + switch (legend->style()->textAlign()) { + case LEFT: + logicalLeft = borderStart() + paddingStart(); + break; + case CENTER: { + // Make sure that the extra pixel goes to the end side in RTL (since it went to the end side + // in LTR). + int centeredWidth = logicalWidth() - logicalWidthForChild(legend); + logicalLeft = centeredWidth - centeredWidth / 2; + break; + } + default: + logicalLeft = logicalWidth() - borderStart() - paddingStart() - marginStartForChild(legend) - logicalWidthForChild(legend); + break; + } + } + + setLogicalLeftForChild(legend, logicalLeft); + + int b = borderBefore(); + int h = logicalHeightForChild(legend); + setLogicalTopForChild(legend, max((b - h) / 2, 0)); + setLogicalHeight(max(b, h) + paddingBefore()); + } + return legend; +} + +RenderBox* RenderFieldset::findLegend() const +{ + for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) { + if (!legend->isFloatingOrPositioned() && legend->node() && + (legend->node()->hasTagName(legendTag) +#if ENABLE(WML) + || legend->node()->hasTagName(WMLNames::insertedLegendTag) +#endif + ) + ) + return toRenderBox(legend); + } + return 0; +} + +void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + if (!paintInfo.shouldPaintWithinRoot(this)) + return; + + int w = width(); + int h = height(); + RenderBox* legend = findLegend(); + if (!legend) + return RenderBlock::paintBoxDecorations(paintInfo, tx, ty); + + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; + h -= yOff; + ty += yOff; + } else { + int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2; + w -= xOff; + tx += xOff; + } + + paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal); + + paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, w, h); + paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset); + + if (!style()->hasBorder()) + return; + + // Create a clipping region around the legend and paint the border as normal + GraphicsContext* graphicsContext = paintInfo.context; + graphicsContext->save(); + + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int clipTop = ty; + int clipHeight = max(static_cast(style()->borderTopWidth()), legend->height()); + graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop, legend->width(), clipHeight)); + } else { + int clipLeft = tx; + int clipWidth = max(static_cast(style()->borderLeftWidth()), legend->width()); + graphicsContext->clipOut(IntRect(clipLeft, ty + legend->y(), clipWidth, legend->height())); + } + + paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true); + + graphicsContext->restore(); +} + +void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + int w = width(); + int h = height(); + RenderBox* legend = findLegend(); + if (!legend) + return RenderBlock::paintMask(paintInfo, tx, ty); + + // FIXME: We need to work with "rl" and "bt" block flow directions. In those + // cases the legend is embedded in the right and bottom borders respectively. + // https://bugs.webkit.org/show_bug.cgi?id=47236 + if (style()->isHorizontalWritingMode()) { + int yOff = (legend->y() > 0) ? 0 : (legend->height() - borderTop()) / 2; + h -= yOff; + ty += yOff; + } else { + int xOff = (legend->x() > 0) ? 0 : (legend->width() - borderLeft()) / 2; + w -= xOff; + tx += xOff; + } + + paintMaskImages(paintInfo, tx, ty, w, h); +} + +void RenderFieldset::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + // WinIE renders fieldsets with display:inline like they're inline-blocks. For us, + // an inline-block is just a block element with replaced set to true and inline set + // to true. Ensure that if we ended up being inline that we set our replaced flag + // so that we're treated like an inline-block. + if (isInline()) + setReplaced(true); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFieldset.h b/Source/WebCore/rendering/RenderFieldset.h new file mode 100644 index 0000000..b340794 --- /dev/null +++ b/Source/WebCore/rendering/RenderFieldset.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderFieldset_h +#define RenderFieldset_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderFieldset : public RenderBlock { +public: + explicit RenderFieldset(Node*); + + RenderBox* findLegend() const; + +private: + virtual const char* renderName() const { return "RenderFieldSet"; } + virtual bool isFieldset() const { return true; } + + virtual RenderObject* layoutSpecialExcludedChild(bool relayoutChildren); + + virtual void computePreferredLogicalWidths(); + virtual bool avoidsFloats() const { return true; } + virtual bool stretchesToMinIntrinsicLogicalWidth() const { return true; } + + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo&, int tx, int ty); +}; + +inline RenderFieldset* toRenderFieldset(RenderObject* object) +{ + ASSERT(!object || object->isFieldset()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderFieldset(const RenderFieldset*); + +} // namespace WebCore + +#endif // RenderFieldset_h diff --git a/Source/WebCore/rendering/RenderFileUploadControl.cpp b/Source/WebCore/rendering/RenderFileUploadControl.cpp new file mode 100644 index 0000000..3c10f43 --- /dev/null +++ b/Source/WebCore/rendering/RenderFileUploadControl.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderFileUploadControl.h" + +#include "Chrome.h" +#include "FileList.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "ShadowElement.h" +#include "Icon.h" +#include "LocalizedStrings.h" +#include "Page.h" +#include "RenderButton.h" +#include "RenderText.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int afterButtonSpacing = 4; +const int iconHeight = 16; +const int iconWidth = 16; +const int iconFilenameSpacing = 2; +const int defaultWidthNumChars = 34; +const int buttonShadowHeight = 2; + +RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input) + : RenderBlock(input) +{ + FileList* list = input->files(); + Vector filenames; + unsigned length = list ? list->length() : 0; + for (unsigned i = 0; i < length; ++i) + filenames.append(list->item(i)->path()); + m_fileChooser = FileChooser::create(this, filenames); +} + +RenderFileUploadControl::~RenderFileUploadControl() +{ + if (m_button) + m_button->detach(); + m_fileChooser->disconnectClient(); +} + +void RenderFileUploadControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + if (m_button) + m_button->renderer()->setStyle(createButtonStyle(style())); + + setReplaced(isInline()); +} + +void RenderFileUploadControl::valueChanged() +{ + // dispatchFormControlChangeEvent may destroy this renderer + RefPtr fileChooser = m_fileChooser; + + HTMLInputElement* inputElement = static_cast(node()); + inputElement->setFileListFromRenderer(fileChooser->filenames()); + inputElement->dispatchFormControlChangeEvent(); + + // only repaint if it doesn't seem we have been destroyed + if (!fileChooser->disconnected()) + repaint(); +} + +bool RenderFileUploadControl::allowsMultipleFiles() +{ +#if ENABLE(DIRECTORY_UPLOAD) + if (allowsDirectoryUpload()) + return true; +#endif + + HTMLInputElement* input = static_cast(node()); + return input->fastHasAttribute(multipleAttr); +} + +#if ENABLE(DIRECTORY_UPLOAD) +bool RenderFileUploadControl::allowsDirectoryUpload() +{ + HTMLInputElement* input = static_cast(node()); + return input->fastHasAttribute(webkitdirectoryAttr); +} +#endif + +String RenderFileUploadControl::acceptTypes() +{ + return static_cast(node())->accept(); +} + +void RenderFileUploadControl::chooseIconForFiles(FileChooser* chooser, const Vector& filenames) +{ + if (Chrome* chromePointer = chrome()) + chromePointer->chooseIconForFiles(filenames, chooser); +} + +void RenderFileUploadControl::click() +{ + // Requires a user gesture to open the file dialog. + if (!frame() || !frame()->loader()->isProcessingUserGesture()) + return; + if (Chrome* chromePointer = chrome()) + chromePointer->runOpenPanel(frame(), m_fileChooser); +} + +Chrome* RenderFileUploadControl::chrome() const +{ + Frame* frame = node()->document()->frame(); + if (!frame) + return 0; + Page* page = frame->page(); + if (!page) + return 0; + return page->chrome(); +} + +void RenderFileUploadControl::updateFromElement() +{ + HTMLInputElement* inputElement = static_cast(node()); + ASSERT(inputElement->isFileUpload()); + + if (!m_button) { + m_button = ShadowInputElement::create(inputElement); + m_button->setType("button"); + m_button->setValue(fileButtonChooseFileLabel()); + RefPtr buttonStyle = createButtonStyle(style()); + RenderObject* renderer = m_button->createRenderer(renderArena(), buttonStyle.get()); + m_button->setRenderer(renderer); + renderer->setStyle(buttonStyle.release()); + renderer->updateFromElement(); + m_button->setAttached(); + m_button->setInDocument(); + + addChild(renderer); + } + + m_button->setDisabled(!theme()->isEnabled(this)); + + // This only supports clearing out the files, but that's OK because for + // security reasons that's the only change the DOM is allowed to make. + FileList* files = inputElement->files(); + ASSERT(files); + if (files && files->isEmpty() && !m_fileChooser->filenames().isEmpty()) { + m_fileChooser->clear(); + repaint(); + } +} + +int RenderFileUploadControl::maxFilenameWidth() const +{ + return max(0, contentWidth() - m_button->renderBox()->width() - afterButtonSpacing + - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0)); +} + +PassRefPtr RenderFileUploadControl::createButtonStyle(const RenderStyle* parentStyle) const +{ + RefPtr style = getCachedPseudoStyle(FILE_UPLOAD_BUTTON); + if (!style) { + style = RenderStyle::create(); + if (parentStyle) + style->inheritFrom(parentStyle); + } + + // Button text will wrap on file upload controls with widths smaller than the intrinsic button width + // without this setWhiteSpace. + style->setWhiteSpace(NOWRAP); + + return style.release(); +} + +void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE) + return; + ASSERT(m_fileChooser); + + // Push a clip. + if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { + IntRect clipRect(tx + borderLeft(), ty + borderTop(), + width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight); + if (clipRect.isEmpty()) + return; + paintInfo.context->save(); + paintInfo.context->clip(clipRect); + } + + if (paintInfo.phase == PaintPhaseForeground) { + const String& displayedFilename = fileTextValue(); + unsigned length = displayedFilename.length(); + const UChar* string = displayedFilename.characters(); + TextRun textRun(string, length, false, 0, 0, !style()->isLeftToRightDirection(), style()->unicodeBidi() == Override); + + // Determine where the filename should be placed + int contentLeft = tx + borderLeft() + paddingLeft(); + int buttonAndIconWidth = m_button->renderBox()->width() + afterButtonSpacing + + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0); + int textX; + if (style()->isLeftToRightDirection()) + textX = contentLeft + buttonAndIconWidth; + else + textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun); + // We want to match the button's baseline + RenderButton* buttonRenderer = toRenderButton(m_button->renderer()); + int textY = buttonRenderer->absoluteBoundingBoxRect().y() + + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() + + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); + + paintInfo.context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace()); + + // Draw the filename + paintInfo.context->drawBidiText(style()->font(), textRun, IntPoint(textX, textY)); + + if (m_fileChooser->icon()) { + // Determine where the icon should be placed + int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2; + int iconX; + if (style()->isLeftToRightDirection()) + iconX = contentLeft + m_button->renderBox()->width() + afterButtonSpacing; + else + iconX = contentLeft + contentWidth() - m_button->renderBox()->width() - afterButtonSpacing - iconWidth; + + // Draw the file icon + m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight)); + } + } + + // Paint the children. + RenderBlock::paintObject(paintInfo, tx, ty); + + // Pop the clip. + if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) + paintInfo.context->restore(); +} + +void RenderFileUploadControl::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); + else { + // Figure out how big the filename space needs to be for a given number of characters + // (using "0" as the nominal character). + const UChar ch = '0'; + float charWidth = style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false)); + m_maxPreferredLogicalWidth = (int)ceilf(charWidth * defaultWidthNumChars); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPreferredLogicalWidth = 0; + else + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + } + + int toAdd = borderAndPaddingWidth(); + m_minPreferredLogicalWidth += toAdd; + m_maxPreferredLogicalWidth += toAdd; + + setPreferredLogicalWidthsDirty(false); +} + +void RenderFileUploadControl::receiveDroppedFiles(const Vector& paths) +{ + if (allowsMultipleFiles()) + m_fileChooser->chooseFiles(paths); + else + m_fileChooser->chooseFile(paths[0]); +} + +String RenderFileUploadControl::buttonValue() +{ + if (!m_button) + return String(); + + return m_button->value(); +} + +String RenderFileUploadControl::fileTextValue() const +{ + return m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFileUploadControl.h b/Source/WebCore/rendering/RenderFileUploadControl.h new file mode 100644 index 0000000..c96800c --- /dev/null +++ b/Source/WebCore/rendering/RenderFileUploadControl.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderFileUploadControl_h +#define RenderFileUploadControl_h + +#include "FileChooser.h" +#include "RenderBlock.h" + +namespace WebCore { + +class Chrome; +class HTMLInputElement; + +// Each RenderFileUploadControl contains a RenderButton (for opening the file chooser), and +// sufficient space to draw a file icon and filename. The RenderButton has a shadow node +// associated with it to receive click/hover events. + +class RenderFileUploadControl : public RenderBlock, private FileChooserClient { +public: + RenderFileUploadControl(HTMLInputElement*); + virtual ~RenderFileUploadControl(); + + virtual bool isFileUploadControl() const { return true; } + + void click(); + + void receiveDroppedFiles(const Vector&); + + String buttonValue(); + String fileTextValue() const; + +private: + virtual const char* renderName() const { return "RenderFileUploadControl"; } + + virtual void updateFromElement(); + virtual void computePreferredLogicalWidths(); + virtual void paintObject(PaintInfo&, int tx, int ty); + + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + + // FileChooserClient methods. + void valueChanged(); + void repaint() { RenderBlock::repaint(); } + bool allowsMultipleFiles(); +#if ENABLE(DIRECTORY_UPLOAD) + bool allowsDirectoryUpload(); +#endif + String acceptTypes(); + void chooseIconForFiles(FileChooser*, const Vector&); + + Chrome* chrome() const; + int maxFilenameWidth() const; + PassRefPtr createButtonStyle(const RenderStyle* parentStyle) const; + + RefPtr m_button; + RefPtr m_fileChooser; +}; + +inline RenderFileUploadControl* toRenderFileUploadControl(RenderObject* object) +{ + ASSERT(!object || object->isFileUploadControl()); + return static_cast(object); +} + +inline const RenderFileUploadControl* toRenderFileUploadControl(const RenderObject* object) +{ + ASSERT(!object || object->isFileUploadControl()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderFileUploadControl(const RenderFileUploadControl*); + +} // namespace WebCore + +#endif // RenderFileUploadControl_h diff --git a/Source/WebCore/rendering/RenderFlexibleBox.cpp b/Source/WebCore/rendering/RenderFlexibleBox.cpp new file mode 100644 index 0000000..5d96adb --- /dev/null +++ b/Source/WebCore/rendering/RenderFlexibleBox.cpp @@ -0,0 +1,1108 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderFlexibleBox.h" + +#include "CharacterNames.h" +#include "RenderLayer.h" +#include "RenderView.h" +#include + +#ifdef ANDROID_LAYOUT +#include "Document.h" +#include "Settings.h" +#endif + +using namespace std; + +namespace WebCore { + +class FlexBoxIterator { +public: + FlexBoxIterator(RenderFlexibleBox* parent) + { + box = parent; + if (box->style()->boxOrient() == HORIZONTAL && !box->style()->isLeftToRightDirection()) + forward = box->style()->boxDirection() != BNORMAL; + else + forward = box->style()->boxDirection() == BNORMAL; + lastOrdinal = 1; + if (!forward) { + // No choice, since we're going backwards, we have to find out the highest ordinal up front. + RenderBox* child = box->firstChildBox(); + while (child) { + if (child->style()->boxOrdinalGroup() > lastOrdinal) + lastOrdinal = child->style()->boxOrdinalGroup(); + child = child->nextSiblingBox(); + } + } + + reset(); + } + + void reset() + { + current = 0; + currentOrdinal = forward ? 0 : lastOrdinal+1; + } + + RenderBox* first() + { + reset(); + return next(); + } + + RenderBox* next() + { + do { + if (!current) { + if (forward) { + currentOrdinal++; + if (currentOrdinal > lastOrdinal) + return 0; + current = box->firstChildBox(); + } else { + currentOrdinal--; + if (currentOrdinal == 0) + return 0; + current = box->lastChildBox(); + } + } + else + current = forward ? current->nextSiblingBox() : current->previousSiblingBox(); + if (current && current->style()->boxOrdinalGroup() > lastOrdinal) + lastOrdinal = current->style()->boxOrdinalGroup(); + } while (!current || current->style()->boxOrdinalGroup() != currentOrdinal || + current->style()->visibility() == COLLAPSE); + return current; + } + +private: + RenderFlexibleBox* box; + RenderBox* current; + bool forward; + unsigned int currentOrdinal; + unsigned int lastOrdinal; +}; + +RenderFlexibleBox::RenderFlexibleBox(Node* node) +:RenderBlock(node) +{ + setChildrenInline(false); // All of our children must be block-level + m_flexingChildren = m_stretchingChildren = false; +} + +RenderFlexibleBox::~RenderFlexibleBox() +{ +} + +void RenderFlexibleBox::calcHorizontalPrefWidths() +{ + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // positioned children don't affect the minmaxwidth + if (child->isPositioned() || child->style()->visibility() == COLLAPSE) + continue; + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto and percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0, marginLeft = 0, marginRight = 0; + if (ml.isFixed()) + marginLeft += ml.value(); + if (mr.isFixed()) + marginRight += mr.value(); + margin = marginLeft + marginRight; + + m_minPreferredLogicalWidth += child->minPreferredLogicalWidth() + margin; + m_maxPreferredLogicalWidth += child->maxPreferredLogicalWidth() + margin; + } +} + +void RenderFlexibleBox::calcVerticalPrefWidths() +{ + for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { + // Positioned children and collapsed children don't affect the min/max width + if (child->isPositioned() || child->style()->visibility() == COLLAPSE) + continue; + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto/percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0; + if (ml.isFixed()) + margin += ml.value(); + if (mr.isFixed()) + margin += mr.value(); + + int w = child->minPreferredLogicalWidth() + margin; + m_minPreferredLogicalWidth = max(w, m_minPreferredLogicalWidth); + + w = child->maxPreferredLogicalWidth() + margin; + m_maxPreferredLogicalWidth = max(w, m_maxPreferredLogicalWidth); + } +} + +void RenderFlexibleBox::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); + else { + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; + + if (hasMultipleLines() || isVertical()) + calcVerticalPrefWidths(); + else + calcHorizontalPrefWidths(); + + m_maxPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); + } + + if (hasOverflowClip() && style()->overflowY() == OSCROLL) { + layer()->setHasVerticalScrollbar(true); + int scrollbarWidth = verticalScrollbarWidth(); + m_maxPreferredLogicalWidth += scrollbarWidth; + m_minPreferredLogicalWidth += scrollbarWidth; + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + } + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + } + + int borderAndPadding = borderAndPaddingLogicalWidth(); + m_minPreferredLogicalWidth += borderAndPadding; + m_maxPreferredLogicalWidth += borderAndPadding; + + setPreferredLogicalWidthsDirty(false); +} + +void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int /*pageHeight FIXME: Implement */) +{ + ASSERT(needsLayout()); + + if (!relayoutChildren && layoutOnlyPositionedObjects()) + return; + + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), hasTransform() || hasReflection() || style()->isFlippedBlocksWritingMode()); + + int previousWidth = width(); + int previousHeight = height(); + + computeLogicalWidth(); + computeLogicalHeight(); + + m_overflow.clear(); + + if (previousWidth != width() || previousHeight != height() || + (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL && + parent()->style()->boxAlign() == BSTRETCH)) + relayoutChildren = true; + +#ifdef ANDROID_LAYOUT + checkAndSetRelayoutChildren(&relayoutChildren); +#endif + setHeight(0); + + m_flexingChildren = m_stretchingChildren = false; + + initMaxMarginValues(); + + // For overflow:scroll blocks, ensure we have both scrollbars in place always. + if (scrollsOverflow()) { + if (style()->overflowX() == OSCROLL) + layer()->setHasHorizontalScrollbar(true); + if (style()->overflowY() == OSCROLL) + layer()->setHasVerticalScrollbar(true); + } + + if (isHorizontal()) + layoutHorizontalBox(relayoutChildren); + else + layoutVerticalBox(relayoutChildren); + + int oldClientAfterEdge = clientLogicalBottom(); + computeLogicalHeight(); + + if (previousHeight != height()) + relayoutChildren = true; + + layoutPositionedObjects(relayoutChildren || isRoot()); + + if (!isFloatingOrPositioned() && height() == 0) { + // We are a block with no border and padding and a computed height + // of 0. The CSS spec states that zero-height blocks collapse their margins + // together. + // When blocks are self-collapsing, we just use the top margin values and set the + // bottom margin max values to 0. This way we don't factor in the values + // twice when we collapse with our previous vertically adjacent and + // following vertically adjacent blocks. + int pos = maxPositiveMarginBefore(); + int neg = maxNegativeMarginBefore(); + if (maxPositiveMarginAfter() > pos) + pos = maxPositiveMarginAfter(); + if (maxNegativeMarginAfter() > neg) + neg = maxNegativeMarginAfter(); + setMaxMarginBeforeValues(pos, neg); + setMaxMarginAfterValues(0, 0); + } + + computeOverflow(oldClientAfterEdge); + + statePusher.pop(); + + updateLayerTransform(); + + if (view()->layoutState()->pageLogicalHeight()) + setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(y())); + + // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if + // we overflow or not. + if (hasOverflowClip()) + layer()->updateScrollInfoAfterLayout(); + + // Repaint with our new bounds if they are different from our old bounds. + repainter.repaintAfterLayout(); + + setNeedsLayout(false); +} + +// The first walk over our kids is to find out if we have any flexible children. +static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex) +{ + RenderBox* child = iterator.first(); + while (child) { + // Check to see if this child flexes. + if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) { + // We always have to lay out flexible objects again, since the flex distribution + // may have changed, and we need to reallocate space. + child->setOverrideSize(-1); + if (!relayoutChildren) + child->setChildNeedsLayout(true, false); + haveFlex = true; + unsigned int flexGroup = child->style()->boxFlexGroup(); + if (lowestFlexGroup == 0) + lowestFlexGroup = flexGroup; + if (flexGroup < lowestFlexGroup) + lowestFlexGroup = flexGroup; + if (flexGroup > highestFlexGroup) + highestFlexGroup = flexGroup; + } + child = iterator.next(); + } +} + +void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) +{ + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + int yPos = borderTop() + paddingTop(); + int xPos = borderLeft() + paddingLeft(); + bool heightSpecified = false; + int oldHeight = 0; + + int remainingSpace = 0; + + + FlexBoxIterator iterator(this); + unsigned int highestFlexGroup = 0; + unsigned int lowestFlexGroup = 0; + bool haveFlex = false; + gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); + + RenderBox* child; + + RenderBlock::startDelayUpdateScrollInfo(); + + // We do 2 passes. The first pass is simply to lay everyone out at + // their preferred widths. The second pass handles flexing the children. + do { + // Reset our height. + setHeight(yPos); + + xPos = borderLeft() + paddingLeft(); + + // Our first pass is done without flexing. We simply lay the children + // out within the box. We have to do a layout first in order to determine + // our box's intrinsic height. + int maxAscent = 0, maxDescent = 0; + child = iterator.first(); + while (child) { + // make sure we relayout children if we need it. + if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))) + child->setChildNeedsLayout(true, false); + + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + // Compute the child's vertical margins. + child->computeBlockDirectionMargins(this); + + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); + + // Now do the layout. + child->layoutIfNeeded(); + + // Update our height and overflow height. + if (style()->boxAlign() == BBASELINE) { + int ascent = child->firstLineBoxBaseline(); + if (ascent == -1) + ascent = child->height() + child->marginBottom(); + ascent += child->marginTop(); + int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent; + + // Update our maximum ascent. + maxAscent = max(maxAscent, ascent); + + // Update our maximum descent. + maxDescent = max(maxDescent, descent); + + // Now update our height. + setHeight(max(yPos + maxAscent + maxDescent, height())); + } + else + setHeight(max(height(), yPos + child->marginTop() + child->height() + child->marginBottom())); + + child = iterator.next(); + } + + if (!iterator.first() && hasLineIfEmpty()) + setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); + + setHeight(height() + toAdd); + + oldHeight = height(); + computeLogicalHeight(); + + relayoutChildren = false; + if (oldHeight != height()) + heightSpecified = true; + + // Now that our height is actually known, we can place our boxes. + m_stretchingChildren = (style()->boxAlign() == BSTRETCH); + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child->containingBlock()->insertPositionedObject(child); + if (child->style()->hasStaticX()) { + if (style()->isLeftToRightDirection()) + child->layer()->setStaticX(xPos); + else child->layer()->setStaticX(width() - xPos); + } + if (child->style()->hasStaticY()) { + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != yPos) { + child->layer()->setStaticY(yPos); + child->setChildNeedsLayout(true, false); + } + } + child = iterator.next(); + continue; + } + + // We need to see if this child's height has changed, since we make block elements + // fill the height of a containing box by default. + // Now do a layout. + int oldChildHeight = child->height(); + child->computeLogicalHeight(); + if (oldChildHeight != child->height()) + child->setChildNeedsLayout(true, false); + + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); + + child->layoutIfNeeded(); + + // We can place the child now, using our value of box-align. + xPos += child->marginLeft(); + int childY = yPos; + switch (style()->boxAlign()) { + case BCENTER: + childY += child->marginTop() + max(0, (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2); + break; + case BBASELINE: { + int ascent = child->firstLineBoxBaseline(); + if (ascent == -1) + ascent = child->height() + child->marginBottom(); + ascent += child->marginTop(); + childY += child->marginTop() + (maxAscent - ascent); + break; + } + case BEND: + childY += contentHeight() - child->marginBottom() - child->height(); + break; + default: // BSTART + childY += child->marginTop(); + break; + } + + placeChild(child, xPos, childY); + + xPos += child->width() + child->marginRight(); + + child = iterator.next(); + } + + remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; + + m_stretchingChildren = false; + if (m_flexingChildren) + haveFlex = false; // We're done. + else if (haveFlex) { + // We have some flexible objects. See if we need to grow/shrink them at all. + if (!remainingSpace) + break; + + // Allocate the remaining space among the flexible objects. If we are trying to + // grow, then we go from the lowest flex group to the highest flex group. For shrinking, + // we go from the highest flex group to the lowest group. + bool expanding = remainingSpace > 0; + unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; + unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; + for (unsigned int i = start; i <= end && remainingSpace; i++) { + // Always start off by assuming the group can get all the remaining space. + int groupRemainingSpace = remainingSpace; + do { + // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width + // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and + // computing the allowed growth before an object hits its min/max width (and thus + // forces a totalFlex recomputation). + int groupRemainingSpaceAtBeginning = groupRemainingSpace; + float totalFlex = 0.0f; + child = iterator.first(); + while (child) { + if (allowedChildFlex(child, expanding, i)) + totalFlex += child->style()->boxFlex(); + child = iterator.next(); + } + child = iterator.first(); + int spaceAvailableThisPass = groupRemainingSpace; + while (child) { + int allowedFlex = allowedChildFlex(child, expanding, i); + if (allowedFlex) { + int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex())); + spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); + } + child = iterator.next(); + } + + // The flex groups may not have any flexible objects this time around. + if (!spaceAvailableThisPass || totalFlex == 0.0f) { + // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. + groupRemainingSpace = 0; + continue; + } + + // Now distribute the space to objects. + child = iterator.first(); + while (child && spaceAvailableThisPass && totalFlex) { + if (allowedChildFlex(child, expanding, i)) { + int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex)); + if (spaceAdd) { + child->setOverrideSize(child->overrideWidth() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + } + + spaceAvailableThisPass -= spaceAdd; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + + totalFlex -= child->style()->boxFlex(); + } + child = iterator.next(); + } + if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { + // this is not advancing, avoid getting stuck by distributing the remaining pixels + child = iterator.first(); + int spaceAdd = groupRemainingSpace > 0 ? 1 : -1; + while (child && groupRemainingSpace) { + if (allowedChildFlex(child, expanding, i)) { + child->setOverrideSize(child->overrideWidth() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + } + child = iterator.next(); + } + } + } while (groupRemainingSpace); + } + + // We didn't find any children that could grow. + if (haveFlex && !m_flexingChildren) + haveFlex = false; + } + } while (haveFlex); + + m_flexingChildren = false; + + RenderBlock::finishDelayUpdateScrollInfo(); + + if (remainingSpace > 0 && ((style()->isLeftToRightDirection() && style()->boxPack() != BSTART) + || (!style()->isLeftToRightDirection() && style()->boxPack() != BEND))) { + // Children must be repositioned. + int offset = 0; + if (style()->boxPack() == BJUSTIFY) { + // Determine the total number of children. + int totalChildren = 0; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + totalChildren++; + child = iterator.next(); + } + + // Iterate over the children and space them out according to the + // justification level. + if (totalChildren > 1) { + totalChildren--; + bool firstChild = true; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + if (firstChild) { + firstChild = false; + child = iterator.next(); + continue; + } + + offset += remainingSpace/totalChildren; + remainingSpace -= (remainingSpace/totalChildren); + totalChildren--; + + placeChild(child, child->x()+offset, child->y()); + child = iterator.next(); + } + } + } else { + if (style()->boxPack() == BCENTER) + offset += remainingSpace/2; + else // END for LTR, START for RTL + offset += remainingSpace; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + placeChild(child, child->x()+offset, child->y()); + child = iterator.next(); + } + } + } + + // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of + // a height change, we revert our height back to the intrinsic height before returning. + if (heightSpecified) + setHeight(oldHeight); +} + +void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) +{ + int xPos = borderLeft() + paddingLeft(); + int yPos = borderTop() + paddingTop(); + if (!style()->isLeftToRightDirection()) + xPos = width() - paddingRight() - borderRight(); + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + bool heightSpecified = false; + int oldHeight = 0; + + int remainingSpace = 0; + + FlexBoxIterator iterator(this); + unsigned int highestFlexGroup = 0; + unsigned int lowestFlexGroup = 0; + bool haveFlex = false; + gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); + + RenderBox* child; + + // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of + // mainstream block layout); this is not really part of the XUL box model. + bool haveLineClamp = !style()->lineClamp().isNone(); + if (haveLineClamp) + applyLineClamp(iterator, relayoutChildren); + + RenderBlock::startDelayUpdateScrollInfo(); + + // We do 2 passes. The first pass is simply to lay everyone out at + // their preferred widths. The second pass handles flexing the children. + // Our first pass is done without flexing. We simply lay the children + // out within the box. + do { + setHeight(borderTop() + paddingTop()); + int minHeight = height() + toAdd; + + child = iterator.first(); + while (child) { + // make sure we relayout children if we need it. + if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) + child->setChildNeedsLayout(true, false); + + if (child->isPositioned()) { + child->containingBlock()->insertPositionedObject(child); + if (child->style()->hasStaticX()) { + if (style()->isLeftToRightDirection()) + child->layer()->setStaticX(borderLeft()+paddingLeft()); + else + child->layer()->setStaticX(borderRight()+paddingRight()); + } + if (child->style()->hasStaticY()) { + RenderLayer* childLayer = child->layer(); + if (childLayer->staticY() != height()) { + child->layer()->setStaticY(height()); + child->setChildNeedsLayout(true, false); + } + } + child = iterator.next(); + continue; + } + + // Compute the child's vertical margins. + child->computeBlockDirectionMargins(this); + + // Add in the child's marginTop to our height. + setHeight(height() + child->marginTop()); + + if (!child->needsLayout()) + child->markForPaginationRelayoutIfNeeded(); + + // Now do a layout. + child->layoutIfNeeded(); + + // We can place the child now, using our value of box-align. + int childX = borderLeft() + paddingLeft(); + switch (style()->boxAlign()) { + case BCENTER: + case BBASELINE: // Baseline just maps to center for vertical boxes + childX += child->marginLeft() + max(0, (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2); + break; + case BEND: + if (!style()->isLeftToRightDirection()) + childX += child->marginLeft(); + else + childX += contentWidth() - child->marginRight() - child->width(); + break; + default: // BSTART/BSTRETCH + if (style()->isLeftToRightDirection()) + childX += child->marginLeft(); + else + childX += contentWidth() - child->marginRight() - child->width(); + break; + } + + // Place the child. + placeChild(child, childX, height()); + setHeight(height() + child->height() + child->marginBottom()); + + child = iterator.next(); + } + + yPos = height(); + + if (!iterator.first() && hasLineIfEmpty()) + setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); + + setHeight(height() + toAdd); + + // Negative margins can cause our height to shrink below our minimal height (border/padding). + // If this happens, ensure that the computed height is increased to the minimal height. + if (height() < minHeight) + setHeight(minHeight); + + // Now we have to calc our height, so we know how much space we have remaining. + oldHeight = height(); + computeLogicalHeight(); + if (oldHeight != height()) + heightSpecified = true; + + remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos; + + if (m_flexingChildren) + haveFlex = false; // We're done. + else if (haveFlex) { + // We have some flexible objects. See if we need to grow/shrink them at all. + if (!remainingSpace) + break; + + // Allocate the remaining space among the flexible objects. If we are trying to + // grow, then we go from the lowest flex group to the highest flex group. For shrinking, + // we go from the highest flex group to the lowest group. + bool expanding = remainingSpace > 0; + unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; + unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; + for (unsigned int i = start; i <= end && remainingSpace; i++) { + // Always start off by assuming the group can get all the remaining space. + int groupRemainingSpace = remainingSpace; + do { + // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width + // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and + // computing the allowed growth before an object hits its min/max width (and thus + // forces a totalFlex recomputation). + int groupRemainingSpaceAtBeginning = groupRemainingSpace; + float totalFlex = 0.0f; + child = iterator.first(); + while (child) { + if (allowedChildFlex(child, expanding, i)) + totalFlex += child->style()->boxFlex(); + child = iterator.next(); + } + child = iterator.first(); + int spaceAvailableThisPass = groupRemainingSpace; + while (child) { + int allowedFlex = allowedChildFlex(child, expanding, i); + if (allowedFlex) { + int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex())); + spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); + } + child = iterator.next(); + } + + // The flex groups may not have any flexible objects this time around. + if (!spaceAvailableThisPass || totalFlex == 0.0f) { + // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. + groupRemainingSpace = 0; + continue; + } + + // Now distribute the space to objects. + child = iterator.first(); + while (child && spaceAvailableThisPass && totalFlex) { + if (allowedChildFlex(child, expanding, i)) { + int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex)); + if (spaceAdd) { + child->setOverrideSize(child->overrideHeight() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + } + + spaceAvailableThisPass -= spaceAdd; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + + totalFlex -= child->style()->boxFlex(); + } + child = iterator.next(); + } + if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { + // this is not advancing, avoid getting stuck by distributing the remaining pixels + child = iterator.first(); + int spaceAdd = groupRemainingSpace > 0 ? 1 : -1; + while (child && groupRemainingSpace) { + if (allowedChildFlex(child, expanding, i)) { + child->setOverrideSize(child->overrideHeight() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + } + child = iterator.next(); + } + } + } while (groupRemainingSpace); + } + + // We didn't find any children that could grow. + if (haveFlex && !m_flexingChildren) + haveFlex = false; + } + } while (haveFlex); + + RenderBlock::finishDelayUpdateScrollInfo(); + + if (style()->boxPack() != BSTART && remainingSpace > 0) { + // Children must be repositioned. + int offset = 0; + if (style()->boxPack() == BJUSTIFY) { + // Determine the total number of children. + int totalChildren = 0; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + totalChildren++; + child = iterator.next(); + } + + // Iterate over the children and space them out according to the + // justification level. + if (totalChildren > 1) { + totalChildren--; + bool firstChild = true; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + if (firstChild) { + firstChild = false; + child = iterator.next(); + continue; + } + + offset += remainingSpace/totalChildren; + remainingSpace -= (remainingSpace/totalChildren); + totalChildren--; + placeChild(child, child->x(), child->y()+offset); + child = iterator.next(); + } + } + } else { + if (style()->boxPack() == BCENTER) + offset += remainingSpace/2; + else // END + offset += remainingSpace; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + placeChild(child, child->x(), child->y()+offset); + child = iterator.next(); + } + } + } + + // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of + // a height change, we revert our height back to the intrinsic height before returning. + if (heightSpecified) + setHeight(oldHeight); +} + +void RenderFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren) +{ + int maxLineCount = 0; + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (child->isPositioned()) + continue; + + if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) + || (child->style()->height().isAuto() && child->isBlockFlow())) { + child->setChildNeedsLayout(true, false); + + // Dirty all the positioned objects. + if (child->isRenderBlock()) { + toRenderBlock(child)->markPositionedObjectsForLayout(); + toRenderBlock(child)->clearTruncation(); + } + } + child->layoutIfNeeded(); + if (child->style()->height().isAuto() && child->isBlockFlow()) + maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount()); + } + + // Get the number of lines and then alter all block flow children with auto height to use the + // specified height. We always try to leave room for at least one line. + LineClampValue lineClamp = style()->lineClamp(); + int numVisibleLines = lineClamp.isPercentage() ? max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value(); + if (numVisibleLines >= maxLineCount) + return; + + for (RenderBox* child = iterator.first(); child; child = iterator.next()) { + if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) + continue; + + RenderBlock* blockChild = toRenderBlock(child); + int lineCount = blockChild->lineCount(); + if (lineCount <= numVisibleLines) + continue; + + int newHeight = blockChild->heightForLineCount(numVisibleLines); + if (newHeight == child->height()) + continue; + + child->setChildNeedsLayout(true, false); + child->setOverrideSize(newHeight); + m_flexingChildren = true; + child->layoutIfNeeded(); + m_flexingChildren = false; + child->setOverrideSize(-1); + + // FIXME: For now don't support RTL. + if (style()->direction() != LTR) + continue; + + // Get the last line + RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1); + if (!lastLine) + continue; + + RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); + if (!lastVisibleLine) + continue; + + const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; + DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); + DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); + const Font& font = style(numVisibleLines == 1)->font(); + + // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too + int totalWidth; + InlineBox* anchorBox = lastLine->lastChild(); + if (anchorBox && anchorBox->renderer()->node() && anchorBox->renderer()->node()->isLink()) + totalWidth = anchorBox->logicalWidth() + font.width(TextRun(ellipsisAndSpace, 2)); + else { + anchorBox = 0; + totalWidth = font.width(TextRun(&horizontalEllipsis, 1)); + } + + // See if this width can be accommodated on the last visible line + RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); + RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); + + // FIXME: Directions of src/destBlock could be different from our direction and from one another. + if (!srcBlock->style()->isLeftToRightDirection()) + continue; + + bool leftToRight = destBlock->style()->isLeftToRightDirection(); + if (!leftToRight) + continue; + + int blockRightEdge = destBlock->logicalRightOffsetForLine(lastVisibleLine->y(), false); + int blockLeftEdge = destBlock->logicalLeftOffsetForLine(lastVisibleLine->y(), false); + + int blockEdge = leftToRight ? blockRightEdge : blockLeftEdge; + if (!lastVisibleLine->canAccommodateEllipsis(leftToRight, blockEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth)) + continue; + + // Let the truncation code kick in. + lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); + destBlock->setHasMarkupTruncation(true); + } +} + +void RenderFlexibleBox::placeChild(RenderBox* child, int x, int y) +{ + IntRect oldRect(child->x(), child->y() , child->width(), child->height()); + + // Place the child. + child->setLocation(x, y); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); +} + +int RenderFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group) +{ + if (child->isPositioned() || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group) + return 0; + + if (expanding) { + if (isHorizontal()) { + // FIXME: For now just handle fixed values. + int maxW = INT_MAX; + int w = child->overrideWidth() - child->borderAndPaddingWidth(); + if (!child->style()->maxWidth().isUndefined() && + child->style()->maxWidth().isFixed()) + maxW = child->style()->maxWidth().value(); + else if (child->style()->maxWidth().type() == Intrinsic) + maxW = child->maxPreferredLogicalWidth(); + else if (child->style()->maxWidth().type() == MinIntrinsic) + maxW = child->minPreferredLogicalWidth(); + if (maxW == INT_MAX) + return maxW; + return max(0, maxW - w); + } else { + // FIXME: For now just handle fixed values. + int maxH = INT_MAX; + int h = child->overrideHeight() - child->borderAndPaddingHeight(); + if (!child->style()->maxHeight().isUndefined() && + child->style()->maxHeight().isFixed()) + maxH = child->style()->maxHeight().value(); + if (maxH == INT_MAX) + return maxH; + return max(0, maxH - h); + } + } + + // FIXME: For now just handle fixed values. + if (isHorizontal()) { + int minW = child->minPreferredLogicalWidth(); + int w = child->overrideWidth() - child->borderAndPaddingWidth(); + if (child->style()->minWidth().isFixed()) + minW = child->style()->minWidth().value(); + else if (child->style()->minWidth().type() == Intrinsic) + minW = child->maxPreferredLogicalWidth(); + else if (child->style()->minWidth().type() == MinIntrinsic) + minW = child->minPreferredLogicalWidth(); + + int allowedShrinkage = min(0, minW - w); + return allowedShrinkage; + } else { + if (child->style()->minHeight().isFixed()) { + int minH = child->style()->minHeight().value(); + int h = child->overrideHeight() - child->borderAndPaddingHeight(); + int allowedShrinkage = min(0, minH - h); + return allowedShrinkage; + } + } + + return 0; +} + +const char *RenderFlexibleBox::renderName() const +{ + if (isFloating()) + return "RenderFlexibleBox (floating)"; + if (isPositioned()) + return "RenderFlexibleBox (positioned)"; + if (isAnonymous()) + return "RenderFlexibleBox (generated)"; + if (isRelPositioned()) + return "RenderFlexibleBox (relative positioned)"; + return "RenderFlexibleBox"; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFlexibleBox.h b/Source/WebCore/rendering/RenderFlexibleBox.h new file mode 100644 index 0000000..8525c29 --- /dev/null +++ b/Source/WebCore/rendering/RenderFlexibleBox.h @@ -0,0 +1,71 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 2003 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderFlexibleBox_h +#define RenderFlexibleBox_h + +#include "RenderBlock.h" + +namespace WebCore { + +class FlexBoxIterator; + +class RenderFlexibleBox : public RenderBlock { +public: + RenderFlexibleBox(Node*); + virtual ~RenderFlexibleBox(); + + virtual const char* renderName() const; + + virtual void computePreferredLogicalWidths(); + void calcHorizontalPrefWidths(); + void calcVerticalPrefWidths(); + + virtual void layoutBlock(bool relayoutChildren, int pageHeight); + void layoutHorizontalBox(bool relayoutChildren); + void layoutVerticalBox(bool relayoutChildren); + + virtual bool avoidsFloats() const { return true; } + + virtual bool isFlexibleBox() const { return true; } + virtual bool isFlexingChildren() const { return m_flexingChildren; } + virtual bool isStretchingChildren() const { return m_stretchingChildren; } + + void placeChild(RenderBox* child, int x, int y); + +protected: + int allowedChildFlex(RenderBox* child, bool expanding, unsigned group); + + bool hasMultipleLines() const { return style()->boxLines() == MULTIPLE; } + bool isVertical() const { return style()->boxOrient() == VERTICAL; } + bool isHorizontal() const { return style()->boxOrient() == HORIZONTAL; } + + bool m_flexingChildren : 1; + bool m_stretchingChildren : 1; + +private: + void applyLineClamp(FlexBoxIterator&, bool relayoutChildren); +}; + +} // namespace WebCore + +#endif // RenderFlexibleBox_h diff --git a/Source/WebCore/rendering/RenderForeignObject.cpp b/Source/WebCore/rendering/RenderForeignObject.cpp new file mode 100644 index 0000000..f9f6988 --- /dev/null +++ b/Source/WebCore/rendering/RenderForeignObject.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2009 Google, Inc. + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#if ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) +#include "RenderForeignObject.h" + +#include "GraphicsContext.h" +#include "RenderSVGResource.h" +#include "RenderView.h" +#include "SVGForeignObjectElement.h" +#include "SVGRenderSupport.h" +#include "SVGSVGElement.h" +#include "TransformState.h" + +namespace WebCore { + +RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) + : RenderSVGBlock(node) + , m_needsTransformUpdate(true) +{ +} + +RenderForeignObject::~RenderForeignObject() +{ +} + +void RenderForeignObject::paint(PaintInfo& paintInfo, int, int) +{ + if (paintInfo.context->paintingDisabled()) + return; + + PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + childPaintInfo.applyTransform(localTransform()); + + if (SVGRenderSupport::isOverflowHidden(this)) + childPaintInfo.context->clip(m_viewport); + + float opacity = style()->opacity(); + if (opacity < 1.0f) + childPaintInfo.context->beginTransparencyLayer(opacity); + + RenderBlock::paint(childPaintInfo, 0, 0); + + if (opacity < 1.0f) + childPaintInfo.context->endTransparencyLayer(); + + childPaintInfo.context->restore(); +} + +IntRect RenderForeignObject::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) +{ + return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer); +} + +void RenderForeignObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed) +{ + SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed); +} + +const AffineTransform& RenderForeignObject::localToParentTransform() const +{ + m_localToParentTransform = localTransform(); + m_localToParentTransform.translate(m_viewport.x(), m_viewport.y()); + return m_localToParentTransform; +} + +void RenderForeignObject::computeLogicalWidth() +{ + // FIXME: Investigate in size rounding issues + setWidth(static_cast(roundf(m_viewport.width()))); +} + +void RenderForeignObject::computeLogicalHeight() +{ + // FIXME: Investigate in size rounding issues + setHeight(static_cast(roundf(m_viewport.height()))); +} + +void RenderForeignObject::layout() +{ + ASSERT(needsLayout()); + ASSERT(!view()->layoutStateEnabled()); // RenderSVGRoot disables layoutState for the SVG rendering tree. + + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + SVGForeignObjectElement* foreign = static_cast(node()); + + bool updateCachedBoundariesInParents = false; + if (m_needsTransformUpdate) { + m_localTransform = foreign->animatedLocalTransform(); + m_needsTransformUpdate = false; + updateCachedBoundariesInParents = true; + } + + FloatRect oldViewport = m_viewport; + + // Cache viewport boundaries + FloatPoint viewportLocation(foreign->x().value(foreign), foreign->y().value(foreign)); + m_viewport = FloatRect(viewportLocation, FloatSize(foreign->width().value(foreign), foreign->height().value(foreign))); + if (!updateCachedBoundariesInParents) + updateCachedBoundariesInParents = oldViewport != m_viewport; + + // Set box origin to the foreignObject x/y translation, so positioned objects in XHTML content get correct + // positions. A regular RenderBoxModelObject would pull this information from RenderStyle - in SVG those + // properties are ignored for non elements, so we mimic what happens when specifying them through CSS. + + // FIXME: Investigate in location rounding issues - only affects RenderForeignObject & RenderSVGText + setLocation(roundedIntPoint(viewportLocation)); + + bool layoutChanged = m_everHadLayout && selfNeedsLayout(); + RenderBlock::layout(); + ASSERT(!needsLayout()); + + // If our bounds changed, notify the parents. + if (updateCachedBoundariesInParents) + RenderSVGBlock::setNeedsBoundariesUpdate(); + + // Invalidate all resources of this client if our layout changed. + if (layoutChanged) + SVGResourcesCache::clientLayoutChanged(this); + + repainter.repaintAfterLayout(); +} + +bool RenderForeignObject::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction) +{ + FloatPoint localPoint = localTransform().inverse().mapPoint(pointInParent); + + // Early exit if local point is not contained in clipped viewport area + if (SVGRenderSupport::isOverflowHidden(this) && !m_viewport.contains(localPoint)) + return false; + + IntPoint roundedLocalPoint = roundedIntPoint(localPoint); + return RenderBlock::nodeAtPoint(request, result, roundedLocalPoint.x(), roundedLocalPoint.y(), 0, 0, hitTestAction); +} + +bool RenderForeignObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction) +{ + ASSERT_NOT_REACHED(); + return false; +} + +void RenderForeignObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const +{ + // When crawling up the hierachy starting from foreignObject child content, useTransforms may not be set to true. + if (!useTransforms) + useTransforms = true; + SVGRenderSupport::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState); +} + +} + +#endif diff --git a/Source/WebCore/rendering/RenderForeignObject.h b/Source/WebCore/rendering/RenderForeignObject.h new file mode 100644 index 0000000..bdf96ce --- /dev/null +++ b/Source/WebCore/rendering/RenderForeignObject.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * Copyright (C) 2009 Google, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderForeignObject_h +#define RenderForeignObject_h + +#if ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) +#include "AffineTransform.h" +#include "FloatPoint.h" +#include "RenderSVGBlock.h" + +namespace WebCore { + +class SVGForeignObjectElement; + +class RenderForeignObject : public RenderSVGBlock { +public: + explicit RenderForeignObject(SVGForeignObjectElement*); + virtual ~RenderForeignObject(); + + virtual const char* renderName() const { return "RenderForeignObject"; } + + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false); + + virtual bool requiresLayer() const { return false; } + virtual void layout(); + + virtual FloatRect objectBoundingBox() const { return m_viewport; } + virtual FloatRect strokeBoundingBox() const { return m_viewport; } + virtual FloatRect repaintRectInLocalCoordinates() const { return m_viewport; } + + virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual bool isSVGForeignObject() const { return true; } + + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const; + virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } + + private: + virtual void computeLogicalWidth(); + virtual void computeLogicalHeight(); + + virtual const AffineTransform& localToParentTransform() const; + virtual AffineTransform localTransform() const { return m_localTransform; } + + bool m_needsTransformUpdate : 1; + FloatRect m_viewport; + AffineTransform m_localTransform; + mutable AffineTransform m_localToParentTransform; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/rendering/RenderFrame.cpp b/Source/WebCore/rendering/RenderFrame.cpp new file mode 100644 index 0000000..4b1444b --- /dev/null +++ b/Source/WebCore/rendering/RenderFrame.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderFrame.h" + +#include "FrameView.h" +#include "HTMLFrameElement.h" +#include "RenderView.h" + +namespace WebCore { + +RenderFrame::RenderFrame(HTMLFrameElement* frame) + : RenderFrameBase(frame) +{ + setInline(false); +} + +FrameEdgeInfo RenderFrame::edgeInfo() const +{ + HTMLFrameElement* element = static_cast(node()); + return FrameEdgeInfo(element->noResize(), element->hasFrameBorder()); +} + +void RenderFrame::viewCleared() +{ + HTMLFrameElement* element = static_cast(node()); + if (!element || !widget() || !widget()->isFrameView()) + return; + + FrameView* view = static_cast(widget()); + + int marginWidth = element->marginWidth(); + int marginHeight = element->marginHeight(); + + if (marginWidth != -1) + view->setMarginWidth(marginWidth); + if (marginHeight != -1) + view->setMarginHeight(marginHeight); +} + +#ifdef ANDROID_FLATTEN_FRAMESET +void RenderFrame::layout() +{ + FrameView* view = static_cast(widget()); + RenderView* root = view ? view->frame()->contentRenderer() : 0; + if (!width() || !height() || !root) { + setNeedsLayout(false); + return; + } + + HTMLFrameElementBase* element = static_cast(node()); + if (element->scrollingMode() == ScrollbarAlwaysOff && !root->isFrameSet()) { + setNeedsLayout(false); + return; + } + + int layoutWidth = width(); + + setWidth(max(view->contentsWidth() + borderAndPaddingWidth(), width())); + setHeight(max(view->contentsHeight() + borderAndPaddingHeight(), height())); + + // Trigger a layout of the FrameView which will schedule a relayout of this RenderFrame. + if (layoutWidth < width()) + view->layout(); + + setNeedsLayout(false); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFrame.h b/Source/WebCore/rendering/RenderFrame.h new file mode 100644 index 0000000..a7a859f --- /dev/null +++ b/Source/WebCore/rendering/RenderFrame.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderFrame_h +#define RenderFrame_h + +#include "RenderFrameBase.h" +#include "RenderFrameSet.h" + +namespace WebCore { + +class HTMLFrameElement; + +class RenderFrame : public RenderFrameBase { +public: + explicit RenderFrame(HTMLFrameElement*); + + FrameEdgeInfo edgeInfo() const; + +private: + virtual const char* renderName() const { return "RenderFrame"; } + virtual bool isFrame() const { return true; } + +#ifdef ANDROID_FLATTEN_FRAMESET + virtual void layout(); +#endif + virtual void viewCleared(); +}; + +inline RenderFrame* toRenderFrame(RenderObject* object) +{ + ASSERT(!object || object->isFrame()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderFrame(const RenderFrame*); + +} // namespace WebCore + +#endif // RenderFrame_h diff --git a/Source/WebCore/rendering/RenderFrameBase.cpp b/Source/WebCore/rendering/RenderFrameBase.cpp new file mode 100644 index 0000000..b36ad3a --- /dev/null +++ b/Source/WebCore/rendering/RenderFrameBase.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderFrameBase.h" + +#include "FrameView.h" +#include "HTMLFrameElementBase.h" +#include "RenderView.h" + +namespace WebCore { + +RenderFrameBase::RenderFrameBase(Element* element) + : RenderPart(element) +{ +} + +void RenderFrameBase::layoutWithFlattening(bool fixedWidth, bool fixedHeight) +{ + FrameView* childFrameView = static_cast(widget()); + RenderView* childRoot = childFrameView ? static_cast(childFrameView->frame()->contentRenderer()) : 0; + + // Do not expand frames which has zero width or height + if (!width() || !height() || !childRoot) { + updateWidgetPosition(); + if (childFrameView) + childFrameView->layout(); + setNeedsLayout(false); + return; + } + + // need to update to calculate min/max correctly + updateWidgetPosition(); + if (childRoot->preferredLogicalWidthsDirty()) + childRoot->computePreferredLogicalWidths(); + + // if scrollbars are off, and the width or height are fixed + // we obey them and do not expand. With frame flattening + // no subframe much ever become scrollable. + + HTMLFrameElementBase* element = static_cast(node()); + bool isScrollable = element->scrollingMode() != ScrollbarAlwaysOff; + + // consider iframe inset border + int hBorder = borderLeft() + borderRight(); + int vBorder = borderTop() + borderBottom(); + + // make sure minimum preferred width is enforced + if (isScrollable || !fixedWidth) { + setWidth(max(width(), childRoot->minPreferredLogicalWidth() + hBorder)); + // update again to pass the new width to the child frame + updateWidgetPosition(); + childFrameView->layout(); + } + + // expand the frame by setting frame height = content height + if (isScrollable || !fixedHeight || childRoot->isFrameSet()) + setHeight(max(height(), childFrameView->contentsHeight() + vBorder)); + if (isScrollable || !fixedWidth || childRoot->isFrameSet()) + setWidth(max(width(), childFrameView->contentsWidth() + hBorder)); + + updateWidgetPosition(); + + ASSERT(!childFrameView->layoutPending()); + ASSERT(!childRoot->needsLayout()); + ASSERT(!childRoot->firstChild() || !childRoot->firstChild()->firstChild() || !childRoot->firstChild()->firstChild()->needsLayout()); + + setNeedsLayout(false); +} + +} diff --git a/Source/WebCore/rendering/RenderFrameBase.h b/Source/WebCore/rendering/RenderFrameBase.h new file mode 100644 index 0000000..4fad560 --- /dev/null +++ b/Source/WebCore/rendering/RenderFrameBase.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderFrameBase_h +#define RenderFrameBase_h + +#include "RenderPart.h" + +namespace WebCore { + +// Base class for RenderFrame and RenderIFrame +class RenderFrameBase : public RenderPart { +protected: + explicit RenderFrameBase(Element*); + +public: + void layoutWithFlattening(bool fixedWidth, bool fixedHeight); +}; + +} // namespace WebCore + +#endif // RenderFrameBase_h diff --git a/Source/WebCore/rendering/RenderFrameSet.cpp b/Source/WebCore/rendering/RenderFrameSet.cpp new file mode 100644 index 0000000..600d65e --- /dev/null +++ b/Source/WebCore/rendering/RenderFrameSet.cpp @@ -0,0 +1,890 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderFrameSet.h" + +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLFrameSetElement.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "MouseEvent.h" +#include "RenderFrame.h" +#include "RenderView.h" +#include "Settings.h" + +namespace WebCore { + +RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet) + : RenderBox(frameSet) + , m_isResizing(false) + , m_isChildResizing(false) +#ifdef ANDROID_FLATTEN_FRAMESET + , m_gridCalculated(false) +#endif +{ + setInline(false); +} + +RenderFrameSet::~RenderFrameSet() +{ +} + +RenderFrameSet::GridAxis::GridAxis() + : m_splitBeingResized(noSplit) +{ +} + +inline HTMLFrameSetElement* RenderFrameSet::frameSet() const +{ + return static_cast(node()); +} + +static Color borderStartEdgeColor() +{ + return Color(170, 170, 170); +} + +static Color borderEndEdgeColor() +{ + return Color::black; +} + +static Color borderFillColor() +{ + return Color(208, 208, 208); +} + +void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect) +{ + if (!paintInfo.rect.intersects(borderRect)) + return; + + // FIXME: We should do something clever when borders from distinct framesets meet at a join. + + // Fill first. + GraphicsContext* context = paintInfo.context; + ColorSpace colorSpace = style()->colorSpace(); + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace); + + // Now stroke the edges but only if we have enough room to paint both edges with a little + // bit of the fill color showing through. + if (borderRect.width() >= 3) { + context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor(), colorSpace); + context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor(), colorSpace); + } +} + +void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect) +{ + if (!paintInfo.rect.intersects(borderRect)) + return; + + // FIXME: We should do something clever when borders from distinct framesets meet at a join. + + // Fill first. + GraphicsContext* context = paintInfo.context; + ColorSpace colorSpace = style()->colorSpace(); + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->visitedDependentColor(CSSPropertyBorderLeftColor) : borderFillColor(), colorSpace); + + // Now stroke the edges but only if we have enough room to paint both edges with a little + // bit of the fill color showing through. + if (borderRect.height() >= 3) { + context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor(), colorSpace); + context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor(), colorSpace); + } +} + +void RenderFrameSet::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase != PaintPhaseForeground) + return; + + RenderObject* child = firstChild(); + if (!child) + return; + + // Add in our offsets. + tx += x(); + ty += y(); + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + int borderThickness = frameSet()->border(); + + int yPos = 0; + for (int r = 0; r < rows; r++) { + int xPos = 0; + for (int c = 0; c < cols; c++) { + child->paint(paintInfo, tx, ty); + xPos += m_cols.m_sizes[c]; + if (borderThickness && m_cols.m_allowBorder[c + 1]) { + paintColumnBorder(paintInfo, IntRect(tx + xPos, ty + yPos, borderThickness, height())); + xPos += borderThickness; + } + child = child->nextSibling(); + if (!child) + return; + } + yPos += m_rows.m_sizes[r]; + if (borderThickness && m_rows.m_allowBorder[r + 1]) { + paintRowBorder(paintInfo, IntRect(tx, ty + yPos, width(), borderThickness)); + yPos += borderThickness; + } + } +} + +bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, + int x, int y, int tx, int ty, HitTestAction action) +{ + if (action != HitTestForeground) + return false; + + bool inside = RenderBox::nodeAtPoint(request, result, x, y, tx, ty, action) + || m_isResizing; + + if (inside && frameSet()->noResize() + && !request.readOnly() && !result.innerNode()) { + result.setInnerNode(node()); + result.setInnerNonSharedNode(node()); + } + + return inside || m_isChildResizing; +} + +void RenderFrameSet::GridAxis::resize(int size) +{ + m_sizes.resize(size); + m_deltas.resize(size); + m_deltas.fill(0); + + // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset + // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about + // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info. + m_preventResize.resize(size + 1); + m_allowBorder.resize(size + 1); +} + +void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen) +{ + availableLen = max(availableLen, 0); + + int* gridLayout = axis.m_sizes.data(); + + if (!grid) { + gridLayout[0] = availableLen; + return; + } + + int gridLen = axis.m_sizes.size(); + ASSERT(gridLen); + + int totalRelative = 0; + int totalFixed = 0; + int totalPercent = 0; + int countRelative = 0; + int countFixed = 0; + int countPercent = 0; + + // First we need to investigate how many columns of each type we have and + // how much space these columns are going to require. + for (int i = 0; i < gridLen; ++i) { + // Count the total length of all of the fixed columns/rows -> totalFixed + // Count the number of columns/rows which are fixed -> countFixed + if (grid[i].isFixed()) { + gridLayout[i] = max(grid[i].value(), 0); + totalFixed += gridLayout[i]; + countFixed++; + } + + // Count the total percentage of all of the percentage columns/rows -> totalPercent + // Count the number of columns/rows which are percentages -> countPercent + if (grid[i].isPercent()) { + gridLayout[i] = max(grid[i].calcValue(availableLen), 0); + totalPercent += gridLayout[i]; + countPercent++; + } + + // Count the total relative of all the relative columns/rows -> totalRelative + // Count the number of columns/rows which are relative -> countRelative + if (grid[i].isRelative()) { + totalRelative += max(grid[i].value(), 1); + countRelative++; + } + } + + int remainingLen = availableLen; + + // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed + // columns/rows we need to proportionally adjust their size. + if (totalFixed > remainingLen) { + int remainingFixed = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed; + remainingLen -= gridLayout[i]; + } + } + } else + remainingLen -= totalFixed; + + // Percentage columns/rows are our second priority. Divide the remaining space proportionally + // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative + // to 100%, but to the total percentage. For example, if there are three columns, each of 75%, + // and the available space is 300px, each column will become 100px in width. + if (totalPercent > remainingLen) { + int remainingPercent = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent; + remainingLen -= gridLayout[i]; + } + } + } else + remainingLen -= totalPercent; + + // Relative columns/rows are our last priority. Divide the remaining space proportionally + // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*. + if (countRelative) { + int lastRelative = 0; + int remainingRelative = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isRelative()) { + gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative; + remainingLen -= gridLayout[i]; + lastRelative = i; + } + } + + // If we could not evenly distribute the available space of all of the relative + // columns/rows, the remainder will be added to the last column/row. + // For example: if we have a space of 100px and three columns (*,*,*), the remainder will + // be 1px and will be added to the last column: 33px, 33px, 34px. + if (remainingLen) { + gridLayout[lastRelative] += remainingLen; + remainingLen = 0; + } + } + + // If we still have some left over space we need to divide it over the already existing + // columns/rows + if (remainingLen) { + // Our first priority is to spread if over the percentage columns. The remaining + // space is spread evenly, for example: if we have a space of 100px, the columns + // definition of 25%,25% used to result in two columns of 25px. After this the + // columns will each be 50px in width. + if (countPercent && totalPercent) { + int remainingPercent = remainingLen; + int changePercent = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + changePercent = (remainingPercent * gridLayout[i]) / totalPercent; + gridLayout[i] += changePercent; + remainingLen -= changePercent; + } + } + } else if (totalFixed) { + // Our last priority is to spread the remaining space over the fixed columns. + // For example if we have 100px of space and two column of each 40px, both + // columns will become exactly 50px. + int remainingFixed = remainingLen; + int changeFixed = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + changeFixed = (remainingFixed * gridLayout[i]) / totalFixed; + gridLayout[i] += changeFixed; + remainingLen -= changeFixed; + } + } + } + } + + // If we still have some left over space we probably ended up with a remainder of + // a division. We cannot spread it evenly anymore. If we have any percentage + // columns/rows simply spread the remainder equally over all available percentage columns, + // regardless of their size. + if (remainingLen && countPercent) { + int remainingPercent = remainingLen; + int changePercent = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + changePercent = remainingPercent / countPercent; + gridLayout[i] += changePercent; + remainingLen -= changePercent; + } + } + } + + // If we don't have any percentage columns/rows we only have fixed columns. Spread + // the remainder equally over all fixed columns/rows. + else if (remainingLen && countFixed) { + int remainingFixed = remainingLen; + int changeFixed = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + changeFixed = remainingFixed / countFixed; + gridLayout[i] += changeFixed; + remainingLen -= changeFixed; + } + } + } + + // Still some left over. Add it to the last column, because it is impossible + // spread it evenly or equally. + if (remainingLen) + gridLayout[gridLen - 1] += remainingLen; + + // now we have the final layout, distribute the delta over it + bool worked = true; + int* gridDelta = axis.m_deltas.data(); + for (int i = 0; i < gridLen; ++i) { + if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0) + worked = false; + gridLayout[i] += gridDelta[i]; + } + // if the deltas broke something, undo them + if (!worked) { + for (int i = 0; i < gridLen; ++i) + gridLayout[i] -= gridDelta[i]; + axis.m_deltas.fill(0); + } +} + +void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c) +{ + if (edgeInfo.allowBorder(LeftFrameEdge)) + m_cols.m_allowBorder[c] = true; + if (edgeInfo.allowBorder(RightFrameEdge)) + m_cols.m_allowBorder[c + 1] = true; + if (edgeInfo.preventResize(LeftFrameEdge)) + m_cols.m_preventResize[c] = true; + if (edgeInfo.preventResize(RightFrameEdge)) + m_cols.m_preventResize[c + 1] = true; + + if (edgeInfo.allowBorder(TopFrameEdge)) + m_rows.m_allowBorder[r] = true; + if (edgeInfo.allowBorder(BottomFrameEdge)) + m_rows.m_allowBorder[r + 1] = true; + if (edgeInfo.preventResize(TopFrameEdge)) + m_rows.m_preventResize[r] = true; + if (edgeInfo.preventResize(BottomFrameEdge)) + m_rows.m_preventResize[r + 1] = true; +} + +void RenderFrameSet::computeEdgeInfo() +{ + m_rows.m_preventResize.fill(frameSet()->noResize()); + m_rows.m_allowBorder.fill(false); + m_cols.m_preventResize.fill(frameSet()->noResize()); + m_cols.m_allowBorder.fill(false); + + RenderObject* child = firstChild(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + FrameEdgeInfo edgeInfo; + if (child->isFrameSet()) + edgeInfo = toRenderFrameSet(child)->edgeInfo(); + else + edgeInfo = toRenderFrame(child)->edgeInfo(); + fillFromEdgeInfo(edgeInfo, r, c); + child = child->nextSibling(); + if (!child) + return; + } + } +} + +FrameEdgeInfo RenderFrameSet::edgeInfo() const +{ + FrameEdgeInfo result(frameSet()->noResize(), true); + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + if (rows && cols) { + result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]); + result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]); + result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]); + result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]); + result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]); + result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]); + result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]); + result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]); + } + + return result; +} + +void RenderFrameSet::layout() +{ + ASSERT(needsLayout()); + + bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout(); + IntRect oldBounds; + if (doFullRepaint) + oldBounds = absoluteClippedOverflowRect(); + + if (!parent()->isFrameSet() && !document()->printing()) { +#ifdef ANDROID_FLATTEN_FRAMESET + // Force a grid recalc. + m_gridCalculated = false; +#endif + setWidth(view()->viewWidth()); + setHeight(view()->viewHeight()); + } + + size_t cols = frameSet()->totalCols(); + size_t rows = frameSet()->totalRows(); + + if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) { + m_rows.resize(rows); + m_cols.resize(cols); +#ifdef ANDROID_FLATTEN_FRAMESET + m_gridCalculated = false; +#endif + } + +#ifdef ANDROID_FLATTEN_FRAMESET + if (!m_gridCalculated) { + m_gridCalculated = true; + // Make all the child framesets recalculate their grid. + RenderObject* child = firstChild(); + for (; child; child = child->nextSibling()) { + if (child->isFrameSet()) + static_cast(child)->setGridNeedsLayout(); + } +#endif + int borderThickness = frameSet()->border(); + layOutAxis(m_rows, frameSet()->rowLengths(), height() - (rows - 1) * borderThickness); + layOutAxis(m_cols, frameSet()->colLengths(), width() - (cols - 1) * borderThickness); +#ifdef ANDROID_FLATTEN_FRAMESET + } +#endif + + if (flattenFrameSet()) + positionFramesWithFlattening(); + else + positionFrames(); + + RenderBox::layout(); + + computeEdgeInfo(); + + if (doFullRepaint) { + view()->repaintViewRectangle(oldBounds); + IntRect newBounds = absoluteClippedOverflowRect(); + if (newBounds != oldBounds) + view()->repaintViewRectangle(newBounds); + } + + setNeedsLayout(false); +} + +void RenderFrameSet::positionFrames() +{ + RenderBox* child = firstChildBox(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + + int yPos = 0; + int borderThickness = frameSet()->border(); +#ifdef ANDROID_FLATTEN_FRAMESET + // Keep track of the maximum width of a row which will become the maximum width of the frameset. + int maxWidth = 0; + const Length* rowLengths = frameSet()->rowLengths(); + const Length* colLengths = frameSet()->colLengths(); + + for (int r = 0; r < rows && child; r++) { + int xPos = 0; + int height = m_rows.m_sizes[r]; + int rowHeight = -1; + if (rowLengths) { + Length l = rowLengths[r]; + if (l.isFixed()) + rowHeight = l.value(); + } + for (int c = 0; c < cols && child; c++) { + child->setX(xPos); + child->setY(yPos); + child->setWidth(m_cols.m_sizes[c]); + child->setHeight(height); + int colWidth = -1; + if (colLengths) { + Length l = colLengths[c]; + if (l.isFixed()) + colWidth = l.value(); + } + if (colWidth && rowHeight) { + child->setNeedsLayout(true); + child->layout(); + } else { + child->layoutIfNeeded(); + } + + ASSERT(child->width() >= m_cols.m_sizes[c]); + m_cols.m_sizes[c] = child->width(); + + height = max(child->height(), height); + xPos += child->width() + borderThickness; + child = (RenderBox*)child->nextSibling(); + } + ASSERT(height >= m_rows.m_sizes[r]); + m_rows.m_sizes[r] = height; + maxWidth = max(xPos, maxWidth); + yPos += height + borderThickness; + } + + // Compute a new width and height according to the positioning of each expanded child frame. + // Note: we subtract borderThickness because we only count borders between frames. + int newWidth = maxWidth - borderThickness; + int newHeight = yPos - borderThickness; + + // Distribute the extra width and height evenly across the grid. + int dWidth = (width() - newWidth) / cols; + int dHeight = (height() - newHeight) / rows; + if (dWidth > 0) { + int availableWidth = width() - (cols - 1) * borderThickness; + for (int c = 0; c < cols; c++) + availableWidth -= m_cols.m_sizes[c] += dWidth; + // If the extra width did not distribute evenly, add the remainder to + // the last column. + if (availableWidth) + m_cols.m_sizes[cols - 1] += availableWidth; + } + if (dHeight > 0) { + int availableHeight = height() - (rows - 1) * borderThickness; + for (int r = 0; r < rows; r++) + availableHeight -= m_rows.m_sizes[r] += dHeight; + // If the extra height did not distribute evenly, add the remainder to + // the last row. + if (availableHeight) + m_rows.m_sizes[rows - 1] += availableHeight; + } + // Ensure the rows and columns are filled by falling through to the normal + // layout + setHeight(max(height(), newHeight)); + setWidth(max(width(), newWidth)); + child = (RenderBox*)firstChild(); + yPos = 0; +#endif // ANDROID_FLATTEN_FRAMESET + + for (int r = 0; r < rows; r++) { + int xPos = 0; + int height = m_rows.m_sizes[r]; + for (int c = 0; c < cols; c++) { + child->setLocation(xPos, yPos); + int width = m_cols.m_sizes[c]; + + // has to be resized and itself resize its contents + if (width != child->width() || height != child->height()) { + child->setWidth(width); + child->setHeight(height); + child->setNeedsLayout(true); + child->layout(); + } + + xPos += width + borderThickness; + + child = child->nextSiblingBox(); + if (!child) + return; + } + yPos += height + borderThickness; + } + + // all the remaining frames are hidden to avoid ugly spurious unflowed frames + for (; child; child = child->nextSiblingBox()) { + child->setWidth(0); + child->setHeight(0); + child->setNeedsLayout(false); + } +} + +void RenderFrameSet::positionFramesWithFlattening() +{ + RenderBox* child = firstChildBox(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + + int borderThickness = frameSet()->border(); + bool repaintNeeded = false; + + // calculate frameset height based on actual content height to eliminate scrolling + bool out = false; + for (int r = 0; r < rows && !out; r++) { + int extra = 0; + int height = m_rows.m_sizes[r]; + + for (int c = 0; c < cols; c++) { + IntRect oldFrameRect = child->frameRect(); + + int width = m_cols.m_sizes[c]; + + bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed(); + bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed(); + + // has to be resized and itself resize its contents + if (!fixedWidth) + child->setWidth(width ? width + extra / (cols - c) : 0); + else + child->setWidth(width); + child->setHeight(height); + + child->setNeedsLayout(true); + + if (child->isFrameSet()) + toRenderFrameSet(child)->layout(); + else + toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight); + + if (child->height() > m_rows.m_sizes[r]) + m_rows.m_sizes[r] = child->height(); + if (child->width() > m_cols.m_sizes[c]) + m_cols.m_sizes[c] = child->width(); + + if (child->frameRect() != oldFrameRect) + repaintNeeded = true; + + // difference between calculated frame width and the width it actually decides to have + extra += width - m_cols.m_sizes[c]; + + child = child->nextSiblingBox(); + if (!child) { + out = true; + break; + } + } + } + + int xPos = 0; + int yPos = 0; + out = false; + child = firstChildBox(); + for (int r = 0; r < rows && !out; r++) { + xPos = 0; + for (int c = 0; c < cols; c++) { + // ensure the rows and columns are filled + IntRect oldRect = child->frameRect(); + + child->setLocation(xPos, yPos); + child->setHeight(m_rows.m_sizes[r]); + child->setWidth(m_cols.m_sizes[c]); + + if (child->frameRect() != oldRect) { + repaintNeeded = true; + + // update to final size + child->setNeedsLayout(true); + if (child->isFrameSet()) + toRenderFrameSet(child)->layout(); + else + toRenderFrame(child)->layoutWithFlattening(true, true); + } + + xPos += m_cols.m_sizes[c] + borderThickness; + child = child->nextSiblingBox(); + if (!child) { + out = true; + break; + } + } + yPos += m_rows.m_sizes[r] + borderThickness; + } + + setWidth(xPos - borderThickness); + setHeight(yPos - borderThickness); + + if (repaintNeeded) + repaint(); + + // all the remaining frames are hidden to avoid ugly spurious unflowed frames + for (; child; child = child->nextSiblingBox()) { + child->setWidth(0); + child->setHeight(0); + child->setNeedsLayout(false); + } +} + +bool RenderFrameSet::flattenFrameSet() const +{ + return frame() && frame()->settings()->frameFlatteningEnabled(); +} + +void RenderFrameSet::startResizing(GridAxis& axis, int position) +{ + int split = hitTestSplit(axis, position); + if (split == noSplit || !axis.m_allowBorder[split] || axis.m_preventResize[split]) { + axis.m_splitBeingResized = noSplit; + return; + } + axis.m_splitBeingResized = split; + axis.m_splitResizeOffset = position - splitPosition(axis, split); +} + +void RenderFrameSet::continueResizing(GridAxis& axis, int position) +{ + if (needsLayout()) + return; + if (axis.m_splitBeingResized == noSplit) + return; + int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized); + int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset; + if (delta == 0) + return; + axis.m_deltas[axis.m_splitBeingResized - 1] += delta; + axis.m_deltas[axis.m_splitBeingResized] -= delta; + setNeedsLayout(true); +} + +bool RenderFrameSet::userResize(MouseEvent* evt) +{ + if (flattenFrameSet()) + return false; + + if (!m_isResizing) { + if (needsLayout()) + return false; + if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) { + FloatPoint pos = localToAbsolute(); + startResizing(m_cols, evt->absoluteLocation().x() - pos.x()); + startResizing(m_rows, evt->absoluteLocation().y() - pos.y()); + if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) { + setIsResizing(true); + return true; + } + } + } else { + if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) { + FloatPoint pos = localToAbsolute(); + continueResizing(m_cols, evt->absoluteLocation().x() - pos.x()); + continueResizing(m_rows, evt->absoluteLocation().y() - pos.y()); + if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) { + setIsResizing(false); + return true; + } + } + } + + return false; +} + +void RenderFrameSet::setIsResizing(bool isResizing) +{ + m_isResizing = isResizing; + for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { + if (ancestor->isFrameSet()) + toRenderFrameSet(ancestor)->m_isChildResizing = isResizing; + } + if (Frame* frame = this->frame()) + frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0); +} + +bool RenderFrameSet::isResizingRow() const +{ + return m_isResizing && m_rows.m_splitBeingResized != noSplit; +} + +bool RenderFrameSet::isResizingColumn() const +{ + return m_isResizing && m_cols.m_splitBeingResized != noSplit; +} + +bool RenderFrameSet::canResizeRow(const IntPoint& p) const +{ + int r = hitTestSplit(m_rows, p.y()); + return r != noSplit && m_rows.m_allowBorder[r] && !m_rows.m_preventResize[r]; +} + +bool RenderFrameSet::canResizeColumn(const IntPoint& p) const +{ + int c = hitTestSplit(m_cols, p.x()); + return c != noSplit && m_cols.m_allowBorder[c] && !m_cols.m_preventResize[c]; +} + +int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const +{ + if (needsLayout()) + return 0; + + int borderThickness = frameSet()->border(); + + int size = axis.m_sizes.size(); + if (!size) + return 0; + + int position = 0; + for (int i = 0; i < split && i < size; ++i) + position += axis.m_sizes[i] + borderThickness; + return position - borderThickness; +} + +int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const +{ + if (needsLayout()) + return noSplit; + + int borderThickness = frameSet()->border(); + if (borderThickness <= 0) + return noSplit; + + size_t size = axis.m_sizes.size(); + if (!size) + return noSplit; + + int splitPosition = axis.m_sizes[0]; + for (size_t i = 1; i < size; ++i) { + if (position >= splitPosition && position < splitPosition + borderThickness) + return i; + splitPosition += borderThickness + axis.m_sizes[i]; + } + return noSplit; +} + +bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isFrame() || child->isFrameSet(); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderFrameSet.h b/Source/WebCore/rendering/RenderFrameSet.h new file mode 100644 index 0000000..cdc7b5a --- /dev/null +++ b/Source/WebCore/rendering/RenderFrameSet.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderFrameSet_h +#define RenderFrameSet_h + +#include "RenderBox.h" + +namespace WebCore { + +class HTMLFrameSetElement; +class MouseEvent; + +enum FrameEdge { LeftFrameEdge, RightFrameEdge, TopFrameEdge, BottomFrameEdge }; + +struct FrameEdgeInfo { + FrameEdgeInfo(bool preventResize = false, bool allowBorder = true) + : m_preventResize(4) + , m_allowBorder(4) + { + m_preventResize.fill(preventResize); + m_allowBorder.fill(allowBorder); + } + + bool preventResize(FrameEdge edge) const { return m_preventResize[edge]; } + bool allowBorder(FrameEdge edge) const { return m_allowBorder[edge]; } + + void setPreventResize(FrameEdge edge, bool preventResize) { m_preventResize[edge] = preventResize; } + void setAllowBorder(FrameEdge edge, bool allowBorder) { m_allowBorder[edge] = allowBorder; } + +private: + Vector m_preventResize; + Vector m_allowBorder; +}; + +class RenderFrameSet : public RenderBox { +public: + RenderFrameSet(HTMLFrameSetElement*); + virtual ~RenderFrameSet(); + + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + FrameEdgeInfo edgeInfo() const; + + bool userResize(MouseEvent*); + + bool isResizingRow() const; + bool isResizingColumn() const; + + bool canResizeRow(const IntPoint&) const; + bool canResizeColumn(const IntPoint&) const; + +#ifdef ANDROID_FLATTEN_FRAMESET + void setGridNeedsLayout() { m_gridCalculated = false; } +#endif + +private: + static const int noSplit = -1; + + class GridAxis : public Noncopyable { + public: + GridAxis(); + void resize(int); + Vector m_sizes; + Vector m_deltas; + Vector m_preventResize; + Vector m_allowBorder; + int m_splitBeingResized; + int m_splitResizeOffset; + }; + + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + + virtual const char* renderName() const { return "RenderFrameSet"; } + virtual bool isFrameSet() const { return true; } + + virtual void layout(); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + + inline HTMLFrameSetElement* frameSet() const; + + bool flattenFrameSet() const; + + void setIsResizing(bool); + + void layOutAxis(GridAxis&, const Length*, int availableSpace); + void computeEdgeInfo(); + void fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c); + void positionFrames(); + void positionFramesWithFlattening(); + + int splitPosition(const GridAxis&, int split) const; + int hitTestSplit(const GridAxis&, int position) const; + + void startResizing(GridAxis&, int position); + void continueResizing(GridAxis&, int position); + + void paintRowBorder(const PaintInfo&, const IntRect&); + void paintColumnBorder(const PaintInfo&, const IntRect&); + + RenderObjectChildList m_children; + + GridAxis m_rows; + GridAxis m_cols; + + bool m_isResizing; + bool m_isChildResizing; +#ifdef ANDROID_FLATTEN_FRAMESET + bool m_gridCalculated; +#endif +}; + + +inline RenderFrameSet* toRenderFrameSet(RenderObject* object) +{ + ASSERT(!object || object->isFrameSet()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderFrameSet(const RenderFrameSet*); + +} // namespace WebCore + +#endif // RenderFrameSet_h diff --git a/Source/WebCore/rendering/RenderFullScreen.cpp b/Source/WebCore/rendering/RenderFullScreen.cpp new file mode 100644 index 0000000..7cd452f --- /dev/null +++ b/Source/WebCore/rendering/RenderFullScreen.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(FULLSCREEN_API) + +#include "RenderFullScreen.h" + +#include "RenderLayer.h" + +using namespace WebCore; + +void RenderFullScreen::setAnimating(bool animating) +{ + m_isAnimating = animating; +#if ENABLE(ACCELERATED_COMPOSITING) + if (layer()) + layer()->contentChanged(RenderLayer::FullScreenChanged); +#endif +} + +PassRefPtr RenderFullScreen::createFullScreenStyle() +{ + RefPtr fullscreenStyle = RenderStyle::createDefaultStyle(); + + // Create a stacking context: + fullscreenStyle->setZIndex(0); + + fullscreenStyle->setFontDescription(FontDescription()); + fullscreenStyle->font().update(0); + + fullscreenStyle->setDisplay(BOX); + fullscreenStyle->setBoxPack(BCENTER); + fullscreenStyle->setBoxAlign(BCENTER); + fullscreenStyle->setBoxOrient(HORIZONTAL); + + fullscreenStyle->setPosition(FixedPosition); + fullscreenStyle->setWidth(Length(100.0, Percent)); + fullscreenStyle->setHeight(Length(100.0, Percent)); + fullscreenStyle->setLeft(Length(0, Fixed)); + fullscreenStyle->setTop(Length(0, Fixed)); + + fullscreenStyle->setBackgroundColor(Color::black); + + return fullscreenStyle; +} + +#endif diff --git a/Source/WebCore/rendering/RenderFullScreen.h b/Source/WebCore/rendering/RenderFullScreen.h new file mode 100644 index 0000000..52d4ee2 --- /dev/null +++ b/Source/WebCore/rendering/RenderFullScreen.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderFullScreen_h +#define RenderFullScreen_h + +#if ENABLE(FULLSCREEN_API) + +#include "RenderFlexibleBox.h" + +namespace WebCore { + +class RenderFullScreen : public RenderFlexibleBox { +public: + RenderFullScreen(Node* node) : RenderFlexibleBox(node) { setReplaced(false); setIsAnonymous(false); } + virtual bool isRenderFullScreen() const { return true; } + virtual const char* renderName() const { return "RenderFullScreen"; } + + bool isAnimating() const { return m_isAnimating; } + void setAnimating(bool); + + static PassRefPtr createFullScreenStyle(); + +protected: + bool m_isAnimating; +}; + +inline RenderFullScreen* toRenderFullScreen(RenderObject* object) +{ + ASSERT(object->isRenderFullScreen()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast: +void toRenderFullScreen(RenderFullScreen*); +} + +#endif + +#endif diff --git a/Source/WebCore/rendering/RenderHTMLCanvas.cpp b/Source/WebCore/rendering/RenderHTMLCanvas.cpp new file mode 100644 index 0000000..68bb536 --- /dev/null +++ b/Source/WebCore/rendering/RenderHTMLCanvas.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderHTMLCanvas.h" + +#include "CanvasRenderingContext.h" +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "HTMLNames.h" +#include "RenderView.h" +#include "FrameView.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderHTMLCanvas::RenderHTMLCanvas(HTMLCanvasElement* element) + : RenderReplaced(element, element->size()) +{ + view()->frameView()->setIsVisuallyNonEmpty(); +} + +bool RenderHTMLCanvas::requiresLayer() const +{ + if (RenderReplaced::requiresLayer()) + return true; + + HTMLCanvasElement* canvas = static_cast(node()); + return canvas && canvas->renderingContext() && canvas->renderingContext()->isAccelerated(); +} + +void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + IntRect rect = contentBoxRect(); + rect.move(tx, ty); + static_cast(node())->paint(paintInfo.context, rect); +} + +void RenderHTMLCanvas::canvasSizeChanged() +{ + IntSize canvasSize = static_cast(node())->size(); + IntSize zoomedSize(canvasSize.width() * style()->effectiveZoom(), canvasSize.height() * style()->effectiveZoom()); + + if (zoomedSize == intrinsicSize()) + return; + + setIntrinsicSize(zoomedSize); + + if (!parent()) + return; + + if (!preferredLogicalWidthsDirty()) + setPreferredLogicalWidthsDirty(true); + + IntSize oldSize = size(); + computeLogicalWidth(); + computeLogicalHeight(); + if (oldSize == size()) + return; + + if (!selfNeedsLayout()) + setNeedsLayout(true); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderHTMLCanvas.h b/Source/WebCore/rendering/RenderHTMLCanvas.h new file mode 100644 index 0000000..2230b39 --- /dev/null +++ b/Source/WebCore/rendering/RenderHTMLCanvas.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2004, 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderHTMLCanvas_h +#define RenderHTMLCanvas_h + +#include "RenderReplaced.h" + +namespace WebCore { + +class HTMLCanvasElement; + +class RenderHTMLCanvas : public RenderReplaced { +public: + explicit RenderHTMLCanvas(HTMLCanvasElement*); + + virtual bool isCanvas() const { return true; } + virtual bool requiresLayer() const; + + void canvasSizeChanged(); + +private: + virtual const char* renderName() const { return "RenderHTMLCanvas"; } + virtual void paintReplaced(PaintInfo&, int tx, int ty); + virtual void intrinsicSizeChanged() { canvasSizeChanged(); } +}; + +inline RenderHTMLCanvas* toRenderHTMLCanvas(RenderObject* object) +{ + ASSERT(!object || !strcmp(object->renderName(), "RenderHTMLCanvas")); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderHTMLCanvas(const RenderHTMLCanvas*); + +} // namespace WebCore + +#endif // RenderHTMLCanvas_h diff --git a/Source/WebCore/rendering/RenderIFrame.cpp b/Source/WebCore/rendering/RenderIFrame.cpp new file mode 100644 index 0000000..01b8a17 --- /dev/null +++ b/Source/WebCore/rendering/RenderIFrame.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderIFrame.h" + +#include "FrameView.h" +#include "HTMLNames.h" +#include "HTMLIFrameElement.h" +#include "RenderView.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderIFrame::RenderIFrame(Element* element) + : RenderFrameBase(element) +{ +} + +void RenderIFrame::computeLogicalHeight() +{ + RenderPart::computeLogicalHeight(); + if (!flattenFrame()) + return; + + HTMLIFrameElement* frame = static_cast(node()); + bool isScrollable = frame->scrollingMode() != ScrollbarAlwaysOff; + + if (isScrollable || !style()->height().isFixed()) { + FrameView* view = static_cast(widget()); + if (!view) + return; + int border = borderTop() + borderBottom(); + setHeight(max(height(), view->contentsHeight() + border)); + } +} + +void RenderIFrame::computeLogicalWidth() +{ + RenderPart::computeLogicalWidth(); + if (!flattenFrame()) + return; + + HTMLIFrameElement* frame = static_cast(node()); + bool isScrollable = frame->scrollingMode() != ScrollbarAlwaysOff; + + if (isScrollable || !style()->width().isFixed()) { + FrameView* view = static_cast(widget()); + if (!view) + return; + int border = borderLeft() + borderRight(); + setWidth(max(width(), view->contentsWidth() + border)); + } +} + +bool RenderIFrame::flattenFrame() +{ + if (!node() || !node()->hasTagName(iframeTag)) + return false; + + HTMLIFrameElement* element = static_cast(node()); + bool isScrollable = element->scrollingMode() != ScrollbarAlwaysOff; + + if (!isScrollable && style()->width().isFixed() + && style()->height().isFixed()) + return false; + + Frame* frame = element->document()->frame(); + bool enabled = frame && frame->settings()->frameFlatteningEnabled(); + + if (!enabled || !frame->page()) + return false; + + FrameView* view = frame->page()->mainFrame()->view(); + if (!view) + return false; + + // Do not flatten offscreen inner frames during frame flattening. + return absoluteBoundingBoxRect().intersects(IntRect(IntPoint(0, 0), view->contentsSize())); +} + +void RenderIFrame::layout() +{ + ASSERT(needsLayout()); + + RenderPart::computeLogicalWidth(); + RenderPart::computeLogicalHeight(); + + if (flattenFrame()) { + layoutWithFlattening(style()->width().isFixed(), style()->height().isFixed()); + return; + } + + RenderPart::layout(); + + m_overflow.clear(); + addShadowOverflow(); + updateLayerTransform(); + + setNeedsLayout(false); +} + +#if USE(ACCELERATED_COMPOSITING) +bool RenderIFrame::requiresLayer() const +{ + if (RenderPart::requiresLayer()) + return true; + + return requiresAcceleratedCompositing(); +} + +bool RenderIFrame::requiresAcceleratedCompositing() const +{ + if (!node() || !node()->hasTagName(iframeTag)) + return false; + +#if PLATFORM(ANDROID) + // XXX: Bug submitted to webkit.org + // https://bugs.webkit.org/show_bug.cgi?id=52655 + if (style()->visibility() != VISIBLE) + return false; +#endif + + // If the contents of the iframe are composited, then we have to be as well. + HTMLIFrameElement* element = static_cast(node()); + if (Document* contentDocument = element->contentDocument()) { + if (RenderView* view = contentDocument->renderView()) + return view->usesCompositing(); + } + + return false; +} +#endif + +} diff --git a/Source/WebCore/rendering/RenderIFrame.h b/Source/WebCore/rendering/RenderIFrame.h new file mode 100644 index 0000000..0bb3182 --- /dev/null +++ b/Source/WebCore/rendering/RenderIFrame.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderIFrame_h +#define RenderIFrame_h + +#include "RenderFrameBase.h" + +namespace WebCore { + +class RenderIFrame : public RenderFrameBase { +public: + explicit RenderIFrame(Element*); + +#if USE(ACCELERATED_COMPOSITING) + bool requiresAcceleratedCompositing() const; +#endif + +private: + virtual void computeLogicalHeight(); + virtual void computeLogicalWidth(); + + virtual void layout(); + +#if USE(ACCELERATED_COMPOSITING) + virtual bool requiresLayer() const; +#endif + virtual bool isRenderIFrame() const { return true; } + + virtual const char* renderName() const { return "RenderPartObject"; } // Lying for now to avoid breaking tests + + bool flattenFrame(); + +}; + +inline RenderIFrame* toRenderIFrame(RenderObject* object) +{ + ASSERT(!object || object->isRenderIFrame()); + return static_cast(object); +} + +inline const RenderIFrame* toRenderIFrame(const RenderObject* object) +{ + ASSERT(!object || object->isRenderIFrame()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderIFrame(const RenderIFrame*); + + +} // namespace WebCore + +#endif // RenderIFrame_h diff --git a/Source/WebCore/rendering/RenderImage.cpp b/Source/WebCore/rendering/RenderImage.cpp new file mode 100644 index 0000000..7f4c41e --- /dev/null +++ b/Source/WebCore/rendering/RenderImage.cpp @@ -0,0 +1,572 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderImage.h" + +#include "Frame.h" +#include "GraphicsContext.h" +#include "HTMLAreaElement.h" +#include "HTMLCollection.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLMapElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderLayer.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "SelectionController.h" +#include +#include + +#ifdef ANDROID_LAYOUT +#include "Settings.h" +#endif + +#if ENABLE(WML) +#include "WMLImageElement.h" +#include "WMLNames.h" +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderImage::RenderImage(Node* node) + : RenderReplaced(node, IntSize(0, 0)) + , m_needsToSetSizeForAltText(false) +{ + updateAltText(); + + view()->frameView()->setIsVisuallyNonEmpty(); +} + +RenderImage::~RenderImage() +{ + ASSERT(m_imageResource); + m_imageResource->shutdown(); +} + +void RenderImage::setImageResource(PassOwnPtr imageResource) +{ + ASSERT(!m_imageResource); + m_imageResource = imageResource; + m_imageResource->initialize(this); +} + +// If we'll be displaying either alt text or an image, add some padding. +static const unsigned short paddingWidth = 4; +static const unsigned short paddingHeight = 4; + +// Alt text is restricted to this maximum size, in pixels. These are +// signed integers because they are compared with other signed values. +static const int maxAltTextWidth = 1024; +static const int maxAltTextHeight = 256; + +IntSize RenderImage::imageSizeForError(CachedImage* newImage) const +{ + ASSERT_ARG(newImage, newImage); + ASSERT_ARG(newImage, newImage->image()); + + // imageSize() returns 0 for the error image. We need the true size of the + // error image, so we have to get it by grabbing image() directly. + return IntSize(paddingWidth + newImage->image()->width() * style()->effectiveZoom(), paddingHeight + newImage->image()->height() * style()->effectiveZoom()); +} + +// Sets the image height and width to fit the alt text. Returns true if the +// image size changed. +bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */) +{ + IntSize imageSize; + if (newImage && newImage->image()) + imageSize = imageSizeForError(newImage); + else if (!m_altText.isEmpty() || newImage) { + // If we'll be displaying either text or an image, add a little padding. + imageSize = IntSize(paddingWidth, paddingHeight); + } + + // we have an alt and the user meant it (its not a text we invented) + if (!m_altText.isEmpty()) { + const Font& font = style()->font(); + IntSize textSize(min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth), min(font.height(), maxAltTextHeight)); + imageSize = imageSize.expandedTo(textSize); + } + + if (imageSize == intrinsicSize()) + return false; + + setIntrinsicSize(imageSize); + return true; +} + +void RenderImage::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderReplaced::styleDidChange(diff, oldStyle); + if (m_needsToSetSizeForAltText) { + if (!m_altText.isEmpty() && setImageSizeForAltText(m_imageResource->cachedImage())) + imageDimensionsChanged(true /* imageSizeChanged */); + m_needsToSetSizeForAltText = false; + } +} + +void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) +{ + if (documentBeingDestroyed()) + return; + + if (hasBoxDecorations() || hasMask()) + RenderReplaced::imageChanged(newImage, rect); + + if (!m_imageResource) + return; + + if (newImage != m_imageResource->imagePtr() || !newImage) + return; + + bool imageSizeChanged = false; + + // Set image dimensions, taking into account the size of the alt text. + if (m_imageResource->errorOccurred()) { + if (!m_altText.isEmpty() && document()->isPendingStyleRecalc()) { + ASSERT(node()); + if (node()) { + m_needsToSetSizeForAltText = true; + node()->setNeedsStyleRecalc(SyntheticStyleChange); + } + return; + } + imageSizeChanged = setImageSizeForAltText(m_imageResource->cachedImage()); + } + + imageDimensionsChanged(imageSizeChanged, rect); +} + +void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect) +{ + bool shouldRepaint = true; + + if (m_imageResource->imageSize(style()->effectiveZoom()) != intrinsicSize() || imageSizeChanged) { + if (!m_imageResource->errorOccurred()) + setIntrinsicSize(m_imageResource->imageSize(style()->effectiveZoom())); + + // In the case of generated image content using :before/:after, we might not be in the + // render tree yet. In that case, we don't need to worry about check for layout, since we'll get a + // layout when we get added in to the render tree hierarchy later. + if (containingBlock()) { + // lets see if we need to relayout at all.. + int oldwidth = width(); + int oldheight = height(); + if (!preferredLogicalWidthsDirty()) + setPreferredLogicalWidthsDirty(true); + computeLogicalWidth(); + computeLogicalHeight(); + + if (imageSizeChanged || width() != oldwidth || height() != oldheight) { + shouldRepaint = false; + if (!selfNeedsLayout()) + setNeedsLayout(true); + } + + setWidth(oldwidth); + setHeight(oldheight); + } + } + + if (shouldRepaint) { + IntRect repaintRect; + if (rect) { + // The image changed rect is in source image coordinates (pre-zooming), + // so map from the bounds of the image to the contentsBox. + repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), m_imageResource->imageSize(1.0f)), contentBoxRect())); + // Guard against too-large changed rects. + repaintRect.intersect(contentBoxRect()); + } else + repaintRect = contentBoxRect(); + + repaintRectangle(repaintRect); + +#if USE(ACCELERATED_COMPOSITING) + if (hasLayer()) { + // Tell any potential compositing layers that the image needs updating. + layer()->contentChanged(RenderLayer::ImageChanged); + } +#endif + } +} + +void RenderImage::notifyFinished(CachedResource* newImage) +{ + if (!m_imageResource) + return; + + if (documentBeingDestroyed()) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (newImage == m_imageResource->cachedImage() && hasLayer()) { + // tell any potential compositing layers + // that the image is done and they can reference it directly. + layer()->contentChanged(RenderLayer::ImageChanged); + } +#else + UNUSED_PARAM(newImage); +#endif +} + +void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + int cWidth = contentWidth(); + int cHeight = contentHeight(); + int leftBorder = borderLeft(); + int topBorder = borderTop(); + int leftPad = paddingLeft(); + int topPad = paddingTop(); + + GraphicsContext* context = paintInfo.context; + + if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) { + if (paintInfo.phase == PaintPhaseSelection) + return; + + if (cWidth > 2 && cHeight > 2) { + // Draw an outline rect where the image should be. +#ifdef ANDROID_FIX // see http://b/issue?id=2052757 + context->save(); +#endif + context->setStrokeStyle(SolidStroke); + context->setStrokeColor(Color::lightGray, style()->colorSpace()); + context->setFillColor(Color::transparent, style()->colorSpace()); + context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight)); +#ifdef ANDROID_FIX // see http://b/issue?id=2052757 + context->restore(); +#endif + + bool errorPictureDrawn = false; + int imageX = 0; + int imageY = 0; + // When calculating the usable dimensions, exclude the pixels of + // the ouline rect so the error image/alt text doesn't draw on it. + int usableWidth = cWidth - 2; + int usableHeight = cHeight - 2; + + Image* image = m_imageResource->image(); + + if (m_imageResource->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { + // Center the error image, accounting for border and padding. + int centerX = (usableWidth - image->width()) / 2; + if (centerX < 0) + centerX = 0; + int centerY = (usableHeight - image->height()) / 2; + if (centerY < 0) + centerY = 0; + imageX = leftBorder + leftPad + centerX + 1; + imageY = topBorder + topPad + centerY + 1; + context->drawImage(image, style()->colorSpace(), IntPoint(tx + imageX, ty + imageY)); + errorPictureDrawn = true; + } + + if (!m_altText.isEmpty()) { + String text = document()->displayStringModifiedByEncoding(m_altText); + context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace()); + int ax = tx + leftBorder + leftPad; + int ay = ty + topBorder + topPad; + const Font& font = style()->font(); + int ascent = font.ascent(); + + // Only draw the alt text if it'll fit within the content box, + // and only if it fits above the error image. + TextRun textRun(text.characters(), text.length()); + int textWidth = font.width(textRun); + if (errorPictureDrawn) { + if (usableWidth >= textWidth && font.height() <= imageY) + context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent)); + } else if (usableWidth >= textWidth && cHeight >= font.height()) + context->drawText(style()->font(), textRun, IntPoint(ax, ay + ascent)); + } + } + } else if (m_imageResource->hasImage() && cWidth > 0 && cHeight > 0) { + Image* img = m_imageResource->image(cWidth, cHeight); + if (!img || img->isNull()) + return; + +#if PLATFORM(MAC) + if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true); +#endif + + IntSize contentSize(cWidth, cHeight); + IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); + paintIntoRect(context, rect); + } +} + +void RenderImage::paint(PaintInfo& paintInfo, int tx, int ty) +{ + RenderReplaced::paint(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseOutline) + paintFocusRing(paintInfo, style()); +} + +void RenderImage::paintFocusRing(PaintInfo& paintInfo, const RenderStyle*) +{ + // Don't draw focus rings if printing. + if (document()->printing() || !frame()->selection()->isFocusedAndActive()) + return; + + if (paintInfo.context->paintingDisabled() && !paintInfo.context->updatingControlTints()) + return; + + HTMLMapElement* mapElement = imageMap(); + if (!mapElement) + return; + + Document* document = mapElement->document(); + if (!document) + return; + + Node* focusedNode = document->focusedNode(); + if (!focusedNode) + return; + + RefPtr areas = mapElement->areas(); + unsigned numAreas = areas->length(); + + // FIXME: Clip the paths to the image bounding box. + for (unsigned k = 0; k < numAreas; ++k) { + HTMLAreaElement* areaElement = static_cast(areas->item(k)); + if (focusedNode != areaElement) + continue; + + RenderStyle* styleToUse = areaElement->computedStyle(); + if (theme()->supportsFocusRing(styleToUse)) + return; // The theme draws the focus ring. + paintInfo.context->drawFocusRing(areaElement->getPath(this), styleToUse->outlineWidth(), styleToUse->outlineOffset(), styleToUse->visitedDependentColor(CSSPropertyOutlineColor)); + break; + } +} + +void RenderImage::paintIntoRect(GraphicsContext* context, const IntRect& rect) +{ + if (!m_imageResource->hasImage() || m_imageResource->errorOccurred() || rect.width() <= 0 || rect.height() <= 0) + return; + + Image* img = m_imageResource->image(rect.width(), rect.height()); + if (!img || img->isNull()) + return; + + HTMLImageElement* imageElt = (node() && node()->hasTagName(imgTag)) ? static_cast(node()) : 0; + CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; + bool useLowQualityScaling = shouldPaintAtLowQuality(context, m_imageResource->image(), 0, rect.size()); + context->drawImage(m_imageResource->image(rect.width(), rect.height()), style()->colorSpace(), rect, compositeOperator, useLowQualityScaling); +} + +int RenderImage::minimumReplacedHeight() const +{ + return m_imageResource->errorOccurred() ? intrinsicSize().height() : 0; +} + +HTMLMapElement* RenderImage::imageMap() const +{ + HTMLImageElement* i = node() && node()->hasTagName(imgTag) ? static_cast(node()) : 0; + return i ? i->document()->getImageMap(i->fastGetAttribute(usemapAttr)) : 0; +} + +bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + bool inside = RenderReplaced::nodeAtPoint(request, tempResult, x, y, tx, ty, hitTestAction); + + if (tempResult.innerNode() && node()) { + if (HTMLMapElement* map = imageMap()) { + IntRect contentBox = contentBoxRect(); + float zoom = style()->effectiveZoom(); + int mapX = lroundf((x - tx - this->x() - contentBox.x()) / zoom); + int mapY = lroundf((y - ty - this->y() - contentBox.y()) / zoom); + if (map->mapMouseEvent(mapX, mapY, contentBox.size(), tempResult)) + tempResult.setInnerNonSharedNode(node()); + } + } + + if (!inside && result.isRectBasedTest()) + result.append(tempResult); + if (inside) + result = tempResult; + return inside; +} + +void RenderImage::updateAltText() +{ + if (!node()) + return; + + if (node()->hasTagName(inputTag)) + m_altText = static_cast(node())->altText(); + else if (node()->hasTagName(imgTag)) + m_altText = static_cast(node())->altText(); +#if ENABLE(WML) + else if (node()->hasTagName(WMLNames::imgTag)) + m_altText = static_cast(node())->altText(); +#endif +} + +bool RenderImage::isLogicalWidthSpecified() const +{ + switch (style()->logicalWidth().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +bool RenderImage::isLogicalHeightSpecified() const +{ + switch (style()->logicalHeight().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +int RenderImage::computeReplacedLogicalWidth(bool includeMaxWidth) const +{ + if (m_imageResource->imageHasRelativeWidth()) + if (RenderObject* cb = isPositioned() ? container() : containingBlock()) { + if (cb->isBox()) + m_imageResource->setImageContainerSize(IntSize(toRenderBox(cb)->availableWidth(), toRenderBox(cb)->availableHeight())); + } + + int logicalWidth; + if (isLogicalWidthSpecified()) + logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth()); + else if (m_imageResource->usesImageContainerSize()) { + IntSize size = m_imageResource->imageSize(style()->effectiveZoom()); + logicalWidth = style()->isHorizontalWritingMode() ? size.width() : size.height(); + } else if (m_imageResource->imageHasRelativeWidth()) + logicalWidth = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size. + else + logicalWidth = calcAspectRatioLogicalWidth(); + + int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth()); + int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth()); + +#ifdef ANDROID_LAYOUT + logicalWidth = max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); + // in SSR mode, we will fit the image to its container width + if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { + int cw = containingBlockLogicalWidthForContent(); + if (cw && logicalWidth > cw) + logicalWidth = cw; + } + return logicalWidth; +#else + return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); +#endif +} + +int RenderImage::computeReplacedLogicalHeight() const +{ + int logicalHeight; + if (isLogicalHeightSpecified()) + logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight()); + else if (m_imageResource->usesImageContainerSize()) { + IntSize size = m_imageResource->imageSize(style()->effectiveZoom()); + logicalHeight = style()->isHorizontalWritingMode() ? size.height() : size.width(); + } else if (m_imageResource->imageHasRelativeHeight()) + logicalHeight = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size. + else + logicalHeight = calcAspectRatioLogicalHeight(); + + int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight()); + int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight()); + +#ifdef ANDROID_LAYOUT + logicalHeight = max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); + // in SSR mode, we will fit the image to its container width + if (logicalHeight && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) { + int logicalWidth; + if (isLogicalWidthSpecified()) + logicalWidth = computeReplacedLogicalWidthUsing(style()->width()); + else + logicalWidth = calcAspectRatioLogicalWidth(); + int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->minWidth()); + int maxLogicalWidth = style()->maxWidth().value() == undefinedLength ? logicalWidth : + computeReplacedLogicalWidthUsing(style()->maxWidth()); + logicalWidth = max(minLogicalWidth, min(logicalWidth, maxLogicalWidth)); + + int cw = containingBlockLogicalWidthForContent(); + if (cw && logicalWidth && logicalWidth > cw) + logicalHeight = cw * logicalHeight / logicalWidth; // preserve aspect ratio + } + return logicalHeight; +#else + return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight)); +#endif +} + +int RenderImage::calcAspectRatioLogicalWidth() const +{ + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); + if (!intrinsicHeight) + return 0; + if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) + return intrinsicWidth; // Don't bother scaling. + return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight; +} + +int RenderImage::calcAspectRatioLogicalHeight() const +{ + int intrinsicWidth = intrinsicLogicalWidth(); + int intrinsicHeight = intrinsicLogicalHeight(); + if (!intrinsicWidth) + return 0; + if (!m_imageResource->hasImage() || m_imageResource->errorOccurred()) + return intrinsicHeight; // Don't bother scaling. + return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderImage.h b/Source/WebCore/rendering/RenderImage.h new file mode 100644 index 0000000..16ae7ec --- /dev/null +++ b/Source/WebCore/rendering/RenderImage.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderImage_h +#define RenderImage_h + +#include "RenderImageResource.h" +#include "RenderReplaced.h" + +namespace WebCore { + +class HTMLMapElement; + +class RenderImage : public RenderReplaced { +public: + RenderImage(Node*); + virtual ~RenderImage(); + + void setImageResource(PassOwnPtr); + + RenderImageResource* imageResource() { return m_imageResource.get(); } + const RenderImageResource* imageResource() const { return m_imageResource.get(); } + CachedImage* cachedImage() const { return m_imageResource ? m_imageResource->cachedImage() : 0; } + + bool setImageSizeForAltText(CachedImage* newImage = 0); + + void updateAltText(); + + HTMLMapElement* imageMap() const; + + void highQualityRepaintTimerFired(Timer*); + +protected: + virtual void styleDidChange(StyleDifference, const RenderStyle*); + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + virtual void paintIntoRect(GraphicsContext*, const IntRect&); + void paintFocusRing(PaintInfo&, const RenderStyle*); + virtual void paint(PaintInfo&, int tx, int ty); + + bool isLogicalWidthSpecified() const; + bool isLogicalHeightSpecified() const; + + virtual void intrinsicSizeChanged() + { + if (m_imageResource) + imageChanged(m_imageResource->imagePtr()); + } + +private: + virtual const char* renderName() const { return "RenderImage"; } + + virtual bool isImage() const { return true; } + virtual bool isRenderImage() const { return true; } + + virtual void paintReplaced(PaintInfo&, int tx, int ty); + + virtual int minimumReplacedHeight() const; + + virtual void notifyFinished(CachedResource*); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const; + virtual int computeReplacedLogicalHeight() const; + + IntSize imageSizeForError(CachedImage*) const; + void imageDimensionsChanged(bool imageSizeChanged, const IntRect* = 0); + + int calcAspectRatioLogicalWidth() const; + int calcAspectRatioLogicalHeight() const; + + // Text to display as long as the image isn't available. + String m_altText; + OwnPtr m_imageResource; + bool m_needsToSetSizeForAltText; + + friend class RenderImageScaleObserver; +}; + +inline RenderImage* toRenderImage(RenderObject* object) +{ + ASSERT(!object || object->isRenderImage()); + return static_cast(object); +} + +inline const RenderImage* toRenderImage(const RenderObject* object) +{ + ASSERT(!object || object->isRenderImage()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderImage(const RenderImage*); + +} // namespace WebCore + +#endif // RenderImage_h diff --git a/Source/WebCore/rendering/RenderImageResource.cpp b/Source/WebCore/rendering/RenderImageResource.cpp new file mode 100644 index 0000000..ea3ed2f --- /dev/null +++ b/Source/WebCore/rendering/RenderImageResource.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 1999 Lars Knoll + * Copyright (C) 1999 Antti Koivisto + * Copyright (C) 2000 Dirk Mueller + * Copyright (C) 2006 Allan Sandfeld Jensen + * Copyright (C) 2006 Samuel Weinig + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderImageResource.h" + +#include "RenderImageResourceStyleImage.h" +#include "RenderObject.h" + +namespace WebCore { + +RenderImageResource::RenderImageResource() + : m_renderer(0) + , m_cachedImage(0) +{ +} + +RenderImageResource::~RenderImageResource() +{ +} + +void RenderImageResource::initialize(RenderObject* renderer) +{ + ASSERT(!m_renderer); + ASSERT(renderer); + m_renderer = renderer; +} + +void RenderImageResource::shutdown() +{ + ASSERT(m_renderer); + + if (m_cachedImage) + m_cachedImage->removeClient(m_renderer); +} + +void RenderImageResource::setCachedImage(CachedImage* newImage) +{ + ASSERT(m_renderer); + + if (m_cachedImage == newImage) + return; + + if (m_cachedImage) + m_cachedImage->removeClient(m_renderer); + m_cachedImage = newImage; + if (m_cachedImage) { + m_cachedImage->addClient(m_renderer); + if (m_cachedImage->errorOccurred()) + m_renderer->imageChanged(m_cachedImage.get()); + } +} + +void RenderImageResource::resetAnimation() +{ + ASSERT(m_renderer); + + if (!m_cachedImage) + return; + + image()->resetAnimation(); + + if (!m_renderer->needsLayout()) + m_renderer->repaint(); +} + +void RenderImageResource::setImageContainerSize(const IntSize& size) const +{ + ASSERT(m_renderer); + + if (!m_cachedImage) + return; + + m_cachedImage->setImageContainerSize(size); +} + +Image* RenderImageResource::nullImage() +{ + return Image::nullImage(); +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderImageResource.h b/Source/WebCore/rendering/RenderImageResource.h new file mode 100644 index 0000000..2346712 --- /dev/null +++ b/Source/WebCore/rendering/RenderImageResource.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 1999 Lars Knoll + * Copyright (C) 1999 Antti Koivisto + * Copyright (C) 2006 Allan Sandfeld Jensen + * Copyright (C) 2006 Samuel Weinig + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderImageResource_h +#define RenderImageResource_h + +#include "CachedImage.h" +#include "CachedResourceHandle.h" +#include "StyleImage.h" +#include + +namespace WebCore { + +class RenderObject; + +class RenderImageResource : public Noncopyable { +public: + virtual ~RenderImageResource(); + + static PassOwnPtr create() + { + return adoptPtr(new RenderImageResource); + } + + virtual void initialize(RenderObject*); + virtual void shutdown(); + + void setCachedImage(CachedImage*); + CachedImage* cachedImage() const { return m_cachedImage.get(); } + virtual bool hasImage() const { return m_cachedImage; } + + void resetAnimation(); + + virtual Image* image(int /* width */ = 0, int /* height */ = 0) { return m_cachedImage ? m_cachedImage->image() : nullImage(); } + virtual bool errorOccurred() const { return m_cachedImage && m_cachedImage->errorOccurred(); } + + virtual void setImageContainerSize(const IntSize& size) const; + virtual bool usesImageContainerSize() const { return m_cachedImage ? m_cachedImage->usesImageContainerSize() : false; } + virtual bool imageHasRelativeWidth() const { return m_cachedImage ? m_cachedImage->imageHasRelativeWidth() : false; } + virtual bool imageHasRelativeHeight() const { return m_cachedImage ? m_cachedImage->imageHasRelativeHeight() : false; } + + virtual IntSize imageSize(float multiplier) const { return m_cachedImage ? m_cachedImage->imageSize(multiplier) : IntSize(); } + + virtual WrappedImagePtr imagePtr() const { return m_cachedImage.get(); } + +protected: + RenderImageResource(); + RenderObject* m_renderer; + CachedResourceHandle m_cachedImage; + +private: + static Image* nullImage(); +}; + +} // namespace WebCore + +#endif // RenderImage_h diff --git a/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp b/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp new file mode 100644 index 0000000..7f41984 --- /dev/null +++ b/Source/WebCore/rendering/RenderImageResourceStyleImage.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999 Lars Knoll + * Copyright (C) 1999 Antti Koivisto + * Copyright (C) 2000 Dirk Mueller + * Copyright (C) 2006 Allan Sandfeld Jensen + * Copyright (C) 2006 Samuel Weinig + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderImageResourceStyleImage.h" + +#include "RenderObject.h" +#include "StyleCachedImage.h" + +namespace WebCore { + +RenderImageResourceStyleImage::RenderImageResourceStyleImage(StyleImage* styleImage) + : m_styleImage(styleImage) +{ + ASSERT(m_styleImage); +} + +RenderImageResourceStyleImage::~RenderImageResourceStyleImage() +{ +} + +void RenderImageResourceStyleImage::initialize(RenderObject* renderer) +{ + RenderImageResource::initialize(renderer); + + if (m_styleImage->isCachedImage()) + m_cachedImage = static_cast(m_styleImage.get())->cachedImage(); + + m_styleImage->addClient(m_renderer); +} + +void RenderImageResourceStyleImage::shutdown() +{ + ASSERT(m_renderer); + m_styleImage->removeClient(m_renderer); + m_cachedImage = 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderImageResourceStyleImage.h b/Source/WebCore/rendering/RenderImageResourceStyleImage.h new file mode 100644 index 0000000..d91aaa8 --- /dev/null +++ b/Source/WebCore/rendering/RenderImageResourceStyleImage.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999 Lars Knoll + * Copyright (C) 1999 Antti Koivisto + * Copyright (C) 2006 Allan Sandfeld Jensen + * Copyright (C) 2006 Samuel Weinig + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Patrick Gansterer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderImageResourceStyleImage_h +#define RenderImageResourceStyleImage_h + +#include "RenderImageResource.h" +#include "StyleImage.h" +#include + +namespace WebCore { + +class RenderObject; + +class RenderImageResourceStyleImage : public RenderImageResource { +public: + virtual ~RenderImageResourceStyleImage(); + + static PassOwnPtr create(StyleImage* styleImage) + { + return adoptPtr(new RenderImageResourceStyleImage(styleImage)); + } + virtual void initialize(RenderObject*); + virtual void shutdown(); + + virtual bool hasImage() const { return true; } + virtual Image* image(int width = 0, int height = 0) { return m_styleImage->image(m_renderer, IntSize(width, height)); } + virtual bool errorOccurred() const { return m_styleImage->errorOccurred(); } + + virtual void setImageContainerSize(const IntSize& size) const { m_styleImage->setImageContainerSize(size); } + virtual bool usesImageContainerSize() const { return m_styleImage->usesImageContainerSize(); } + virtual bool imageHasRelativeWidth() const { return m_styleImage->imageHasRelativeWidth(); } + virtual bool imageHasRelativeHeight() const { return m_styleImage->imageHasRelativeHeight(); } + + virtual IntSize imageSize(float multiplier) const { return m_styleImage->imageSize(m_renderer, multiplier); } + + virtual WrappedImagePtr imagePtr() const { return m_styleImage->data(); } + +private: + RenderImageResourceStyleImage(StyleImage*); + RefPtr m_styleImage; +}; + +} // namespace WebCore + +#endif // RenderImageStyleImage_h diff --git a/Source/WebCore/rendering/RenderIndicator.cpp b/Source/WebCore/rendering/RenderIndicator.cpp new file mode 100644 index 0000000..b03dfba --- /dev/null +++ b/Source/WebCore/rendering/RenderIndicator.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#if ENABLE(PROGRESS_TAG) || ENABLE(METER_TAG) + +#include "RenderIndicator.h" + +#include "RenderTheme.h" +#include "ShadowElement.h" + +using namespace std; + +namespace WebCore { + +RenderIndicator::RenderIndicator(Node* node) + : RenderBlock(node) +{ +} + +RenderIndicator::~RenderIndicator() +{ +} + +void RenderIndicator::layout() +{ + ASSERT(needsLayout()); + + LayoutRepainter repainter(*this, checkForRepaintDuringLayout()); + computeLogicalWidth(); + computeLogicalHeight(); + layoutParts(); + repainter.repaintAfterLayout(); + setNeedsLayout(false); +} + +void RenderIndicator::updateFromElement() +{ + setNeedsLayout(true); + repaint(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/rendering/RenderIndicator.h b/Source/WebCore/rendering/RenderIndicator.h new file mode 100644 index 0000000..50d819d --- /dev/null +++ b/Source/WebCore/rendering/RenderIndicator.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderIndicator_h +#define RenderIndicator_h + +#if ENABLE(PROGRESS_TAG) || ENABLE(METER_TAG) +#include "RenderBlock.h" + +namespace WebCore { + +class RenderIndicator : public RenderBlock { +public: + RenderIndicator(Node*); + virtual ~RenderIndicator(); + +protected: + virtual void layout(); + virtual void updateFromElement(); + virtual bool requiresForcedStyleRecalcPropagation() const { return true; } + virtual bool canHaveChildren() const { return false; } + + virtual void layoutParts() = 0; +}; + +} // namespace WebCore + +#endif + +#endif // RenderIndicator_h + diff --git a/Source/WebCore/rendering/RenderInline.cpp b/Source/WebCore/rendering/RenderInline.cpp new file mode 100644 index 0000000..7466ace --- /dev/null +++ b/Source/WebCore/rendering/RenderInline.cpp @@ -0,0 +1,1155 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderInline.h" + +#include "Chrome.h" +#include "FloatQuad.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "RenderLayer.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "TransformState.h" +#include "VisiblePosition.h" + +#if ENABLE(DASHBOARD_SUPPORT) +#include "Frame.h" +#endif + +using namespace std; + +namespace WebCore { + +RenderInline::RenderInline(Node* node) + : RenderBoxModelObject(node) + , m_lineHeight(-1) +{ + setChildrenInline(true); +} + +void RenderInline::destroy() +{ + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + children()->destroyLeftoverChildren(); + + // Destroy our continuation before anything other than anonymous children. + // The reason we don't destroy it before anonymous children is that they may + // have continuations of their own that are anonymous children of our continuation. + RenderBoxModelObject* continuation = this->continuation(); + if (continuation) { + continuation->destroy(); + setContinuation(0); + } + + if (!documentBeingDestroyed()) { + if (firstLineBox()) { + // We can't wait for RenderBoxModelObject::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If line boxes are contained inside a root, that means we're an inline. + // In that case, we need to remove all the line boxes so that the parent + // lines aren't pointing to deleted children. If the first line box does + // not have a parent that means they are either already disconnected or + // root lines that can just be destroyed without disconnecting. + if (firstLineBox()->parent()) { + for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) + box->remove(); + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + m_lineBoxes.deleteLineBoxes(renderArena()); + + RenderBoxModelObject::destroy(); +} + +RenderInline* RenderInline::inlineElementContinuation() const +{ + RenderBoxModelObject* continuation = this->continuation(); + if (!continuation || continuation->isInline()) + return toRenderInline(continuation); + return toRenderBlock(continuation)->inlineElementContinuation(); +} + +void RenderInline::updateBoxModelInfoFromStyle() +{ + RenderBoxModelObject::updateBoxModelInfoFromStyle(); + + setInline(true); // Needed for run-ins, since run-in is considered a block display type. + + // FIXME: Support transforms and reflections on inline flows someday. + setHasTransform(false); + setHasReflection(false); +} + +void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBoxModelObject::styleDidChange(diff, oldStyle); + + // Ensure that all of the split inlines pick up the new style. We + // only do this if we're an inline, since we don't want to propagate + // a block's style to the other inlines. + // e.g., foo

    goo

    moo. The inlines before + // and after the block share the same style, but the block doesn't + // need to pass its style on to anyone else. + for (RenderInline* currCont = inlineElementContinuation(); currCont; currCont = currCont->inlineElementContinuation()) { + RenderBoxModelObject* nextCont = currCont->continuation(); + currCont->setContinuation(0); + currCont->setStyle(style()); + currCont->setContinuation(nextCont); + } + + m_lineHeight = -1; + + // Update pseudos for :before and :after now. + if (!isAnonymous() && document()->usesBeforeAfterRules()) { + children()->updateBeforeAfterContent(this, BEFORE); + children()->updateBeforeAfterContent(this, AFTER); + } +} + +void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (continuation()) + return addChildToContinuation(newChild, beforeChild); + return addChildIgnoringContinuation(newChild, beforeChild); +} + +static RenderBoxModelObject* nextContinuation(RenderObject* renderer) +{ + if (renderer->isInline() && !renderer->isReplaced()) + return toRenderInline(renderer)->continuation(); + return toRenderBlock(renderer)->inlineElementContinuation(); +} + +RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild) +{ + if (beforeChild && beforeChild->parent() == this) + return this; + + RenderBoxModelObject* curr = nextContinuation(this); + RenderBoxModelObject* nextToLast = this; + RenderBoxModelObject* last = this; + while (curr) { + if (beforeChild && beforeChild->parent() == curr) { + if (curr->firstChild() == beforeChild) + return last; + return curr; + } + + nextToLast = last; + last = curr; + curr = nextContinuation(curr); + } + + if (!beforeChild && !last->firstChild()) + return nextToLast; + return last; +} + +void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) { + // We are placing a block inside an inline. We have to perform a split of this + // inline into continuations. This involves creating an anonymous block box to hold + // |newChild|. We then make that block box a continuation of this inline. We take all of + // the children after |beforeChild| and put them in a clone of this object. + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + RenderBoxModelObject* oldContinuation = continuation(); + setContinuation(newBox); + + // Someone may have put a

    inside a , causing a split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after + // content gets properly destroyed. + bool isLastChild = (beforeChild == lastChild()); + if (document()->usesBeforeAfterRules()) + children()->updateBeforeAfterContent(this, AFTER); + if (isLastChild && beforeChild != lastChild()) + beforeChild = 0; // We destroyed the last child, so now we need to update our insertion + // point to be 0. It's just a straight append now. + + splitFlow(beforeChild, newBox, newChild, oldContinuation); + return; + } + + RenderBoxModelObject::addChild(newChild, beforeChild); + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); +} + +RenderInline* RenderInline::cloneInline(RenderInline* src) +{ + RenderInline* o = new (src->renderArena()) RenderInline(src->node()); + o->setStyle(src->style()); + return o; +} + +void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, + RenderBlock* middleBlock, + RenderObject* beforeChild, RenderBoxModelObject* oldCont) +{ + // Create a clone of this inline. + RenderInline* clone = cloneInline(this); + clone->setContinuation(oldCont); + + // Now take all of the children from beforeChild to the end and remove + // them from |this| and place them in the clone. + RenderObject* o = beforeChild; + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0); + tmp->setNeedsLayoutAndPrefWidthsRecalc(); + } + + // Hook |clone| up as the continuation of the middle block. + middleBlock->setContinuation(clone); + + // We have been reparented and are now under the fromBlock. We need + // to walk up our inline parent chain until we hit the containing block. + // Once we hit the containing block we're done. + RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); + RenderBoxModelObject* currChild = this; + + // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone. + // There will eventually be a better approach to this problem that will let us nest to a much + // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in + // incorrect rendering, but the alternative is to hang forever. + unsigned splitDepth = 1; + const unsigned cMaxSplitDepth = 200; + while (curr && curr != fromBlock) { + ASSERT(curr->isRenderInline()); + if (splitDepth < cMaxSplitDepth) { + // Create a new clone. + RenderInline* cloneChild = clone; + clone = cloneInline(toRenderInline(curr)); + + // Insert our child clone as the first child. + clone->addChildIgnoringContinuation(cloneChild, 0); + + // Hook the clone up as a continuation of |curr|. + RenderInline* inlineCurr = toRenderInline(curr); + oldCont = inlineCurr->continuation(); + inlineCurr->setContinuation(clone); + clone->setContinuation(oldCont); + + // Someone may have indirectly caused a to split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after + // content gets properly destroyed. + if (document()->usesBeforeAfterRules()) + inlineCurr->children()->updateBeforeAfterContent(inlineCurr, AFTER); + + // Now we need to take all of the children starting from the first child + // *after* currChild and append them all to the clone. + o = currChild->nextSibling(); + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0); + tmp->setNeedsLayoutAndPrefWidthsRecalc(); + } + } + + // Keep walking up the chain. + currChild = curr; + curr = toRenderBoxModelObject(curr->parent()); + splitDepth++; + } + + // Now we are at the block level. We need to put the clone into the toBlock. + toBlock->children()->appendChildNode(toBlock, clone); + + // Now take all the children after currChild and remove them from the fromBlock + // and put them in the toBlock. + o = currChild->nextSibling(); + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp)); + } +} + +void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderBoxModelObject* oldCont) +{ + RenderBlock* pre = 0; + RenderBlock* block = containingBlock(); + + // Delete our line boxes before we do the inline split into continuations. + block->deleteLineBoxTree(); + + bool madeNewBeforeBlock = false; + if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) { + // We can reuse this block and make it the preBlock of the next continuation. + pre = block; + pre->removePositionedObjects(0); + block = block->containingBlock(); + } else { + // No anonymous block available for use. Make one. + pre = block->createAnonymousBlock(); + madeNewBeforeBlock = true; + } + + RenderBlock* post = block->createAnonymousBlock(); + + RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); + if (madeNewBeforeBlock) + block->children()->insertChildNode(block, pre, boxFirst); + block->children()->insertChildNode(block, newBlockBox, boxFirst); + block->children()->insertChildNode(block, post, boxFirst); + block->setChildrenInline(false); + + if (madeNewBeforeBlock) { + RenderObject* o = boxFirst; + while (o) { + RenderObject* no = o; + o = no->nextSibling(); + pre->children()->appendChildNode(pre, block->children()->removeChildNode(block, no)); + no->setNeedsLayoutAndPrefWidthsRecalc(); + } + } + + splitInlines(pre, post, newBlockBox, beforeChild, oldCont); + + // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting + // time in makeChildrenNonInline by just setting this explicitly up front. + newBlockBox->setChildrenInline(false); + + // We delayed adding the newChild until now so that the |newBlockBox| would be fully + // connected, thus allowing newChild access to a renderArena should it need + // to wrap itself in additional boxes (e.g., table construction). + newBlockBox->addChild(newChild); + + // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) + // get deleted properly. Because objects moves from the pre block into the post block, we want to + // make new line boxes instead of leaving the old line boxes around. + pre->setNeedsLayoutAndPrefWidthsRecalc(); + block->setNeedsLayoutAndPrefWidthsRecalc(); + post->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderInline::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + RenderBoxModelObject* flow = continuationBefore(beforeChild); + ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || beforeChild->parent()->isRenderInline()); + RenderBoxModelObject* beforeChildParent = 0; + if (beforeChild) + beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); + else { + RenderBoxModelObject* cont = nextContinuation(flow); + if (cont) + beforeChildParent = cont; + else + beforeChildParent = flow; + } + + if (newChild->isFloatingOrPositioned()) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + + // A continuation always consists of two potential candidates: an inline or an anonymous + // block box holding block children. + bool childInline = newChild->isInline(); + bool bcpInline = beforeChildParent->isInline(); + bool flowInline = flow->isInline(); + + if (flow == beforeChildParent) + return flow->addChildIgnoringContinuation(newChild, beforeChild); + else { + // The goal here is to match up if we can, so that we can coalesce and create the + // minimal # of continuations needed for the inline. + if (childInline == bcpInline) + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + else if (flowInline == childInline) + return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. + else + return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); + } +} + +void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty) +{ + m_lineBoxes.paint(this, paintInfo, tx, ty); +} + +void RenderInline::absoluteRects(Vector& rects, int tx, int ty) +{ + if (InlineFlowBox* curr = firstLineBox()) { + for (; curr; curr = curr->nextLineBox()) + rects.append(IntRect(tx + curr->x(), ty + curr->y(), curr->logicalWidth(), curr->logicalHeight())); + } else + rects.append(IntRect(tx, ty, 0, 0)); + + if (continuation()) { + if (continuation()->isBox()) { + RenderBox* box = toRenderBox(continuation()); + continuation()->absoluteRects(rects, + tx - containingBlock()->x() + box->x(), + ty - containingBlock()->y() + box->y()); + } else + continuation()->absoluteRects(rects, tx - containingBlock()->x(), ty - containingBlock()->y()); + } +} + +void RenderInline::absoluteQuads(Vector& quads) +{ + if (InlineFlowBox* curr = firstLineBox()) { + for (; curr; curr = curr->nextLineBox()) { + FloatRect localRect(curr->x(), curr->y(), curr->logicalWidth(), curr->logicalHeight()); + quads.append(localToAbsoluteQuad(localRect)); + } + } else + quads.append(localToAbsoluteQuad(FloatRect())); + + if (continuation()) + continuation()->absoluteQuads(quads); +} + +int RenderInline::offsetLeft() const +{ + int x = RenderBoxModelObject::offsetLeft(); + if (firstLineBox()) + x += firstLineBox()->x(); + return x; +} + +int RenderInline::offsetTop() const +{ + int y = RenderBoxModelObject::offsetTop(); + if (firstLineBox()) + y += firstLineBox()->y(); + return y; +} + +static int computeMargin(const RenderInline* renderer, const Length& margin) +{ + if (margin.isAuto()) + return 0; + if (margin.isFixed()) + return margin.value(); + if (margin.isPercent()) + return margin.calcMinValue(max(0, renderer->containingBlock()->availableLogicalWidth())); + return 0; +} + +int RenderInline::marginLeft() const +{ + if (!style()->isHorizontalWritingMode()) + return 0; + return computeMargin(this, style()->marginLeft()); +} + +int RenderInline::marginRight() const +{ + if (!style()->isHorizontalWritingMode()) + return 0; + return computeMargin(this, style()->marginRight()); +} + +int RenderInline::marginTop() const +{ + if (style()->isHorizontalWritingMode()) + return 0; + return computeMargin(this, style()->marginTop()); +} + +int RenderInline::marginBottom() const +{ + if (style()->isHorizontalWritingMode()) + return 0; + return computeMargin(this, style()->marginBottom()); +} + +int RenderInline::marginStart() const +{ + return computeMargin(this, style()->marginStart()); +} + +int RenderInline::marginEnd() const +{ + return computeMargin(this, style()->marginEnd()); +} + +const char* RenderInline::renderName() const +{ + if (isRelPositioned()) + return "RenderInline (relative positioned)"; + if (isAnonymous()) + return "RenderInline (generated)"; + if (isRunIn()) + return "RenderInline (run-in)"; + return "RenderInline"; +} + +bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, + int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + return m_lineBoxes.hitTest(this, request, result, x, y, tx, ty, hitTestAction); +} + +VisiblePosition RenderInline::positionForPoint(const IntPoint& point) +{ + // FIXME: Does not deal with relative positioned inlines (should it?) + RenderBlock* cb = containingBlock(); + if (firstLineBox()) { + // This inline actually has a line box. We must have clicked in the border/padding of one of these boxes. We + // should try to find a result by asking our containing block. + return cb->positionForPoint(point); + } + + // Translate the coords from the pre-anonymous block to the post-anonymous block. + int parentBlockX = cb->x() + point.x(); + int parentBlockY = cb->y() + point.y(); + RenderBoxModelObject* c = continuation(); + while (c) { + RenderBox* contBlock = c->isInline() ? c->containingBlock() : toRenderBlock(c); + if (c->isInline() || c->firstChild()) + return c->positionForCoordinates(parentBlockX - contBlock->x(), parentBlockY - contBlock->y()); + c = toRenderBlock(c)->inlineElementContinuation(); + } + + return RenderBoxModelObject::positionForPoint(point); +} + +IntRect RenderInline::linesBoundingBox() const +{ + IntRect result; + + // See , for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been + // 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 + // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now. + ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist. + if (firstLineBox() && lastLineBox()) { + // Return the width of the minimal left side and the maximal right side. + int logicalLeftSide = 0; + int logicalRightSide = 0; + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide) + logicalLeftSide = curr->logicalLeft(); + if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide) + logicalRightSide = curr->logicalRight(); + } + + bool isHorizontal = style()->isHorizontalWritingMode(); + + int x = isHorizontal ? logicalLeftSide : firstLineBox()->x(); + int y = isHorizontal ? firstLineBox()->y() : logicalLeftSide; + int width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x; + int height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide; + result = IntRect(x, y, width, height); + } + + return result; +} + +IntRect RenderInline::linesVisualOverflowBoundingBox() const +{ + if (!firstLineBox() || !lastLineBox()) + return IntRect(); + + // Return the width of the minimal left side and the maximal right side. + int logicalLeftSide = numeric_limits::max(); + int logicalRightSide = numeric_limits::min(); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow()); + logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow()); + } + + bool isHorizontal = style()->isHorizontalWritingMode(); + + int x = isHorizontal ? logicalLeftSide : firstLineBox()->leftVisualOverflow(); + int y = isHorizontal ? firstLineBox()->topVisualOverflow() : logicalLeftSide; + int width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->rightVisualOverflow() - firstLineBox()->leftVisualOverflow(); + int height = isHorizontal ? lastLineBox()->bottomVisualOverflow() - firstLineBox()->topVisualOverflow() : logicalRightSide - logicalLeftSide; + return IntRect(x, y, width, height); +} + +IntRect RenderInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer) +{ + // Only run-ins are allowed in here during layout. + ASSERT(!view() || !view()->layoutStateEnabled() || isRunIn()); + + if (!firstLineBox() && !continuation()) + return IntRect(); + + // Find our leftmost position. + IntRect boundingBox(linesVisualOverflowBoundingBox()); + int left = boundingBox.x(); + int top = boundingBox.y(); + + // Now invalidate a rectangle. + int ow = style() ? style()->outlineSize() : 0; + + // We need to add in the relative position offsets of any inlines (including us) up to our + // containing block. + RenderBlock* cb = containingBlock(); + for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != cb; + inlineFlow = inlineFlow->parent()) { + if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer()) + toRenderInline(inlineFlow)->layer()->relativePositionOffset(left, top); + } + + IntRect r(-ow + left, -ow + top, boundingBox.width() + ow * 2, boundingBox.height() + ow * 2); + + if (cb->hasColumns()) + cb->adjustRectForColumns(r); + + if (cb->hasOverflowClip()) { + // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + IntRect repaintRect(r); + repaintRect.move(-cb->layer()->scrolledContentOffset()); // For overflow:auto/scroll/hidden. + + IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height()); + r = intersection(repaintRect, boxRect); + } + + // FIXME: need to ensure that we compute the correct repaint rect when the repaint container + // is an inline. + if (repaintContainer != this) + cb->computeRectForRepaint(repaintContainer, r); + + if (ow) { + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) { + IntRect childRect = curr->rectWithOutlineForRepaint(repaintContainer, ow); + r.unite(childRect); + } + } + + if (continuation() && !continuation()->isInline()) { + IntRect contRect = continuation()->rectWithOutlineForRepaint(repaintContainer, ow); + r.unite(contRect); + } + } + + return r; +} + +IntRect RenderInline::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) +{ + IntRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) + r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth)); + } + return r; +} + +void RenderInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) +{ + if (RenderView* v = view()) { + // LayoutState is only valid for root-relative repainting + if (v->layoutStateEnabled() && !repaintContainer) { + LayoutState* layoutState = v->layoutState(); + if (style()->position() == RelativePosition && layer()) + rect.move(layer()->relativePositionOffset()); + rect.move(layoutState->m_paintOffset); + if (layoutState->m_clipped) + rect.intersect(layoutState->m_clipRect); + return; + } + } + + if (repaintContainer == this) + return; + + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); + if (!o) + return; + + IntPoint topLeft = rect.location(); + + if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { + RenderBlock* cb = toRenderBlock(o); + if (cb->hasColumns()) { + IntRect repaintRect(topLeft, rect.size()); + cb->adjustRectForColumns(repaintRect); + topLeft = repaintRect.location(); + rect = repaintRect; + } + } + + if (style()->position() == RelativePosition && layer()) { + // Apply the relative position offset when invalidating a rectangle. The layer + // is translated, but the render box isn't, so we need to do this to get the + // right dirty rect. Since this is called from RenderObject::setStyle, the relative position + // flag on the RenderObject has been cleared, so use the one on the style(). + topLeft += layer()->relativePositionOffset(); + } + + // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, + // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. + if (o->hasOverflowClip()) { + RenderBox* containerBox = toRenderBox(o); + + // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. + + IntRect repaintRect(topLeft, rect.size()); + IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height()); + rect = intersection(repaintRect, boxRect); + if (rect.isEmpty()) + return; + } else + rect.setLocation(topLeft); + + if (containerSkipped) { + // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + rect.move(-containerOffset); + return; + } + + o->computeRectForRepaint(repaintContainer, rect, fixed); +} + +IntSize RenderInline::offsetFromContainer(RenderObject* container, const IntPoint& point) const +{ + ASSERT(container == this->container()); + + IntSize offset; + if (isRelPositioned()) + offset += relativePositionOffset(); + + container->adjustForColumns(offset, point); + + if (container->hasOverflowClip()) + offset -= toRenderBox(container)->layer()->scrolledContentOffset(); + + return offset; +} + +void RenderInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const +{ + if (repaintContainer == this) + return; + + if (RenderView *v = view()) { + if (v->layoutStateEnabled() && !repaintContainer) { + LayoutState* layoutState = v->layoutState(); + IntSize offset = layoutState->m_paintOffset; + if (style()->position() == RelativePosition && layer()) + offset += layer()->relativePositionOffset(); + transformState.move(offset); + return; + } + } + + bool containerSkipped; + RenderObject* o = container(repaintContainer, &containerSkipped); + if (!o) + return; + + IntSize containerOffset = offsetFromContainer(o, roundedIntPoint(transformState.mappedPoint())); + + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + } else + transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + + if (containerSkipped) { + // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe + // to just subtract the delta between the repaintContainer and o. + IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o); + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + return; + } + + o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState); +} + +void RenderInline::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const +{ + // We don't expect this function to be called during layout. + ASSERT(!view() || !view()->layoutStateEnabled()); + + RenderObject* o = container(); + if (!o) + return; + + o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState); + + IntSize containerOffset = offsetFromContainer(o, IntPoint()); + + bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D()); + if (useTransforms && shouldUseTransformFromContainer(o)) { + TransformationMatrix t; + getTransformFromContainer(o, containerOffset, t); + transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); + } else + transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform); +} + +void RenderInline::updateDragState(bool dragOn) +{ + RenderBoxModelObject::updateDragState(dragOn); + if (continuation()) + continuation()->updateDragState(dragOn); +} + +void RenderInline::childBecameNonInline(RenderObject* child) +{ + // We have to split the parent flow. + RenderBlock* newBox = containingBlock()->createAnonymousBlock(); + RenderBoxModelObject* oldContinuation = continuation(); + setContinuation(newBox); + RenderObject* beforeChild = child->nextSibling(); + children()->removeChildNode(this, child); + splitFlow(beforeChild, newBox, child, oldContinuation); +} + +void RenderInline::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* n = node(); + IntPoint localPoint(point); + if (n) { + if (isInlineElementContinuation()) { + // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space + // of the principal renderer's containing block. This will end up being the innerNonSharedNode. + RenderBlock* firstBlock = n->renderer()->containingBlock(); + + // Get our containing block. + RenderBox* block = containingBlock(); + localPoint.move(block->x() - firstBlock->x(), block->y() - firstBlock->y()); + } + + result.setInnerNode(n); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(n); + result.setLocalPoint(localPoint); + } +} + +void RenderInline::dirtyLineBoxes(bool fullLayout) +{ + if (fullLayout) + m_lineBoxes.deleteLineBoxes(renderArena()); + else + m_lineBoxes.dirtyLineBoxes(); +} + +InlineFlowBox* RenderInline::createInlineFlowBox() +{ + return new (renderArena()) InlineFlowBox(this); +} + +InlineFlowBox* RenderInline::createAndAppendInlineFlowBox() +{ + InlineFlowBox* flowBox = createInlineFlowBox(); + m_lineBoxes.appendLineBox(flowBox); + return flowBox; +} + +int RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const +{ + if (firstLine && document()->usesFirstLineRules()) { + RenderStyle* s = style(firstLine); + if (s != style()) + return s->computedLineHeight(); + } + + if (m_lineHeight == -1) + m_lineHeight = style()->computedLineHeight(); + + return m_lineHeight; +} + +int RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const +{ + const Font& f = style(firstLine)->font(); + return f.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - f.height()) / 2; +} + +IntSize RenderInline::relativePositionedInlineOffset(const RenderBox* child) const +{ + ASSERT(isRelPositioned()); + if (!isRelPositioned()) + return IntSize(); + + // When we have an enclosing relpositioned inline, we need to add in the offset of the first line + // box from the rest of the content, but only in the cases where we know we're positioned + // relative to the inline itself. + + IntSize offset; + int sx; + int sy; + if (firstLineBox()) { + sx = firstLineBox()->x(); + sy = firstLineBox()->y(); + } else { + sx = layer()->staticX(); + sy = layer()->staticY(); + } + + if (!child->style()->hasStaticX()) + offset.setWidth(sx); + // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside + // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct + // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers + // do. + else if (!child->style()->isOriginalDisplayInlineType()) + // Avoid adding in the left border/padding of the containing block twice. Subtract it out. + offset.setWidth(sx - (child->containingBlock()->borderLeft() + child->containingBlock()->paddingLeft())); + + if (!child->style()->hasStaticY()) + offset.setHeight(sy); + + return offset; +} + +void RenderInline::imageChanged(WrappedImagePtr, const IntRect*) +{ + if (!parent()) + return; + + // FIXME: We can do better. + repaint(); +} + +void RenderInline::addFocusRingRects(Vector& rects, int tx, int ty) +{ + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + RootInlineBox* root = curr->root(); + int top = max(root->lineTop(), curr->y()); + int bottom = min(root->lineBottom(), curr->y() + curr->logicalHeight()); + IntRect rect(tx + curr->x(), ty + top, curr->logicalWidth(), bottom - top); + if (!rect.isEmpty()) + rects.append(rect); + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText() && !curr->isListMarker()) { + FloatPoint pos(tx, ty); + // FIXME: This doesn't work correctly with transforms. + if (curr->hasLayer()) + pos = curr->localToAbsolute(); + else if (curr->isBox()) + pos.move(toRenderBox(curr)->x(), toRenderBox(curr)->y()); + curr->addFocusRingRects(rects, pos.x(), pos.y()); + } + } + + if (continuation()) { + if (continuation()->isInline()) + continuation()->addFocusRingRects(rects, + tx - containingBlock()->x() + continuation()->containingBlock()->x(), + ty - containingBlock()->y() + continuation()->containingBlock()->y()); + else + continuation()->addFocusRingRects(rects, + tx - containingBlock()->x() + toRenderBox(continuation())->x(), + ty - containingBlock()->y() + toRenderBox(continuation())->y()); + } +} + +void RenderInline::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) +{ + if (!hasOutline()) + return; + + RenderStyle* styleToUse = style(); + if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) { + if (!theme()->supportsFocusRing(styleToUse)) { + // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. + paintFocusRing(graphicsContext, tx, ty, styleToUse); + } + } + + if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE) + return; + + Vector rects; + + rects.append(IntRect()); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + RootInlineBox* root = curr->root(); + int top = max(root->lineTop(), curr->y()); + int bottom = min(root->lineBottom(), curr->y() + curr->logicalHeight()); + rects.append(IntRect(curr->x(), top, curr->logicalWidth(), bottom - top)); + } + rects.append(IntRect()); + + for (unsigned i = 1; i < rects.size() - 1; i++) + paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); +} + +void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, + const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) +{ + RenderStyle* styleToUse = style(); + int ow = styleToUse->outlineWidth(); + EBorderStyle os = styleToUse->outlineStyle(); + Color oc = styleToUse->visitedDependentColor(CSSPropertyOutlineColor); + + int offset = style()->outlineOffset(); + + int t = ty + thisline.y() - offset; + int l = tx + thisline.x() - offset; + int b = ty + thisline.bottom() + offset; + int r = tx + thisline.right() + offset; + + // left edge + drawLineForBoxSide(graphicsContext, + l - ow, + t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), + l, + b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), + BSLeft, + oc, os, + (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); + + // right edge + drawLineForBoxSide(graphicsContext, + r, + t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), + r + ow, + b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), + BSRight, + oc, os, + (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); + // upper edge + if (thisline.x() < lastline.x()) + drawLineForBoxSide(graphicsContext, + l - ow, + t - ow, + min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), + t , + BSTop, oc, os, + ow, + (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); + + if (lastline.right() < thisline.right()) + drawLineForBoxSide(graphicsContext, + max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), + t - ow, + r + ow, + t , + BSTop, oc, os, + (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, + ow); + + // lower edge + if (thisline.x() < nextline.x()) + drawLineForBoxSide(graphicsContext, + l - ow, + b, + min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), + b + ow, + BSBottom, oc, os, + ow, + (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); + + if (nextline.right() < thisline.right()) + drawLineForBoxSide(graphicsContext, + max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), + b, + r + ow, + b + ow, + BSBottom, oc, os, + (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, + ow); +} + +#if ENABLE(DASHBOARD_SUPPORT) +void RenderInline::addDashboardRegions(Vector& regions) +{ + // Convert the style regions to absolute coordinates. + if (style()->visibility() != VISIBLE) + return; + + const Vector& styleRegions = style()->dashboardRegions(); + unsigned i, count = styleRegions.size(); + for (i = 0; i < count; i++) { + StyleDashboardRegion styleRegion = styleRegions[i]; + + IntRect linesBoundingBox = this->linesBoundingBox(); + int w = linesBoundingBox.width(); + int h = linesBoundingBox.height(); + + DashboardRegionValue region; + region.label = styleRegion.label; + region.bounds = IntRect(linesBoundingBox.x() + styleRegion.offset.left().value(), + linesBoundingBox.y() + styleRegion.offset.top().value(), + w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), + h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); + region.type = styleRegion.type; + + RenderObject* container = containingBlock(); + if (!container) + container = this; + + region.clip = region.bounds; + container->computeAbsoluteRepaintRect(region.clip); + if (region.clip.height() < 0) { + region.clip.setHeight(0); + region.clip.setWidth(0); + } + + FloatPoint absPos = container->localToAbsolute(); + region.bounds.setX(absPos.x() + region.bounds.x()); + region.bounds.setY(absPos.y() + region.bounds.y()); + + if (frame()) { + float pageScaleFactor = frame()->page()->chrome()->scaleFactor(); + if (pageScaleFactor != 1.0f) { + region.bounds.scale(pageScaleFactor); + region.clip.scale(pageScaleFactor); + } + } + + regions.append(region); + } +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderInline.h b/Source/WebCore/rendering/RenderInline.h new file mode 100644 index 0000000..18b4a3c --- /dev/null +++ b/Source/WebCore/rendering/RenderInline.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef RenderInline_h +#define RenderInline_h + +#include "RenderBoxModelObject.h" +#include "RenderLineBoxList.h" + +namespace WebCore { + +class Position; + +class RenderInline : public RenderBoxModelObject { +public: + explicit RenderInline(Node*); + + virtual void destroy(); + + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + + virtual int marginLeft() const; + virtual int marginRight() const; + virtual int marginTop() const; + virtual int marginBottom() const; + virtual int marginBefore() const { return 0; } + virtual int marginAfter() const { return 0; } + virtual int marginStart() const; + virtual int marginEnd() const; + + virtual void absoluteRects(Vector&, int tx, int ty); + virtual void absoluteQuads(Vector&); + + virtual IntSize offsetFromContainer(RenderObject*, const IntPoint&) const; + + IntRect linesBoundingBox() const; + IntRect linesVisualOverflowBoundingBox() const; + + InlineFlowBox* createAndAppendInlineFlowBox(); + + void dirtyLineBoxes(bool fullLayout); + + RenderLineBoxList* lineBoxes() { return &m_lineBoxes; } + const RenderLineBoxList* lineBoxes() const { return &m_lineBoxes; } + + InlineFlowBox* firstLineBox() const { return m_lineBoxes.firstLineBox(); } + InlineFlowBox* lastLineBox() const { return m_lineBoxes.lastLineBox(); } + + virtual RenderBoxModelObject* virtualContinuation() const { return continuation(); } + RenderInline* inlineElementContinuation() const; + + virtual void updateDragState(bool dragOn); + + IntSize relativePositionedInlineOffset(const RenderBox* child) const; + + virtual void addFocusRingRects(Vector&, int tx, int ty); + void paintOutline(GraphicsContext*, int tx, int ty); + + using RenderBoxModelObject::continuation; + using RenderBoxModelObject::setContinuation; + +protected: + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + +private: + virtual RenderObjectChildList* virtualChildren() { return children(); } + virtual const RenderObjectChildList* virtualChildren() const { return children(); } + const RenderObjectChildList* children() const { return &m_children; } + RenderObjectChildList* children() { return &m_children; } + + virtual const char* renderName() const; + + virtual bool isRenderInline() const { return true; } + + void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild); + virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = 0); + + void splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, + RenderObject* beforeChild, RenderBoxModelObject* oldCont); + void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderBoxModelObject* oldCont); + + virtual void layout() { ASSERT_NOT_REACHED(); } // Do nothing for layout() + + virtual void paint(PaintInfo&, int tx, int ty); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual bool requiresLayer() const { return isRelPositioned() || isTransparent() || hasMask(); } + + virtual int offsetLeft() const; + virtual int offsetTop() const; + virtual int offsetWidth() const { return linesBoundingBox().width(); } + virtual int offsetHeight() const { return linesBoundingBox().height(); } + + virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer); + virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth); + virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed); + + virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState&) const; + virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const; + + virtual VisiblePosition positionForPoint(const IntPoint&); + + virtual IntRect borderBoundingBox() const + { + IntRect boundingBox = linesBoundingBox(); + return IntRect(0, 0, boundingBox.width(), boundingBox.height()); + } + + virtual InlineFlowBox* createInlineFlowBox(); // Subclassed by SVG and Ruby + + virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } + + virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + + virtual void childBecameNonInline(RenderObject* child); + + virtual void updateHitTestResult(HitTestResult&, const IntPoint&); + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + +#if ENABLE(DASHBOARD_SUPPORT) + virtual void addDashboardRegions(Vector&); +#endif + + virtual void updateBoxModelInfoFromStyle(); + + static RenderInline* cloneInline(RenderInline* src); + + void paintOutlineForLine(GraphicsContext*, int tx, int ty, const IntRect& prevLine, const IntRect& thisLine, const IntRect& nextLine); + RenderBoxModelObject* continuationBefore(RenderObject* beforeChild); + + RenderObjectChildList m_children; + RenderLineBoxList m_lineBoxes; // All of the line boxes created for this inline flow. For example, Hello
    world.
    will have two line boxes. + + mutable int m_lineHeight; +}; + +inline RenderInline* toRenderInline(RenderObject* object) +{ + ASSERT(!object || object->isRenderInline()); + return static_cast(object); +} + +inline const RenderInline* toRenderInline(const RenderObject* object) +{ + ASSERT(!object || object->isRenderInline()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderInline(const RenderInline*); + +} // namespace WebCore + +#endif // RenderInline_h diff --git a/Source/WebCore/rendering/RenderInputSpeech.cpp b/Source/WebCore/rendering/RenderInputSpeech.cpp new file mode 100644 index 0000000..5472025 --- /dev/null +++ b/Source/WebCore/rendering/RenderInputSpeech.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderInputSpeech.h" + +#if ENABLE(INPUT_SPEECH) + +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "RenderBox.h" +#include "TextControlInnerElements.h" + +namespace WebCore { + +static const float defaultControlFontPixelSize = 13; +static const float defaultSpeechButtonSize = 16; +static const float minSpeechButtonSize = 8; +static const float maxSpeechButtonSize = 40; + +void RenderInputSpeech::adjustInputFieldSpeechButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) +{ + // Scale the button size based on the font size. + float fontScale = style->fontSize() / defaultControlFontPixelSize; + int speechButtonSize = lroundf(std::min(std::max(minSpeechButtonSize, defaultSpeechButtonSize * fontScale), maxSpeechButtonSize)); + style->setWidth(Length(speechButtonSize, Fixed)); + style->setHeight(Length(speechButtonSize, Fixed)); +} + +bool RenderInputSpeech::paintInputFieldSpeechButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + // Get the renderer of element. + Node* input = object->node()->shadowAncestorNode(); + if (!input->renderer()->isBox()) + return false; + RenderBox* inputRenderBox = toRenderBox(input->renderer()); + IntRect inputContentBox = inputRenderBox->contentBoxRect(); + + // Make sure the scaled button stays square and will fit in its parent's box. + int buttonSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height())); + // Calculate button's coordinates relative to the input element. + // Center the button vertically. Round up though, so if it has to be one pixel off-center, it will + // be one pixel closer to the bottom of the field. This tends to look better with the text. + IntRect buttonRect(object->offsetFromAncestorContainer(inputRenderBox).width(), + inputContentBox.y() + (inputContentBox.height() - buttonSize + 1) / 2, + buttonSize, buttonSize); + + // Compute an offset between the part renderer and the input renderer. + IntSize offsetFromInputRenderer = -(object->offsetFromAncestorContainer(inputRenderBox)); + // Move the rect into partRenderer's coords. + buttonRect.move(offsetFromInputRenderer); + // Account for the local drawing offset. + buttonRect.move(rect.x(), rect.y()); + + DEFINE_STATIC_LOCAL(RefPtr, imageStateNormal, (Image::loadPlatformResource("inputSpeech"))); + DEFINE_STATIC_LOCAL(RefPtr, imageStateRecording, (Image::loadPlatformResource("inputSpeechRecording"))); + DEFINE_STATIC_LOCAL(RefPtr, imageStateWaiting, (Image::loadPlatformResource("inputSpeechWaiting"))); + + InputFieldSpeechButtonElement* speechButton = reinterpret_cast(object->node()); + Image* image = imageStateNormal.get(); + if (speechButton->state() == InputFieldSpeechButtonElement::Recording) + image = imageStateRecording.get(); + else if (speechButton->state() == InputFieldSpeechButtonElement::Recognizing) + image = imageStateWaiting.get(); + paintInfo.context->drawImage(image, object->style()->colorSpace(), buttonRect); + + return false; +} + +} // namespace WebCore + +#endif // ENABLE(INPUT_SPEECH) diff --git a/Source/WebCore/rendering/RenderInputSpeech.h b/Source/WebCore/rendering/RenderInputSpeech.h new file mode 100644 index 0000000..63ef8ae --- /dev/null +++ b/Source/WebCore/rendering/RenderInputSpeech.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderInputSpeech_h +#define RenderInputSpeech_h + +#include "RenderObject.h" + +#if ENABLE(INPUT_SPEECH) + +namespace WebCore { + +class RenderInputSpeech { +public: + static void adjustInputFieldSpeechButtonStyle(CSSStyleSelector*, RenderStyle*, Element*); + static bool paintInputFieldSpeechButton(RenderObject*, const PaintInfo&, const IntRect&); +}; + +} // namespace WebCore + +#endif +#endif // RenderInputSpeech_h diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp new file mode 100644 index 0000000..5623662 --- /dev/null +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -0,0 +1,4058 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan + * David Baron + * Christian Biesinger + * Randall Jesup + * Roland Mainz + * Josh Soref + * Boris Zbarsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "RenderLayer.h" + +#include "ColumnInfo.h" +#include "CSSPropertyNames.h" +#include "CSSStyleDeclaration.h" +#include "CSSStyleSelector.h" +#include "Chrome.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "FloatPoint3D.h" +#include "FloatRect.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLNames.h" +#if ENABLE(ANDROID_OVERFLOW_SCROLL) +#include "HTMLTextAreaElement.h" +#endif +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "OverflowEvent.h" +#include "OverlapTestRequestClient.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderMarquee.h" +#include "RenderReplica.h" +#include "RenderScrollbar.h" +#include "RenderScrollbarPart.h" +#include "RenderTheme.h" +#include "RenderTreeAsText.h" +#include "RenderView.h" +#include "ScaleTransformOperation.h" +#include "Scrollbar.h" +#include "ScrollbarTheme.h" +#include "SelectionController.h" +#include "TextStream.h" +#include "TransformState.h" +#include "TransformationMatrix.h" +#include "TranslateTransformOperation.h" +#include +#include +#include + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#endif + +#if ENABLE(SVG) +#include "SVGNames.h" +#endif + +#define MIN_INTERSECT_FOR_REVEAL 32 + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int MinimumWidthWhileResizing = 100; +const int MinimumHeightWhileResizing = 40; + +void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void ClipRects::operator delete(void* ptr, size_t sz) +{ + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +void ClipRects::destroy(RenderArena* renderArena) +{ + delete this; + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +RenderLayer::RenderLayer(RenderBoxModelObject* renderer) + : m_renderer(renderer) + , m_parent(0) + , m_previous(0) + , m_next(0) + , m_first(0) + , m_last(0) + , m_relX(0) + , m_relY(0) + , m_x(0) + , m_y(0) + , m_width(0) + , m_height(0) + , m_scrollX(0) + , m_scrollY(0) + , m_scrollLeftOverflow(0) + , m_scrollTopOverflow(0) + , m_scrollWidth(0) + , m_scrollHeight(0) + , m_inResizeMode(false) + , m_posZOrderList(0) + , m_negZOrderList(0) + , m_normalFlowList(0) + , m_clipRects(0) +#ifndef NDEBUG + , m_clipRectsRoot(0) +#endif + , m_scrollDimensionsDirty(true) + , m_zOrderListsDirty(true) + , m_normalFlowListDirty(true) + , m_isNormalFlowOnly(shouldBeNormalFlowOnly()) + , m_usedTransparency(false) + , m_paintingInsideReflection(false) + , m_inOverflowRelayout(false) + , m_needsFullRepaint(false) + , m_overflowStatusDirty(true) + , m_visibleContentStatusDirty(true) + , m_hasVisibleContent(false) + , m_visibleDescendantStatusDirty(false) + , m_hasVisibleDescendant(false) + , m_isPaginated(false) + , m_3DTransformedDescendantStatusDirty(true) + , m_has3DTransformedDescendant(false) +#if USE(ACCELERATED_COMPOSITING) + , m_hasCompositingDescendant(false) + , m_mustOverlapCompositedLayers(false) +#endif +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + , m_hasOverflowScroll(false) +#endif + , m_marquee(0) + , m_staticX(0) + , m_staticY(0) + , m_reflection(0) + , m_scrollCorner(0) + , m_resizer(0) +{ + if (!renderer->firstChild() && renderer->style()) { + m_visibleContentStatusDirty = false; + m_hasVisibleContent = renderer->style()->visibility() == VISIBLE; + } +} + +RenderLayer::~RenderLayer() +{ + if (inResizeMode() && !renderer()->documentBeingDestroyed()) { + if (Frame* frame = renderer()->frame()) + frame->eventHandler()->resizeLayerDestroyed(); + } + + destroyScrollbar(HorizontalScrollbar); + destroyScrollbar(VerticalScrollbar); + + // Child layers will be deleted by their corresponding render objects, so + // we don't need to delete them ourselves. + + delete m_posZOrderList; + delete m_negZOrderList; + delete m_normalFlowList; + delete m_marquee; + +#if USE(ACCELERATED_COMPOSITING) + clearBacking(); +#endif + + // Make sure we have no lingering clip rects. + ASSERT(!m_clipRects); + + if (m_reflection) + removeReflection(); + + if (m_scrollCorner) + m_scrollCorner->destroy(); + if (m_resizer) + m_resizer->destroy(); +} + +#if USE(ACCELERATED_COMPOSITING) +RenderLayerCompositor* RenderLayer::compositor() const +{ + ASSERT(renderer()->view()); + return renderer()->view()->compositor(); +} + +void RenderLayer::contentChanged(ContentChangeType changeType) +{ + // This can get called when video becomes accelerated, so the layers may change. + if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged) && compositor()->updateLayerCompositingState(this)) + compositor()->setCompositingLayersNeedRebuild(); + + if (m_backing) + m_backing->contentChanged(changeType); +} +#endif // USE(ACCELERATED_COMPOSITING) + +bool RenderLayer::hasAcceleratedCompositing() const +{ +#if USE(ACCELERATED_COMPOSITING) + return compositor()->hasAcceleratedCompositing(); +#else + return false; +#endif +} + +bool RenderLayer::canRender3DTransforms() const +{ +#if USE(ACCELERATED_COMPOSITING) + return compositor()->canRender3DTransforms(); +#else + return false; +#endif +} + +void RenderLayer::updateLayerPositions(UpdateLayerPositionsFlags flags, IntPoint* cachedOffset) +{ + if (flags & DoFullRepaint) { + renderer()->repaint(); +#if USE(ACCELERATED_COMPOSITING) + flags &= ~CheckForRepaint; + // We need the full repaint to propagate to child layers if we are hardware compositing. + if (!compositor()->inCompositingMode()) + flags &= ~DoFullRepaint; +#else + flags &= ~(CheckForRepaint | DoFullRepaint); +#endif + } + + + updateLayerPosition(); // For relpositioned layers or non-positioned layers, + // we need to keep in sync, since we may have shifted relative + // to our parent layer. + IntPoint oldCachedOffset; + if (cachedOffset) { + // We can't cache our offset to the repaint container if the mapping is anything more complex than a simple translation + bool disableOffsetCache = renderer()->hasColumns() || renderer()->hasTransform() || isComposited(); +#if ENABLE(SVG) + disableOffsetCache = disableOffsetCache || renderer()->isSVGRoot(); +#endif + if (disableOffsetCache) + cachedOffset = 0; // If our cached offset is invalid make sure it's not passed to any of our children + else { + oldCachedOffset = *cachedOffset; + // Frequently our parent layer's renderer will be the same as our renderer's containing block. In that case, + // we just update the cache using our offset to our parent (which is m_x / m_y). Otherwise, regenerated cached + // offsets to the root from the render tree. + if (!m_parent || m_parent->renderer() == renderer()->containingBlock()) + cachedOffset->move(m_x, m_y); // Fast case + else { + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + *cachedOffset = IntPoint(x, y); + } + } + } + + int x = 0; + int y = 0; + if (cachedOffset) { + x += cachedOffset->x(); + y += cachedOffset->y(); +#ifndef NDEBUG + int nonCachedX = 0; + int nonCachedY = 0; + convertToLayerCoords(root(), nonCachedX, nonCachedY); + ASSERT(x == nonCachedX); + ASSERT(y == nonCachedY); +#endif + } else + convertToLayerCoords(root(), x, y); + positionOverflowControls(x, y); + + updateVisibilityStatus(); + + if (flags & UpdatePagination) + updatePagination(); + else + m_isPaginated = false; + + if (m_hasVisibleContent) { + RenderView* view = renderer()->view(); + ASSERT(view); + // FIXME: Optimize using LayoutState and remove the disableLayoutState() call + // from updateScrollInfoAfterLayout(). + ASSERT(!view->layoutStateEnabled()); + + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + IntRect newRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + IntRect newOutlineBox = renderer()->outlineBoundsForRepaint(repaintContainer, cachedOffset); + // FIXME: Should ASSERT that value calculated for newOutlineBox using the cached offset is the same + // as the value not using the cached offset, but we can't due to https://bugs.webkit.org/show_bug.cgi?id=37048 + if (flags & CheckForRepaint) { + if (view && !view->printing()) { + if (m_needsFullRepaint) { + renderer()->repaintUsingContainer(repaintContainer, m_repaintRect); + if (newRect != m_repaintRect) + renderer()->repaintUsingContainer(repaintContainer, newRect); + } else + renderer()->repaintAfterLayoutIfNeeded(repaintContainer, m_repaintRect, m_outlineBox, &newRect, &newOutlineBox); + } + } + m_repaintRect = newRect; + m_outlineBox = newOutlineBox; + } else { + m_repaintRect = IntRect(); + m_outlineBox = IntRect(); + } + + m_needsFullRepaint = false; + + // Go ahead and update the reflection's position and size. + if (m_reflection) + m_reflection->layout(); + +#if USE(ACCELERATED_COMPOSITING) + // Clear the IsCompositingUpdateRoot flag once we've found the first compositing layer in this update. + bool isUpdateRoot = (flags & IsCompositingUpdateRoot); + if (isComposited()) + flags &= ~IsCompositingUpdateRoot; +#endif + + if (renderer()->hasColumns()) + flags |= UpdatePagination; + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->updateLayerPositions(flags, cachedOffset); + +#if USE(ACCELERATED_COMPOSITING) + if ((flags & UpdateCompositingLayers) && isComposited()) + backing()->updateAfterLayout(RenderLayerBacking::CompositingChildren, isUpdateRoot); +#endif + + // With all our children positioned, now update our marquee if we need to. + if (m_marquee) + m_marquee->updateMarqueePosition(); + + if (cachedOffset) + *cachedOffset = oldCachedOffset; +} + +IntRect RenderLayer::repaintRectIncludingDescendants() const +{ + IntRect repaintRect = m_repaintRect; + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + repaintRect.unite(child->repaintRectIncludingDescendants()); + return repaintRect; +} + +void RenderLayer::computeRepaintRects() +{ + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + m_repaintRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + m_outlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); +} + +void RenderLayer::updateRepaintRectsAfterScroll(bool fixed) +{ + if (fixed || renderer()->style()->position() == FixedPosition) { + computeRepaintRects(); + fixed = true; + } else if (renderer()->hasTransform()) { + // Transforms act as fixed position containers, so nothing inside a + // transformed element can be fixed relative to the viewport if the + // transformed element is not fixed itself or child of a fixed element. + return; + } + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->updateRepaintRectsAfterScroll(fixed); +} + +void RenderLayer::updateTransform() +{ + // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, + // so check style too. + bool hasTransform = renderer()->hasTransform() && renderer()->style()->hasTransform(); + bool had3DTransform = has3DTransform(); + + bool hadTransform = m_transform; + if (hasTransform != hadTransform) { + if (hasTransform) + m_transform.set(new TransformationMatrix); + else + m_transform.clear(); + } + + if (hasTransform) { + RenderBox* box = renderBox(); + ASSERT(box); + m_transform->makeIdentity(); + box->style()->applyTransform(*m_transform, box->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(*m_transform, canRender3DTransforms()); + } + + if (had3DTransform != has3DTransform()) + dirty3DTransformedDescendantStatus(); +} + +TransformationMatrix RenderLayer::currentTransform() const +{ + if (!m_transform) + return TransformationMatrix(); + +#if USE(ACCELERATED_COMPOSITING) + if (renderer()->style()->isRunningAcceleratedAnimation()) { + TransformationMatrix currTransform; + RefPtr style = renderer()->animation()->getAnimatedStyleForRenderer(renderer()); + style->applyTransform(currTransform, renderBox()->borderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + makeMatrixRenderable(currTransform, canRender3DTransforms()); + return currTransform; + } +#endif + + return *m_transform; +} + +TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavior) const +{ + if (!m_transform) + return TransformationMatrix(); + + if (paintBehavior & PaintBehaviorFlattenCompositingLayers) { + TransformationMatrix matrix = *m_transform; + makeMatrixRenderable(matrix, false /* flatten 3d */); + return matrix; + } + + return *m_transform; +} + +static bool checkContainingBlockChainForPagination(RenderBoxModelObject* renderer, RenderBox* ancestorColumnsRenderer) +{ + RenderView* view = renderer->view(); + RenderBoxModelObject* prevBlock = renderer; + RenderBlock* containingBlock; + for (containingBlock = renderer->containingBlock(); + containingBlock && containingBlock != view && containingBlock != ancestorColumnsRenderer; + containingBlock = containingBlock->containingBlock()) + prevBlock = containingBlock; + + // If the columns block wasn't in our containing block chain, then we aren't paginated by it. + if (containingBlock != ancestorColumnsRenderer) + return false; + + // If the previous block is absolutely positioned, then we can't be paginated by the columns block. + if (prevBlock->isPositioned()) + return false; + + // Otherwise we are paginated by the columns block. + return true; +} + +void RenderLayer::updatePagination() +{ + m_isPaginated = false; + if (isComposited() || !parent()) + return; // FIXME: We will have to deal with paginated compositing layers someday. + // FIXME: For now the RenderView can't be paginated. Eventually printing will move to a model where it is though. + + if (isNormalFlowOnly()) { + m_isPaginated = parent()->renderer()->hasColumns(); + return; + } + + // If we're not normal flow, then we need to look for a multi-column object between us and our stacking context. + RenderLayer* ancestorStackingContext = stackingContext(); + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->renderer()->hasColumns()) { + m_isPaginated = checkContainingBlockChainForPagination(renderer(), curr->renderBox()); + return; + } + if (curr == ancestorStackingContext) + return; + } +} + +void RenderLayer::setHasVisibleContent(bool b) +{ + if (m_hasVisibleContent == b && !m_visibleContentStatusDirty) + return; + m_visibleContentStatusDirty = false; + m_hasVisibleContent = b; + if (m_hasVisibleContent) { + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + m_repaintRect = renderer()->clippedOverflowRectForRepaint(repaintContainer); + m_outlineBox = renderer()->outlineBoundsForRepaint(repaintContainer); + if (!isNormalFlowOnly()) { + for (RenderLayer* sc = stackingContext(); sc; sc = sc->stackingContext()) { + sc->dirtyZOrderLists(); + if (sc->hasVisibleContent()) + break; + } + } + } + if (parent()) + parent()->childVisibilityChanged(m_hasVisibleContent); +} + +void RenderLayer::dirtyVisibleContentStatus() +{ + m_visibleContentStatusDirty = true; + if (parent()) + parent()->dirtyVisibleDescendantStatus(); +} + +void RenderLayer::childVisibilityChanged(bool newVisibility) +{ + if (m_hasVisibleDescendant == newVisibility || m_visibleDescendantStatusDirty) + return; + if (newVisibility) { + RenderLayer* l = this; + while (l && !l->m_visibleDescendantStatusDirty && !l->m_hasVisibleDescendant) { + l->m_hasVisibleDescendant = true; + l = l->parent(); + } + } else + dirtyVisibleDescendantStatus(); +} + +void RenderLayer::dirtyVisibleDescendantStatus() +{ + RenderLayer* l = this; + while (l && !l->m_visibleDescendantStatusDirty) { + l->m_visibleDescendantStatusDirty = true; + l = l->parent(); + } +} + +void RenderLayer::updateVisibilityStatus() +{ + if (m_visibleDescendantStatusDirty) { + m_hasVisibleDescendant = false; + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + child->updateVisibilityStatus(); + if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) { + m_hasVisibleDescendant = true; + break; + } + } + m_visibleDescendantStatusDirty = false; + } + + if (m_visibleContentStatusDirty) { + if (renderer()->style()->visibility() == VISIBLE) + m_hasVisibleContent = true; + else { + // layer may be hidden but still have some visible content, check for this + m_hasVisibleContent = false; + RenderObject* r = renderer()->firstChild(); + while (r) { + if (r->style()->visibility() == VISIBLE && !r->hasLayer()) { + m_hasVisibleContent = true; + break; + } + if (r->firstChild() && !r->hasLayer()) + r = r->firstChild(); + else if (r->nextSibling()) + r = r->nextSibling(); + else { + do { + r = r->parent(); + if (r == renderer()) + r = 0; + } while (r && !r->nextSibling()); + if (r) + r = r->nextSibling(); + } + } + } + m_visibleContentStatusDirty = false; + } +} + +void RenderLayer::dirty3DTransformedDescendantStatus() +{ + RenderLayer* curr = stackingContext(); + if (curr) + curr->m_3DTransformedDescendantStatusDirty = true; + + // This propagates up through preserve-3d hierarchies to the enclosing flattening layer. + // Note that preserves3D() creates stacking context, so we can just run up the stacking contexts. + while (curr && curr->preserves3D()) { + curr->m_3DTransformedDescendantStatusDirty = true; + curr = curr->stackingContext(); + } +} + +// Return true if this layer or any preserve-3d descendants have 3d. +bool RenderLayer::update3DTransformedDescendantStatus() +{ + if (m_3DTransformedDescendantStatusDirty) { + m_has3DTransformedDescendant = false; + + // Transformed or preserve-3d descendants can only be in the z-order lists, not + // in the normal flow list, so we only need to check those. + if (m_posZOrderList) { + for (unsigned i = 0; i < m_posZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_posZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + + // Now check our negative z-index children. + if (m_negZOrderList) { + for (unsigned i = 0; i < m_negZOrderList->size(); ++i) + m_has3DTransformedDescendant |= m_negZOrderList->at(i)->update3DTransformedDescendantStatus(); + } + + m_3DTransformedDescendantStatusDirty = false; + } + + // If we live in a 3d hierarchy, then the layer at the root of that hierarchy needs + // the m_has3DTransformedDescendant set. + if (preserves3D()) + return has3DTransform() || m_has3DTransformedDescendant; + + return has3DTransform(); +} + +void RenderLayer::updateLayerPosition() +{ + IntPoint localPoint; + IntSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done. + if (renderer()->isRenderInline()) { + RenderInline* inlineFlow = toRenderInline(renderer()); + IntRect lineBox = inlineFlow->linesBoundingBox(); + setWidth(lineBox.width()); + setHeight(lineBox.height()); + inlineBoundingBoxOffset = IntSize(lineBox.x(), lineBox.y()); + localPoint += inlineBoundingBoxOffset; + } else if (RenderBox* box = renderBox()) { + setWidth(box->width()); + setHeight(box->height()); + localPoint += box->locationOffsetIncludingFlipping(); + } + + // Clear our cached clip rect information. + clearClipRects(); + + if (!renderer()->isPositioned() && renderer()->parent()) { + // We must adjust our position by walking up the render tree looking for the + // nearest enclosing object with a layer. + RenderObject* curr = renderer()->parent(); + while (curr && !curr->hasLayer()) { + if (curr->isBox() && !curr->isTableRow()) { + // Rows and cells share the same coordinate space (that of the section). + // Omit them when computing our xpos/ypos. + localPoint += toRenderBox(curr)->locationOffsetIncludingFlipping(); + } + curr = curr->parent(); + } + if (curr->isBox() && curr->isTableRow()) { + // Put ourselves into the row coordinate space. + localPoint -= toRenderBox(curr)->locationOffsetIncludingFlipping(); + } + } + + // Subtract our parent's scroll offset. + if (renderer()->isPositioned() && enclosingPositionedAncestor()) { + RenderLayer* positionedParent = enclosingPositionedAncestor(); + + // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. + IntSize offset = positionedParent->scrolledContentOffset(); + localPoint -= offset; + + if (renderer()->isPositioned() && positionedParent->renderer()->isRelPositioned() && positionedParent->renderer()->isRenderInline()) { + IntSize offset = toRenderInline(positionedParent->renderer())->relativePositionedInlineOffset(toRenderBox(renderer())); + localPoint += offset; + } + } else if (parent()) { + if (isComposited()) { + // FIXME: Composited layers ignore pagination, so about the best we can do is make sure they're offset into the appropriate column. + // They won't split across columns properly. + IntSize columnOffset; + parent()->renderer()->adjustForColumns(columnOffset, localPoint); + localPoint += columnOffset; + } + + IntSize scrollOffset = parent()->scrolledContentOffset(); + localPoint -= scrollOffset; + } + + m_relX = m_relY = 0; + if (renderer()->isRelPositioned()) { + m_relX = renderer()->relativePositionOffsetX(); + m_relY = renderer()->relativePositionOffsetY(); + localPoint.move(m_relX, m_relY); + } + + // FIXME: We'd really like to just get rid of the concept of a layer rectangle and rely on the renderers. + localPoint -= inlineBoundingBoxOffset; + setLocation(localPoint.x(), localPoint.y()); +} + +TransformationMatrix RenderLayer::perspectiveTransform() const +{ + if (!renderer()->hasTransform()) + return TransformationMatrix(); + + RenderStyle* style = renderer()->style(); + if (!style->hasPerspective()) + return TransformationMatrix(); + + // Maybe fetch the perspective from the backing? + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + const float boxWidth = borderBox.width(); + const float boxHeight = borderBox.height(); + + float perspectiveOriginX = style->perspectiveOriginX().calcFloatValue(boxWidth); + float perspectiveOriginY = style->perspectiveOriginY().calcFloatValue(boxHeight); + + // A perspective origin of 0,0 makes the vanishing point in the center of the element. + // We want it to be in the top-left, so subtract half the height and width. + perspectiveOriginX -= boxWidth / 2.0f; + perspectiveOriginY -= boxHeight / 2.0f; + + TransformationMatrix t; + t.translate(perspectiveOriginX, perspectiveOriginY); + t.applyPerspective(style->perspective()); + t.translate(-perspectiveOriginX, -perspectiveOriginY); + + return t; +} + +FloatPoint RenderLayer::perspectiveOrigin() const +{ + if (!renderer()->hasTransform()) + return FloatPoint(); + + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + RenderStyle* style = renderer()->style(); + + return FloatPoint(style->perspectiveOriginX().calcFloatValue(borderBox.width()), + style->perspectiveOriginY().calcFloatValue(borderBox.height())); +} + +RenderLayer* RenderLayer::stackingContext() const +{ + RenderLayer* layer = parent(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) || ENABLE(ANDROID_OVERFLOW_SCROLL) + // When using composited fixed elements, they are turned into a stacking + // context and we thus need to return them. + // We can simplify the while loop by using isStackingContext(); with + // composited fixed elements turned on, this will return true for them, + // and is otherwise equivalent to the replaced statements. + while (layer && !layer->renderer()->isRoot() && !layer->isStackingContext()) +#else + while (layer && !layer->renderer()->isRenderView() && !layer->renderer()->isRoot() && layer->renderer()->style()->hasAutoZIndex()) +#endif + layer = layer->parent(); + return layer; +} + +static inline bool isPositionedContainer(RenderLayer* layer) +{ + RenderObject* o = layer->renderer(); + return o->isRenderView() || o->isPositioned() || o->isRelPositioned() || layer->hasTransform(); +} + +static inline bool isFixedPositionedContainer(RenderLayer* layer) +{ + RenderObject* o = layer->renderer(); + return o->isRenderView() || layer->hasTransform(); +} + +RenderLayer* RenderLayer::enclosingPositionedAncestor() const +{ + RenderLayer* curr = parent(); + while (curr && !isPositionedContainer(curr)) + curr = curr->parent(); + + return curr; +} + +RenderLayer* RenderLayer::enclosingTransformedAncestor() const +{ + RenderLayer* curr = parent(); + while (curr && !curr->renderer()->isRenderView() && !curr->transform()) + curr = curr->parent(); + + return curr; +} + +static inline const RenderLayer* compositingContainer(const RenderLayer* layer) +{ + return layer->isNormalFlowOnly() ? layer->parent() : layer->stackingContext(); +} + +#if USE(ACCELERATED_COMPOSITING) +RenderLayer* RenderLayer::enclosingCompositingLayer(bool includeSelf) const +{ + if (includeSelf && isComposited()) + return const_cast(this); + + for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { + if (curr->isComposited()) + return const_cast(curr); + } + + return 0; +} +#endif + +RenderLayer* RenderLayer::clippingRoot() const +{ +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) + return const_cast(this); +#endif + + const RenderLayer* current = this; + while (current) { + if (current->renderer()->isRenderView()) + return const_cast(current); + + current = compositingContainer(current); + ASSERT(current); + if (current->transform() +#if USE(ACCELERATED_COMPOSITING) + || current->isComposited() +#endif + ) + return const_cast(current); + } + + ASSERT_NOT_REACHED(); + return 0; +} + +IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const +{ + // We don't use convertToLayerCoords because it doesn't know about transforms + return roundedIntPoint(renderer()->absoluteToLocal(absolutePoint, false, true)); +} + +bool RenderLayer::requiresSlowRepaints() const +{ + if (isTransparent() || hasReflection() || hasTransform()) + return true; + if (!parent()) + return false; + return parent()->requiresSlowRepaints(); +} + +bool RenderLayer::isTransparent() const +{ +#if ENABLE(SVG) + if (renderer()->node() && renderer()->node()->namespaceURI() == SVGNames::svgNamespaceURI) + return false; +#endif + return renderer()->isTransparent() || renderer()->hasMask(); +} + +RenderLayer* RenderLayer::transparentPaintingAncestor() +{ + if (isComposited()) + return 0; + + for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { + if (curr->isComposited()) + return 0; + if (curr->isTransparent()) + return curr; + } + return 0; +} + +static IntRect transparencyClipBox(const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior); + +static void expandClipRectForDescendantsAndReflection(IntRect& clipRect, const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +{ + // If we have a mask, then the clip is limited to the border box area (and there is + // no need to examine child layers). + if (!l->renderer()->hasMask()) { + // Note: we don't have to walk z-order lists since transparent elements always establish + // a stacking context. This means we can just walk the layer tree directly. + for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { + if (!l->reflection() || l->reflectionLayer() != curr) + clipRect.unite(transparencyClipBox(curr, rootLayer, paintBehavior)); + } + } + + // If we have a reflection, then we need to account for that when we push the clip. Reflect our entire + // current transparencyClipBox to catch all child layers. + // FIXME: Accelerated compositing will eventually want to do something smart here to avoid incorporating this + // size into the parent layer. + if (l->renderer()->hasReflection()) { + int deltaX = 0; + int deltaY = 0; + l->convertToLayerCoords(rootLayer, deltaX, deltaY); + clipRect.move(-deltaX, -deltaY); + clipRect.unite(l->renderBox()->reflectedRect(clipRect)); + clipRect.move(deltaX, deltaY); + } +} + +static IntRect transparencyClipBox(const RenderLayer* l, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +{ + // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the + // paintDirtyRect, and that should cut down on the amount we have to paint. Still it + // would be better to respect clips. + + if (rootLayer != l && l->paintsWithTransform(paintBehavior)) { + // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass + // the transformed layer and all of its children. + int x = 0; + int y = 0; + l->convertToLayerCoords(rootLayer, x, y); + + TransformationMatrix transform; + transform.translate(x, y); + transform = *l->transform() * transform; + + IntRect clipRect = l->boundingBox(l); + expandClipRectForDescendantsAndReflection(clipRect, l, l, paintBehavior); + return transform.mapRect(clipRect); + } + + IntRect clipRect = l->boundingBox(rootLayer); + expandClipRectForDescendantsAndReflection(clipRect, l, rootLayer, paintBehavior); + return clipRect; +} + +void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer, PaintBehavior paintBehavior) +{ + if (p->paintingDisabled() || (paintsWithTransparency(paintBehavior) && m_usedTransparency)) + return; + + RenderLayer* ancestor = transparentPaintingAncestor(); + if (ancestor) + ancestor->beginTransparencyLayers(p, rootLayer, paintBehavior); + + if (paintsWithTransparency(paintBehavior)) { + m_usedTransparency = true; + p->save(); + IntRect clipRect = transparencyClipBox(this, rootLayer, paintBehavior); + p->clip(clipRect); + p->beginTransparencyLayer(renderer()->opacity()); +#ifdef REVEAL_TRANSPARENCY_LAYERS + p->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), ColorSpaceDeviceRGB); + p->fillRect(clipRect); +#endif + } +} + +void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void RenderLayer::operator delete(void* ptr, size_t sz) +{ + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +void RenderLayer::destroy(RenderArena* renderArena) +{ + delete this; + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) +{ + RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild(); + if (prevSibling) { + child->setPreviousSibling(prevSibling); + prevSibling->setNextSibling(child); + ASSERT(prevSibling != child); + } else + setFirstChild(child); + + if (beforeChild) { + beforeChild->setPreviousSibling(child); + child->setNextSibling(beforeChild); + ASSERT(beforeChild != child); + } else + setLastChild(child); + + child->setParent(this); + + if (child->isNormalFlowOnly()) + dirtyNormalFlowList(); + + if (!child->isNormalFlowOnly() || child->firstChild()) { + // Dirty the z-order list in which we are contained. The stackingContext() can be null in the + // case where we're building up generated content layers. This is ok, since the lists will start + // off dirty in that case anyway. + child->dirtyStackingContextZOrderLists(); + } + + child->updateVisibilityStatus(); + if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) + childVisibilityChanged(true); + +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWasAdded(this, child); +#endif +} + +RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) +{ +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(this, oldChild); +#endif + + // remove the child + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (m_first == oldChild) + m_first = oldChild->nextSibling(); + if (m_last == oldChild) + m_last = oldChild->previousSibling(); + + if (oldChild->isNormalFlowOnly()) + dirtyNormalFlowList(); + if (!oldChild->isNormalFlowOnly() || oldChild->firstChild()) { + // Dirty the z-order list in which we are contained. When called via the + // reattachment process in removeOnlyThisLayer, the layer may already be disconnected + // from the main layer tree, so we need to null-check the |stackingContext| value. + oldChild->dirtyStackingContextZOrderLists(); + } + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + oldChild->updateVisibilityStatus(); + if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) + childVisibilityChanged(false); + + return oldChild; +} + +void RenderLayer::removeOnlyThisLayer() +{ + if (!m_parent) + return; + + // Mark that we are about to lose our layer. This makes render tree + // walks ignore this layer while we're removing it. + m_renderer->setHasLayer(false); + +#if USE(ACCELERATED_COMPOSITING) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + + // Dirty the clip rects. + clearClipRectsIncludingDescendants(); + + // Remove us from the parent. + RenderLayer* parent = m_parent; + RenderLayer* nextSib = nextSibling(); + parent->removeChild(this); + + if (reflection()) + removeChild(reflectionLayer()); + + // Now walk our kids and reattach them to our parent. + RenderLayer* current = m_first; + while (current) { + RenderLayer* next = current->nextSibling(); + removeChild(current); + parent->addChild(current, nextSib); + current->updateLayerPositions(); // Depends on hasLayer() already being false for proper layout. + current = next; + } + + m_renderer->destroyLayer(); +} + +void RenderLayer::insertOnlyThisLayer() +{ + if (!m_parent && renderer()->parent()) { + // We need to connect ourselves when our renderer() has a parent. + // Find our enclosingLayer and add ourselves. + RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); + ASSERT(parentLayer); + RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; + parentLayer->addChild(this, beforeChild); + } + + // Remove all descendant layers from the hierarchy and add them to the new position. + for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) + curr->moveLayers(m_parent, this); + + // Clear out all the clip rects. + clearClipRectsIncludingDescendants(); +} + +void +RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& xPos, int& yPos) const +{ + if (ancestorLayer == this) + return; + + EPosition position = renderer()->style()->position(); + if (position == FixedPosition && (!ancestorLayer || ancestorLayer == renderer()->view()->layer())) { + // If the fixed layer's container is the root, just add in the offset of the view. We can obtain this by calling + // localToAbsolute() on the RenderView. + FloatPoint absPos = renderer()->localToAbsolute(FloatPoint(), true); + xPos += absPos.x(); + yPos += absPos.y(); + return; + } + + if (position == FixedPosition) { + // For a fixed layers, we need to walk up to the root to see if there's a fixed position container + // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, + // so we should always find the ancestor at or before we find the fixed position container. + RenderLayer* fixedPositionContainerLayer = 0; + bool foundAncestor = false; + for (RenderLayer* currLayer = parent(); currLayer; currLayer = currLayer->parent()) { + if (currLayer == ancestorLayer) + foundAncestor = true; + + if (isFixedPositionedContainer(currLayer)) { + fixedPositionContainerLayer = currLayer; + ASSERT(foundAncestor); + break; + } + } + + ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. + + if (fixedPositionContainerLayer != ancestorLayer) { + int fixedContainerX = 0; + int fixedContainerY = 0; + convertToLayerCoords(fixedPositionContainerLayer, fixedContainerX, fixedContainerY); + + int ancestorX = 0; + int ancestorY = 0; + ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorX, ancestorY); + + xPos += (fixedContainerX - ancestorX); + yPos += (fixedContainerY - ancestorY); + return; + } + } + + + RenderLayer* parentLayer; + if (position == AbsolutePosition || position == FixedPosition) { + // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. + parentLayer = parent(); + bool foundAncestorFirst = false; + while (parentLayer) { + if (isPositionedContainer(parentLayer)) + break; + + if (parentLayer == ancestorLayer) { + foundAncestorFirst = true; + break; + } + + parentLayer = parentLayer->parent(); + } + + if (foundAncestorFirst) { + // Found ancestorLayer before the abs. positioned container, so compute offset of both relative + // to enclosingPositionedAncestor and subtract. + RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor(); + + int thisX = 0; + int thisY = 0; + convertToLayerCoords(positionedAncestor, thisX, thisY); + + int ancestorX = 0; + int ancestorY = 0; + ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorX, ancestorY); + + xPos += (thisX - ancestorX); + yPos += (thisY - ancestorY); + return; + } + } else + parentLayer = parent(); + + if (!parentLayer) + return; + + parentLayer->convertToLayerCoords(ancestorLayer, xPos, yPos); + + xPos += x(); + yPos += y(); +} + +static inline int adjustedScrollDelta(int beginningDelta) { + // This implemention matches Firefox's. + // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856. + const int speedReducer = 12; + + int adjustedDelta = beginningDelta / speedReducer; + if (adjustedDelta > 1) + adjustedDelta = static_cast(adjustedDelta * sqrt(static_cast(adjustedDelta))) - 1; + else if (adjustedDelta < -1) + adjustedDelta = static_cast(adjustedDelta * sqrt(static_cast(-adjustedDelta))) + 1; + + return adjustedDelta; +} + +void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) +{ + Frame* frame = renderer()->frame(); + if (!frame) + return; + + IntPoint currentMousePosition = frame->eventHandler()->currentMousePosition(); + + // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent + static IntPoint previousMousePosition; + if (currentMousePosition.x() < 0 || currentMousePosition.y() < 0) + currentMousePosition = previousMousePosition; + else + previousMousePosition = currentMousePosition; + + int xDelta = currentMousePosition.x() - sourcePoint.x(); + int yDelta = currentMousePosition.y() - sourcePoint.y(); + + if (abs(xDelta) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon + xDelta = 0; + if (abs(yDelta) <= ScrollView::noPanScrollRadius) + yDelta = 0; + + scrollByRecursively(adjustedScrollDelta(xDelta), adjustedScrollDelta(yDelta)); +} + +void RenderLayer::scrollByRecursively(int xDelta, int yDelta) +{ + if (!xDelta && !yDelta) + return; + + bool restrictedByLineClamp = false; + if (renderer()->parent()) + restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); + + if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { + int newOffsetX = scrollXOffset() + xDelta; + int newOffsetY = scrollYOffset() + yDelta; + scrollToOffset(newOffsetX, newOffsetY); + + // If this layer can't do the scroll we ask the next layer up that can scroll to try + int leftToScrollX = newOffsetX - scrollXOffset(); + int leftToScrollY = newOffsetY - scrollYOffset(); + if ((leftToScrollX || leftToScrollY) && renderer()->parent()) { + RenderObject* nextRenderer = renderer()->parent(); + while (nextRenderer) { + if (nextRenderer->isBox() && toRenderBox(nextRenderer)->canBeScrolledAndHasScrollableArea()) { + nextRenderer->enclosingLayer()->scrollByRecursively(leftToScrollX, leftToScrollY); + break; + } + nextRenderer = nextRenderer->parent(); + } + + Frame* frame = renderer()->frame(); + if (frame) + frame->eventHandler()->updateAutoscrollRenderer(); + } + } else if (renderer()->view()->frameView()) { + // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't + // have an overflow clip. Which means that it is a document node that can be scrolled. + renderer()->view()->frameView()->scrollBy(IntSize(xDelta, yDelta)); + // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement? + // https://bugs.webkit.org/show_bug.cgi?id=28237 + } +} + +void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint) +{ + RenderBox* box = renderBox(); + if (!box) + return; + + if (box->style()->overflowX() != OMARQUEE) { + if (x < 0) x = 0; + if (y < 0) y = 0; + + // Call the scrollWidth/Height functions so that the dimensions will be computed if they need + // to be (for overflow:hidden blocks). + int maxX = scrollWidth() - box->clientWidth(); + int maxY = scrollHeight() - box->clientHeight(); + + if (x > maxX) x = maxX; + if (y > maxY) y = maxY; + } + + // FIXME: Eventually, we will want to perform a blit. For now never + // blit, since the check for blitting is going to be very + // complicated (since it will involve testing whether our layer + // is either occluded by another layer or clipped by an enclosing + // layer or contains fixed backgrounds, etc.). + int newScrollX = x - m_scrollOrigin.x(); + int newScrollY = y - m_scrollOrigin.y(); + if (m_scrollY == newScrollY && m_scrollX == newScrollX) + return; + m_scrollX = newScrollX; + m_scrollY = newScrollY; + + // Update the positions of our child layers. Don't have updateLayerPositions() update + // compositing layers, because we need to do a deep update from the compositing ancestor. + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->updateLayerPositions(0); + + RenderView* view = renderer()->view(); + + // We should have a RenderView if we're trying to scroll. + ASSERT(view); + if (view) { +#if ENABLE(DASHBOARD_SUPPORT) + // Update dashboard regions, scrolling may change the clip of a + // particular region. + view->frameView()->updateDashboardRegions(); +#endif + + view->updateWidgetPositions(); + } + +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode()) { + // Our stacking context is guaranteed to contain all of our descendants that may need + // repositioning, so update compositing layers from there. + if (RenderLayer* compositingAncestor = stackingContext()->enclosingCompositingLayer()) { + if (compositor()->compositingConsultsOverlap()) + compositor()->updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); + else { + bool isUpdateRoot = true; + compositingAncestor->backing()->updateAfterLayout(RenderLayerBacking::AllDescendants, isUpdateRoot); + } + } + } +#endif + + RenderBoxModelObject* repaintContainer = renderer()->containerForRepaint(); + IntRect rectForRepaint = renderer()->clippedOverflowRectForRepaint(repaintContainer); + + Frame* frame = renderer()->frame(); + if (frame) { + // The caret rect needs to be invalidated after scrolling + frame->selection()->setCaretRectNeedsUpdate(); + + FloatQuad quadForFakeMouseMoveEvent = FloatQuad(rectForRepaint); + if (repaintContainer) + quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent); + frame->eventHandler()->dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent); + } + + // Just schedule a full repaint of our object. + if (view && repaint) + renderer()->repaintUsingContainer(repaintContainer, rectForRepaint); + + if (updateScrollbars) { + if (m_hBar) + m_hBar->setValue(scrollXOffset(), Scrollbar::NotFromScrollAnimator); + if (m_vBar) + m_vBar->setValue(m_scrollY, Scrollbar::NotFromScrollAnimator); + } + + // Schedule the scroll DOM event. + if (view) { + if (FrameView* frameView = view->frameView()) + frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), renderer()->node()); + } +} + +void RenderLayer::scrollRectToVisible(const IntRect& rect, bool scrollToAnchor, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +{ + RenderLayer* parentLayer = 0; + IntRect newRect = rect; + int xOffset = 0, yOffset = 0; + + // We may end up propagating a scroll event. It is important that we suspend events until + // the end of the function since they could delete the layer or the layer's renderer(). + FrameView* frameView = renderer()->document()->view(); + if (frameView) + frameView->pauseScheduledEvents(); + + bool restrictedByLineClamp = false; + if (renderer()->parent()) { + parentLayer = renderer()->parent()->enclosingLayer(); + restrictedByLineClamp = !renderer()->parent()->style()->lineClamp().isNone(); + } + + if (renderer()->hasOverflowClip() && !restrictedByLineClamp) { + // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. + // This will prevent us from revealing text hidden by the slider in Safari RSS. + RenderBox* box = renderBox(); + ASSERT(box); + FloatPoint absPos = box->localToAbsolute(); + absPos.move(box->borderLeft(), box->borderTop()); + + IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), box->clientWidth(), box->clientHeight()); + IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); + IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); + + xOffset = r.x() - absPos.x(); + yOffset = r.y() - absPos.y(); + // Adjust offsets if they're outside of the allowable range. + xOffset = max(0, min(scrollWidth() - layerBounds.width(), xOffset)); + yOffset = max(0, min(scrollHeight() - layerBounds.height(), yOffset)); + + if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) { + int diffX = scrollXOffset(); + int diffY = scrollYOffset(); + scrollToOffset(xOffset, yOffset); + diffX = scrollXOffset() - diffX; + diffY = scrollYOffset() - diffY; + newRect.setX(rect.x() - diffX); + newRect.setY(rect.y() - diffY); + } + } else if (!parentLayer && renderer()->isBox() && renderBox()->canBeProgramaticallyScrolled(scrollToAnchor)) { + if (frameView) { + if (renderer()->document() && renderer()->document()->ownerElement() && renderer()->document()->ownerElement()->renderer()) { + IntRect viewRect = frameView->visibleContentRect(); + IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); + + xOffset = r.x(); + yOffset = r.y(); + // Adjust offsets if they're outside of the allowable range. + xOffset = max(0, min(frameView->contentsWidth(), xOffset)); + yOffset = max(0, min(frameView->contentsHeight(), yOffset)); + + frameView->setScrollPosition(IntPoint(xOffset, yOffset)); + parentLayer = renderer()->document()->ownerElement()->renderer()->enclosingLayer(); + newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); + newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); + } else { + IntRect viewRect = frameView->visibleContentRect(); + IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); + + frameView->setScrollPosition(r.location()); + + // This is the outermost view of a web page, so after scrolling this view we + // scroll its container by calling Page::scrollRectIntoView. + // This only has an effect on the Mac platform in applications + // that put web views into scrolling containers, such as Mac OS X Mail. + // The canAutoscroll function in EventHandler also knows about this. + if (Frame* frame = frameView->frame()) { + if (Page* page = frame->page()) + page->chrome()->scrollRectIntoView(rect); + } + } + } + } + + if (parentLayer) + parentLayer->scrollRectToVisible(newRect, scrollToAnchor, alignX, alignY); + + if (frameView) + frameView->resumeScheduledEvents(); +} + +IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +{ + // Determine the appropriate X behavior. + ScrollBehavior scrollX; + IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height()); + int intersectWidth = intersection(visibleRect, exposeRectX).width(); + if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) + // If the rectangle is fully visible, use the specified visible behavior. + // If the rectangle is partially visible, but over a certain threshold, + // then treat it as fully visible to avoid unnecessary horizontal scrolling + scrollX = ScrollAlignment::getVisibleBehavior(alignX); + else if (intersectWidth == visibleRect.width()) { + // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. + scrollX = ScrollAlignment::getVisibleBehavior(alignX); + if (scrollX == alignCenter) + scrollX = noScroll; + } else if (intersectWidth > 0) + // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior + scrollX = ScrollAlignment::getPartialBehavior(alignX); + else + scrollX = ScrollAlignment::getHiddenBehavior(alignX); + // If we're trying to align to the closest edge, and the exposeRect is further right + // than the visibleRect, and not bigger than the visible area, then align with the right. + if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width()) + scrollX = alignRight; + + // Given the X behavior, compute the X coordinate. + int x; + if (scrollX == noScroll) + x = visibleRect.x(); + else if (scrollX == alignRight) + x = exposeRect.right() - visibleRect.width(); + else if (scrollX == alignCenter) + x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2; + else + x = exposeRect.x(); + + // Determine the appropriate Y behavior. + ScrollBehavior scrollY; + IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); + int intersectHeight = intersection(visibleRect, exposeRectY).height(); + if (intersectHeight == exposeRect.height()) + // If the rectangle is fully visible, use the specified visible behavior. + scrollY = ScrollAlignment::getVisibleBehavior(alignY); + else if (intersectHeight == visibleRect.height()) { + // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. + scrollY = ScrollAlignment::getVisibleBehavior(alignY); + if (scrollY == alignCenter) + scrollY = noScroll; + } else if (intersectHeight > 0) + // If the rectangle is partially visible, use the specified partial behavior + scrollY = ScrollAlignment::getPartialBehavior(alignY); + else + scrollY = ScrollAlignment::getHiddenBehavior(alignY); + // If we're trying to align to the closest edge, and the exposeRect is further down + // than the visibleRect, and not bigger than the visible area, then align with the bottom. + if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height()) + scrollY = alignBottom; + + // Given the Y behavior, compute the Y coordinate. + int y; + if (scrollY == noScroll) + y = visibleRect.y(); + else if (scrollY == alignBottom) + y = exposeRect.bottom() - visibleRect.height(); + else if (scrollY == alignCenter) + y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2; + else + y = exposeRect.y(); + + return IntRect(IntPoint(x, y), visibleRect.size()); +} + +void RenderLayer::autoscroll() +{ + Frame* frame = renderer()->frame(); + if (!frame) + return; + + FrameView* frameView = frame->view(); + if (!frameView) + return; + +#if ENABLE(DRAG_SUPPORT) + frame->eventHandler()->updateSelectionForMouseDrag(); +#endif + + IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); + scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); +} + +void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset) +{ + // FIXME: This should be possible on generated content but is not right now. + if (!inResizeMode() || !renderer()->hasOverflowClip() || !renderer()->node()) + return; + + // Set the width and height of the shadow ancestor node if there is one. + // This is necessary for textarea elements since the resizable layer is in the shadow content. + Element* element = static_cast(renderer()->node()->shadowAncestorNode()); + RenderBox* renderer = toRenderBox(element->renderer()); + + EResize resize = renderer->style()->resize(); + if (resize == RESIZE_NONE) + return; + + Document* document = element->document(); + if (!document->frame()->eventHandler()->mousePressed()) + return; + + float zoomFactor = renderer->style()->effectiveZoom(); + + IntSize newOffset = offsetFromResizeCorner(document->view()->windowToContents(evt.pos())); + newOffset.setWidth(newOffset.width() / zoomFactor); + newOffset.setHeight(newOffset.height() / zoomFactor); + + IntSize currentSize = IntSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor); + IntSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize); + element->setMinimumSizeForResizing(minimumSize); + + IntSize adjustedOldOffset = IntSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor); + + IntSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize; + + CSSStyleDeclaration* style = element->style(); + bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX; + + ExceptionCode ec; + + if (resize != RESIZE_VERTICAL && difference.width()) { + if (element->isFormControlElement()) { + // Make implicit margins from the theme explicit (see ). + style->setProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false, ec); + style->setProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false, ec); + } + int baseWidth = renderer->width() - (isBoxSizingBorder ? 0 : renderer->borderAndPaddingWidth()); + baseWidth = baseWidth / zoomFactor; + style->setProperty(CSSPropertyWidth, String::number(baseWidth + difference.width()) + "px", false, ec); + } + + if (resize != RESIZE_HORIZONTAL && difference.height()) { + if (element->isFormControlElement()) { + // Make implicit margins from the theme explicit (see ). + style->setProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false, ec); + style->setProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false, ec); + } + int baseHeight = renderer->height() - (isBoxSizingBorder ? 0 : renderer->borderAndPaddingHeight()); + baseHeight = baseHeight / zoomFactor; + style->setProperty(CSSPropertyHeight, String::number(baseHeight + difference.height()) + "px", false, ec); + } + + document->updateLayout(); + + // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view. +} + +int RenderLayer::scrollSize(ScrollbarOrientation orientation) const +{ + Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_hBar : m_vBar).get(); + return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0; +} + +void RenderLayer::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_hBar) + m_hBar->setValue(offset.x(), Scrollbar::FromScrollAnimator); + if (m_vBar) + m_vBar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + +void RenderLayer::valueChanged(Scrollbar*) +{ + // Update scroll position from scrollbars. + + bool needUpdate = false; + int newX = scrollXOffset(); + int newY = m_scrollY; + + if (m_hBar) { + newX = m_hBar->value(); + if (newX != scrollXOffset()) + needUpdate = true; + } + + if (m_vBar) { + newY = m_vBar->value(); + if (newY != m_scrollY) + needUpdate = true; + } + + if (needUpdate) + scrollToOffset(newX, newY, false); +} + +bool RenderLayer::isActive() const +{ + Page* page = renderer()->frame()->page(); + return page && page->focusController()->isActive(); +} + + +static IntRect cornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + int horizontalThickness; + int verticalThickness; + if (!layer->verticalScrollbar() && !layer->horizontalScrollbar()) { + // FIXME: This isn't right. We need to know the thickness of custom scrollbars + // even when they don't exist in order to set the resizer square size properly. + horizontalThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness(); + verticalThickness = horizontalThickness; + } else if (layer->verticalScrollbar() && !layer->horizontalScrollbar()) { + horizontalThickness = layer->verticalScrollbar()->width(); + verticalThickness = horizontalThickness; + } else if (layer->horizontalScrollbar() && !layer->verticalScrollbar()) { + verticalThickness = layer->horizontalScrollbar()->height(); + horizontalThickness = verticalThickness; + } else { + horizontalThickness = layer->verticalScrollbar()->width(); + verticalThickness = layer->horizontalScrollbar()->height(); + } + return IntRect(bounds.right() - horizontalThickness - layer->renderer()->style()->borderRightWidth(), + bounds.bottom() - verticalThickness - layer->renderer()->style()->borderBottomWidth(), + horizontalThickness, verticalThickness); +} + +static IntRect scrollCornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box. + // This happens when: + // (a) A resizer is present and at least one scrollbar is present + // (b) Both scrollbars are present. + bool hasHorizontalBar = layer->horizontalScrollbar(); + bool hasVerticalBar = layer->verticalScrollbar(); + bool hasResizer = layer->renderer()->style()->resize() != RESIZE_NONE; + if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar))) + return cornerRect(layer, bounds); + return IntRect(); +} + +static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + ASSERT(layer->renderer()->isBox()); + if (layer->renderer()->style()->resize() == RESIZE_NONE) + return IntRect(); + return cornerRect(layer, bounds); +} + +bool RenderLayer::scrollbarCornerPresent() const +{ + ASSERT(renderer()->isBox()); + return !scrollCornerRect(this, renderBox()->borderBoxRect()).isEmpty(); +} + +IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const +{ + RenderView* view = renderer()->view(); + if (!view) + return scrollbarRect; + + IntRect rect = scrollbarRect; + rect.move(scrollbarOffset(scrollbar)); + + return view->frameView()->convertFromRenderer(renderer(), rect); +} + +IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const +{ + RenderView* view = renderer()->view(); + if (!view) + return parentRect; + + IntRect rect = view->frameView()->convertToRenderer(renderer(), parentRect); + rect.move(-scrollbarOffset(scrollbar)); + return rect; +} + +IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const +{ + RenderView* view = renderer()->view(); + if (!view) + return scrollbarPoint; + + IntPoint point = scrollbarPoint; + point.move(scrollbarOffset(scrollbar)); + return view->frameView()->convertFromRenderer(renderer(), point); +} + +IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const +{ + RenderView* view = renderer()->view(); + if (!view) + return parentPoint; + + IntPoint point = view->frameView()->convertToRenderer(renderer(), parentPoint); + + point.move(-scrollbarOffset(scrollbar)); + return point; +} + +IntSize RenderLayer::scrollbarOffset(const Scrollbar* scrollbar) const +{ + RenderBox* box = renderBox(); + + if (scrollbar == m_vBar.get()) + return IntSize(box->width() - box->borderRight() - scrollbar->width(), box->borderTop()); + + if (scrollbar == m_hBar.get()) + return IntSize(box->borderLeft(), box->height() - box->borderBottom() - scrollbar->height()); + + ASSERT_NOT_REACHED(); + return IntSize(); +} + +void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect scrollRect = rect; + RenderBox* box = renderBox(); + ASSERT(box); + if (scrollbar == m_vBar.get()) + scrollRect.move(box->width() - box->borderRight() - scrollbar->width(), box->borderTop()); + else + scrollRect.move(box->borderLeft(), box->height() - box->borderBottom() - scrollbar->height()); + renderer()->repaintRectangle(scrollRect); +} + +PassRefPtr RenderLayer::createScrollbar(ScrollbarOrientation orientation) +{ + RefPtr widget; + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style()->hasPseudoStyle(SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(this, orientation, toRenderBox(actualRenderer)); + else + widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); + renderer()->document()->view()->addChild(widget.get()); + return widget.release(); +} + +void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) +{ + RefPtr& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar; + if (scrollbar) { + if (scrollbar->isCustomScrollbar()) + static_cast(scrollbar.get())->clearOwningRenderer(); + + scrollbar->removeFromParent(); + scrollbar->setClient(0); + scrollbar = 0; + } +} + +void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_hBar != 0)) + return; + + if (hasScrollbar) + m_hBar = createScrollbar(HorizontalScrollbar); + else + destroyScrollbar(HorizontalScrollbar); + + // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (renderer()->document()->hasDashboardRegions()) + renderer()->document()->setDashboardRegionsDirty(true); +#endif +} + +void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_vBar != 0)) + return; + + if (hasScrollbar) + m_vBar = createScrollbar(VerticalScrollbar); + else + destroyScrollbar(VerticalScrollbar); + + // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (renderer()->document()->hasDashboardRegions()) + renderer()->document()->setDashboardRegionsDirty(true); +#endif +} + +int RenderLayer::verticalScrollbarWidth() const +{ + if (!m_vBar) + return 0; + return m_vBar->width(); +} + +int RenderLayer::horizontalScrollbarHeight() const +{ + if (!m_hBar) + return 0; + return m_hBar->height(); +} + +IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const +{ + // Currently the resize corner is always the bottom right corner + IntPoint bottomRight(width(), height()); + IntPoint localPoint = absoluteToContents(absolutePoint); + return localPoint - bottomRight; +} + +bool RenderLayer::hasOverflowControls() const +{ + return m_hBar || m_vBar || m_scrollCorner || renderer()->style()->resize() != RESIZE_NONE; +} +#if ENABLE(ANDROID_OVERFLOW_SCROLL) +bool RenderLayer::hasOverflowParent() const +{ + const RenderLayer* layer = this; + while (layer && !layer->hasOverflowScroll()) + layer = layer->parent(); + return layer; +} +#endif + +void RenderLayer::positionOverflowControls(int tx, int ty) +{ + if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) + return; + + RenderBox* box = renderBox(); + if (!box) + return; + + IntRect borderBox = box->borderBoxRect(); + IntRect scrollCorner(scrollCornerRect(this, borderBox)); + IntRect absBounds(borderBox.x() + tx, borderBox.y() + ty, borderBox.width(), borderBox.height()); + if (m_vBar) + m_vBar->setFrameRect(IntRect(absBounds.right() - box->borderRight() - m_vBar->width(), + absBounds.y() + box->borderTop(), + m_vBar->width(), + absBounds.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height())); + + if (m_hBar) + m_hBar->setFrameRect(IntRect(absBounds.x() + box->borderLeft(), + absBounds.bottom() - box->borderBottom() - m_hBar->height(), + absBounds.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(), + m_hBar->height())); + + if (m_scrollCorner) + m_scrollCorner->setFrameRect(scrollCorner); + if (m_resizer) + m_resizer->setFrameRect(resizerCornerRect(this, borderBox)); +} + +#if PLATFORM(ANDROID) +// When width/height change, the scrollWidth/scrollHeight should be dirty. +// And this should be upstreamed to webkit. +void RenderLayer::setWidth(int w) +{ + if (m_width != w) { + m_scrollDimensionsDirty = true; + m_width = w; + } +} + +void RenderLayer::setHeight(int h) +{ + if (m_height != h) { + m_scrollDimensionsDirty = true; + m_height = h; + } +} +#endif + +int RenderLayer::scrollWidth() +{ + if (m_scrollDimensionsDirty) + computeScrollDimensions(); + return m_scrollWidth; +} + +int RenderLayer::scrollHeight() +{ + if (m_scrollDimensionsDirty) + computeScrollDimensions(); + return m_scrollHeight; +} + +int RenderLayer::overflowTop() const +{ + RenderBox* box = renderBox(); + IntRect overflowRect(box->layoutOverflowRect()); + box->flipForWritingMode(overflowRect); + return overflowRect.y(); +} + +int RenderLayer::overflowBottom() const +{ + RenderBox* box = renderBox(); + IntRect overflowRect(box->layoutOverflowRect()); + box->flipForWritingMode(overflowRect); + return overflowRect.bottom(); +} + +int RenderLayer::overflowLeft() const +{ + RenderBox* box = renderBox(); + IntRect overflowRect(box->layoutOverflowRect()); + box->flipForWritingMode(overflowRect); + return overflowRect.x(); +} + +int RenderLayer::overflowRight() const +{ + RenderBox* box = renderBox(); + IntRect overflowRect(box->layoutOverflowRect()); + box->flipForWritingMode(overflowRect); + return overflowRect.right(); +} + +void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) +{ + RenderBox* box = renderBox(); + ASSERT(box); + + m_scrollDimensionsDirty = false; + + m_scrollLeftOverflow = overflowLeft() - box->borderLeft(); + m_scrollTopOverflow = overflowTop() - box->borderTop(); + + m_scrollWidth = overflowRight() - overflowLeft(); + m_scrollHeight = overflowBottom() - overflowTop(); + + m_scrollOrigin = IntPoint(-m_scrollLeftOverflow, -m_scrollTopOverflow); + + if (needHBar) + *needHBar = m_scrollWidth > box->clientWidth(); + if (needVBar) + *needVBar = m_scrollHeight > box->clientHeight(); +} + +void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow) +{ + if (m_overflowStatusDirty) { + m_horizontalOverflow = horizontalOverflow; + m_verticalOverflow = verticalOverflow; + m_overflowStatusDirty = false; + return; + } + + bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow); + bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow); + + if (horizontalOverflowChanged || verticalOverflowChanged) { + m_horizontalOverflow = horizontalOverflow; + m_verticalOverflow = verticalOverflow; + + if (FrameView* frameView = renderer()->document()->view()) { + frameView->scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), + renderer()->node()); + } + } +} + +void +RenderLayer::updateScrollInfoAfterLayout() +{ + RenderBox* box = renderBox(); + if (!box) + return; + + m_scrollDimensionsDirty = true; + + bool horizontalOverflow, verticalOverflow; + computeScrollDimensions(&horizontalOverflow, &verticalOverflow); + + if (box->style()->overflowX() != OMARQUEE) { + // Layout may cause us to be in an invalid scroll position. In this case we need + // to pull our scroll offsets back to the max (or push them up to the min). + int newX = max(0, min(scrollXOffset(), scrollWidth() - box->clientWidth())); + int newY = max(0, min(m_scrollY, scrollHeight() - box->clientHeight())); + if (newX != scrollXOffset() || newY != m_scrollY) { + RenderView* view = renderer()->view(); + ASSERT(view); + // scrollToOffset() may call updateLayerPositions(), which doesn't work + // with LayoutState. + // FIXME: Remove the disableLayoutState/enableLayoutState if the above changes. + if (view) + view->disableLayoutState(); + scrollToOffset(newX, newY); + if (view) + view->enableLayoutState(); + } + } + + bool haveHorizontalBar = m_hBar; + bool haveVerticalBar = m_vBar; + + // overflow:scroll should just enable/disable. + if (renderer()->style()->overflowX() == OSCROLL) + m_hBar->setEnabled(horizontalOverflow); + if (renderer()->style()->overflowY() == OSCROLL) + m_vBar->setEnabled(verticalOverflow); + + // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any + // scrollbars that may be present. + if (renderer()->style()->overflowX() == OHIDDEN && haveHorizontalBar) + setHasHorizontalScrollbar(false); + if (renderer()->style()->overflowY() == OHIDDEN && haveVerticalBar) + setHasVerticalScrollbar(false); + + // overflow:auto may need to lay out again if scrollbars got added/removed. + bool scrollbarsChanged = (box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || + (box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); + if (scrollbarsChanged) { + if (box->hasAutoHorizontalScrollbar()) + setHasHorizontalScrollbar(horizontalOverflow); + if (box->hasAutoVerticalScrollbar()) + setHasVerticalScrollbar(verticalOverflow); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (renderer()->document()->hasDashboardRegions()) + renderer()->document()->setDashboardRegionsDirty(true); +#endif + + renderer()->repaint(); + + if (renderer()->style()->overflowX() == OAUTO || renderer()->style()->overflowY() == OAUTO) { + if (!m_inOverflowRelayout) { + // Our proprietary overflow: overlay value doesn't trigger a layout. + m_inOverflowRelayout = true; + renderer()->setNeedsLayout(true, false); + if (renderer()->isRenderBlock()) { + RenderBlock* block = toRenderBlock(renderer()); + block->scrollbarsChanged(box->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow, + box->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); + block->layoutBlock(true); + } else + renderer()->layout(); + m_inOverflowRelayout = false; + } + } + } + + // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). + if (m_hBar && box->hasAutoHorizontalScrollbar()) + m_hBar->setEnabled(true); + if (m_vBar && box->hasAutoVerticalScrollbar()) + m_vBar->setEnabled(true); + + // Set up the range (and page step/line step). + if (m_hBar) { + int clientWidth = box->clientWidth(); + int pageStep = max(max(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1); + m_hBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); + m_hBar->setProportion(clientWidth, m_scrollWidth); + // Explicitly set the horizontal scroll value. This ensures that when a + // right-to-left scrollable area's width (or content width) changes, the + // top right corner of the content doesn't shift with respect to the top + // right corner of the area. Conceptually, right-to-left areas have + // their origin at the top-right, but RenderLayer is top-left oriented, + // so this is needed to keep everything working. + m_hBar->setValue(scrollXOffset(), Scrollbar::NotFromScrollAnimator); + } + if (m_vBar) { + int clientHeight = box->clientHeight(); + int pageStep = max(max(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1); + m_vBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); + m_vBar->setProportion(clientHeight, m_scrollHeight); + // Explicitly set the vertical scroll value. This ensures that when a + // right-to-left vertical writing-mode scrollable area's height (or content height) changes, the + // bottom right corner of the content doesn't shift with respect to the bottom + // right corner of the area. Conceptually, right-to-left vertical writing-mode areas have + // their origin at the bottom-right, but RenderLayer is top-left oriented, + // so this is needed to keep everything working. + m_vBar->setValue(scrollYOffset(), Scrollbar::NotFromScrollAnimator); + } + + if (renderer()->node() && renderer()->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) + updateOverflowStatus(horizontalOverflow, verticalOverflow); + +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + bool hasOverflowScroll = ((horizontalOverflow && m_hBar) || (verticalOverflow && m_vBar)) + // Disable UI side scrolling for textareas, unless they are readonly. + && (!renderer()->isTextArea() || (renderer()->node() + && static_cast(renderer()->node())->readOnly())); + if (hasOverflowScroll != m_hasOverflowScroll) { + m_hasOverflowScroll = hasOverflowScroll; + dirtyZOrderLists(); + dirtyStackingContextZOrderLists(); + if (renderer()->node()) + renderer()->node()->setNeedsStyleRecalc(SyntheticStyleChange); + } +#endif +} + +void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + // Don't do anything if we have no overflow. + if (!renderer()->hasOverflowClip()) + return; + + // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout, but sometimes + // widgets can move without layout occurring (most notably when you scroll a document that + // contains fixed positioned elements). + positionOverflowControls(tx, ty); + + // Now that we're sure the scrollbars are in the right place, paint them. + if (m_hBar) + m_hBar->paint(context, damageRect); + if (m_vBar) + m_vBar->paint(context, damageRect); + + // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the + // edge of the box. + paintScrollCorner(context, tx, ty, damageRect); + + // Paint our resizer last, since it sits on top of the scroll corner. + paintResizer(context, tx, ty, damageRect); +} + +void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = scrollCornerRect(this, box->borderBoxRect()); + IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); + if (!absRect.intersects(damageRect)) + return; + + if (context->updatingControlTints()) { + updateScrollCornerStyle(); + return; + } + + if (m_scrollCorner) { + m_scrollCorner->paintIntoRect(context, tx, ty, absRect); + return; + } + + context->fillRect(absRect, Color::white, box->style()->colorSpace()); +} + +void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + if (renderer()->style()->resize() == RESIZE_NONE) + return; + + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect cornerRect = resizerCornerRect(this, box->borderBoxRect()); + IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); + if (!absRect.intersects(damageRect)) + return; + + if (context->updatingControlTints()) { + updateResizerStyle(); + return; + } + + if (m_resizer) { + m_resizer->paintIntoRect(context, tx, ty, absRect); + return; + } + + // Paint the resizer control. + DEFINE_STATIC_LOCAL(RefPtr, resizeCornerImage, (Image::loadPlatformResource("textAreaResizeCorner"))); + IntPoint imagePoint(absRect.right() - resizeCornerImage->width(), absRect.bottom() - resizeCornerImage->height()); + context->drawImage(resizeCornerImage.get(), box->style()->colorSpace(), imagePoint); + + // Draw a frame around the resizer (1px grey line) if there are any scrollbars present. + // Clipping will exclude the right and bottom edges of this frame. + if (m_hBar || m_vBar) { + context->save(); + context->clip(absRect); + IntRect largerCorner = absRect; + largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); + context->setStrokeColor(Color(makeRGB(217, 217, 217)), ColorSpaceDeviceRGB); + context->setStrokeThickness(1.0f); + context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); + context->drawRect(largerCorner); + context->restore(); + } +} + +bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const +{ + if (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE) + return false; + + RenderBox* box = renderBox(); + ASSERT(box); + + IntPoint localPoint = absoluteToContents(absolutePoint); + + IntRect localBounds(0, 0, box->width(), box->height()); + return resizerCornerRect(this, localBounds).contains(localPoint); +} + +bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint) +{ + if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) + return false; + + RenderBox* box = renderBox(); + ASSERT(box); + + IntRect resizeControlRect; + if (renderer()->style()->resize() != RESIZE_NONE) { + resizeControlRect = resizerCornerRect(this, box->borderBoxRect()); + if (resizeControlRect.contains(localPoint)) + return true; + } + + int resizeControlSize = max(resizeControlRect.height(), 0); + + if (m_vBar) { + IntRect vBarRect(box->width() - box->borderRight() - m_vBar->width(), + box->borderTop(), + m_vBar->width(), + box->height() - (box->borderTop() + box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); + if (vBarRect.contains(localPoint)) { + result.setScrollbar(m_vBar.get()); + return true; + } + } + + resizeControlSize = max(resizeControlRect.width(), 0); + if (m_hBar) { + IntRect hBarRect(box->borderLeft(), + box->height() - box->borderBottom() - m_hBar->height(), + box->width() - (box->borderLeft() + box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), + m_hBar->height()); + if (hBarRect.contains(localPoint)) { + result.setScrollbar(m_hBar.get()); + return true; + } + } + + return false; +} + +bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + bool didHorizontalScroll = false; + bool didVerticalScroll = false; + + if (m_hBar) + didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier); + if (m_vBar) + didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier); + + return (didHorizontalScroll || didVerticalScroll); +} + +void RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintBehavior paintBehavior, RenderObject *paintingRoot) +{ + OverlapTestRequestMap overlapTestRequests; + paintLayer(this, p, damageRect, paintBehavior, paintingRoot, &overlapTestRequests); + OverlapTestRequestMap::iterator end = overlapTestRequests.end(); + for (OverlapTestRequestMap::iterator it = overlapTestRequests.begin(); it != end; ++it) + it->first->setOverlapTestResult(false); +} + +static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->save(); + p->clip(clipRect); +} + +static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->restore(); +} + +static void performOverlapTests(OverlapTestRequestMap& overlapTestRequests, const RenderLayer* rootLayer, const RenderLayer* layer) +{ + Vector overlappedRequestClients; + OverlapTestRequestMap::iterator end = overlapTestRequests.end(); + IntRect boundingBox = layer->boundingBox(rootLayer); + for (OverlapTestRequestMap::iterator it = overlapTestRequests.begin(); it != end; ++it) { + if (!boundingBox.intersects(it->second)) + continue; + + it->first->setOverlapTestResult(true); + overlappedRequestClients.append(it->first); + } + for (size_t i = 0; i < overlappedRequestClients.size(); ++i) + overlapTestRequests.remove(overlappedRequestClients[i]); +} + +#if USE(ACCELERATED_COMPOSITING) +static bool shouldDoSoftwarePaint(const RenderLayer* layer, bool paintingReflection) +{ + return paintingReflection && !layer->has3DTransform(); +} +#endif + +void RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, + const IntRect& paintDirtyRect, PaintBehavior paintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap* overlapTestRequests, + PaintLayerFlags paintFlags) +{ +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) { + // The updatingControlTints() painting pass goes through compositing layers, + // but we need to ensure that we don't cache clip rects computed with the wrong root in this case. + if (p->updatingControlTints() || (paintBehavior & PaintBehaviorFlattenCompositingLayers)) + paintFlags |= PaintLayerTemporaryClipRects; + else if (!backing()->paintingGoesToWindow() && !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)) { + // If this RenderLayer should paint into its backing, that will be done via RenderLayerBacking::paintIntoLayer(). + return; + } + } +#endif + + // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC. + // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document + // will do a full repaint(). + if (renderer()->document()->mayCauseFlashOfUnstyledContent() && !renderer()->isRenderView() && !renderer()->isRoot()) + return; + + // If this layer is totally invisible then there is nothing to paint. + if (!renderer()->opacity()) + return; + + if (paintsWithTransparency(paintBehavior)) + paintFlags |= PaintLayerHaveTransparency; + + // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate. + if (paintsWithTransform(paintBehavior) && !(paintFlags & PaintLayerAppliedTransform)) { + TransformationMatrix layerTransform = renderableTransform(paintBehavior); + // If the transform can't be inverted, then don't paint anything. + if (!layerTransform.isInvertible()) + return; + + // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency + // layer from the parent now. + if (paintFlags & PaintLayerHaveTransparency) + parent()->beginTransparencyLayers(p, rootLayer, paintBehavior); + + // Make sure the parent's clip rects have been calculated. + IntRect clipRect = paintDirtyRect; + if (parent()) { + clipRect = backgroundClipRect(rootLayer, paintFlags & PaintLayerTemporaryClipRects); + clipRect.intersect(paintDirtyRect); + } + + // Push the parent coordinate space's clip. + setClip(p, paintDirtyRect, clipRect); + + // Adjust the transform such that the renderer's upper left corner will paint at (0,0) in user space. + // This involves subtracting out the position of the layer in our current coordinate space. + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform(layerTransform); + transform.translateRight(x, y); + + // Apply the transform. + p->save(); + p->concatCTM(transform.toAffineTransform()); + + // Now do a paint with the root layer shifted to be us. + paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), paintBehavior, paintingRoot, overlapTestRequests, paintFlags | PaintLayerAppliedTransform); + + p->restore(); + + // Restore the clip. + restoreClip(p, paintDirtyRect, clipRect); + + return; + } + + PaintLayerFlags localPaintFlags = paintFlags & ~PaintLayerAppliedTransform; + bool haveTransparency = localPaintFlags & PaintLayerHaveTransparency; + + // Paint the reflection first if we have one. + if (m_reflection && !m_paintingInsideReflection) { + // Mark that we are now inside replica painting. + m_paintingInsideReflection = true; + reflectionLayer()->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags | PaintLayerPaintingReflection); + m_paintingInsideReflection = false; + } + + // Calculate the clip rects we should use. + IntRect layerBounds, damageRect, clipRectToApply, outlineRect; + calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, localPaintFlags & PaintLayerTemporaryClipRects); + int x = layerBounds.x(); + int y = layerBounds.y(); + int tx = x - renderBoxX(); + int ty = y - renderBoxY(); + + // Ensure our lists are up-to-date. + updateCompositingAndLayerListsIfNeeded(); + + bool forceBlackText = paintBehavior & PaintBehaviorForceBlackText; + bool selectionOnly = paintBehavior & PaintBehaviorSelectionOnly; + + // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which + // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). + // Else, our renderer tree may or may not contain the painting root, so we pass that root along + // so it will be tested against as we descend through the renderers. + RenderObject* paintingRootForRenderer = 0; + if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) + paintingRootForRenderer = paintingRoot; + + if (overlapTestRequests && isSelfPaintingLayer()) + performOverlapTests(*overlapTestRequests, rootLayer, this); + + // We want to paint our layer, but only if we intersect the damage rect. + bool shouldPaint = intersectsDamageRect(layerBounds, damageRect, rootLayer) && m_hasVisibleContent && isSelfPaintingLayer(); + if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + // Begin transparency layers lazily now that we know we have to paint something. + if (haveTransparency) + beginTransparencyLayers(p, rootLayer, paintBehavior); + + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + setClip(p, paintDirtyRect, damageRect); + + // Paint the background. + PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Restore the clip. + restoreClip(p, paintDirtyRect, damageRect); + } + + // Now walk the sorted list of children with negative z-indices. + paintList(m_negZOrderList, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); + + // Now establish the appropriate clip and paint our child RenderObjects. + if (shouldPaint && !clipRectToApply.isEmpty()) { + // Begin transparency layers lazily now that we know we have to paint something. + if (haveTransparency) + beginTransparencyLayers(p, rootLayer, paintBehavior); + + // Set up the clip used when painting our children. + setClip(p, paintDirtyRect, clipRectToApply); + PaintInfo paintInfo(p, clipRectToApply, + selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, + forceBlackText, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + if (!selectionOnly) { + paintInfo.phase = PaintPhaseFloat; + renderer()->paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseForeground; + paintInfo.overlapTestRequests = overlapTestRequests; + renderer()->paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseChildOutlines; + renderer()->paint(paintInfo, tx, ty); + } + + // Now restore our clip. + restoreClip(p, paintDirtyRect, clipRectToApply); + } + + if (!outlineRect.isEmpty() && isSelfPaintingLayer()) { + // Paint our own outline + PaintInfo paintInfo(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); + setClip(p, paintDirtyRect, outlineRect); + renderer()->paint(paintInfo, tx, ty); + restoreClip(p, paintDirtyRect, outlineRect); + } + + // Paint any child layers that have overflow. + paintList(m_normalFlowList, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); + + // Now walk the sorted list of children with positive z-indices. + paintList(m_posZOrderList, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, localPaintFlags); + + if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + setClip(p, paintDirtyRect, damageRect); + + // Paint the mask. + PaintInfo paintInfo(p, damageRect, PaintPhaseMask, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Restore the clip. + restoreClip(p, paintDirtyRect, damageRect); + } + + // End our transparency layer + if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { + p->endTransparencyLayer(); + p->restore(); + m_usedTransparency = false; + } +} + +void RenderLayer::paintList(Vector* list, RenderLayer* rootLayer, GraphicsContext* p, + const IntRect& paintDirtyRect, PaintBehavior paintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap* overlapTestRequests, + PaintLayerFlags paintFlags) +{ + if (!list) + return; + + for (size_t i = 0; i < list->size(); ++i) { + RenderLayer* childLayer = list->at(i); + if (!childLayer->isPaginated()) + childLayer->paintLayer(rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, paintFlags); + else + paintPaginatedChildLayer(childLayer, rootLayer, p, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, paintFlags); + } +} + +void RenderLayer::paintPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, GraphicsContext* context, + const IntRect& paintDirtyRect, PaintBehavior paintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap* overlapTestRequests, + PaintLayerFlags paintFlags) +{ + // We need to do multiple passes, breaking up our child layer into strips. + Vector columnLayers; + RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContext(); + for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { + if (curr->renderer()->hasColumns()) + columnLayers.append(curr); +#ifdef ANDROID + // Bug filed: 56107 + if (curr == ancestorLayer) + break; +#else + if (curr == ancestorLayer || (curr->parent() && curr->parent()->renderer()->isPositioned())) + break; +#endif + } + + ASSERT(columnLayers.size()); + + paintChildLayerIntoColumns(childLayer, rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, paintFlags, columnLayers, columnLayers.size() - 1); +} + +void RenderLayer::paintChildLayerIntoColumns(RenderLayer* childLayer, RenderLayer* rootLayer, GraphicsContext* context, + const IntRect& paintDirtyRect, PaintBehavior paintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap* overlapTestRequests, + PaintLayerFlags paintFlags, const Vector& columnLayers, size_t colIndex) +{ + RenderBlock* columnBlock = toRenderBlock(columnLayers[colIndex]->renderer()); + + ASSERT(columnBlock && columnBlock->hasColumns()); + if (!columnBlock || !columnBlock->hasColumns()) + return; + + int layerX = 0; + int layerY = 0; + columnBlock->layer()->convertToLayerCoords(rootLayer, layerX, layerY); + + ColumnInfo* colInfo = columnBlock->columnInfo(); + unsigned colCount = columnBlock->columnCount(colInfo); + int currYOffset = 0; + for (unsigned i = 0; i < colCount; i++) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = columnBlock->columnRectAt(colInfo, i); + int currXOffset = colRect.x() - (columnBlock->borderLeft() + columnBlock->paddingLeft()); + colRect.move(layerX, layerY); + + IntRect localDirtyRect(paintDirtyRect); + localDirtyRect.intersect(colRect); + + if (!localDirtyRect.isEmpty()) { + context->save(); + + // Each strip pushes a clip, since column boxes are specified as being + // like overflow:hidden. + context->clip(colRect); + + if (!colIndex) { + // Apply a translation transform to change where the layer paints. + TransformationMatrix oldTransform; + bool oldHasTransform = childLayer->transform(); + if (oldHasTransform) + oldTransform = *childLayer->transform(); + TransformationMatrix newTransform(oldTransform); + newTransform.translateRight(currXOffset, currYOffset); + + childLayer->m_transform.set(new TransformationMatrix(newTransform)); + childLayer->paintLayer(rootLayer, context, localDirtyRect, paintBehavior, paintingRoot, overlapTestRequests, paintFlags); + if (oldHasTransform) + childLayer->m_transform.set(new TransformationMatrix(oldTransform)); + else + childLayer->m_transform.clear(); + } else { + // Adjust the transform such that the renderer's upper left corner will paint at (0,0) in user space. + // This involves subtracting out the position of the layer in our current coordinate space. + int childX = 0; + int childY = 0; + columnLayers[colIndex - 1]->convertToLayerCoords(rootLayer, childX, childY); + TransformationMatrix transform; + transform.translateRight(childX + currXOffset, childY + currYOffset); + + // Apply the transform. + context->concatCTM(transform.toAffineTransform()); + + // Now do a paint with the root layer shifted to be the next multicol block. + paintChildLayerIntoColumns(childLayer, columnLayers[colIndex - 1], context, transform.inverse().mapRect(localDirtyRect), paintBehavior, + paintingRoot, overlapTestRequests, paintFlags, + columnLayers, colIndex - 1); + } + + context->restore(); + } + + // Move to the next position. + currYOffset -= colRect.height(); + } +} + +static inline IntRect frameVisibleRect(RenderObject* renderer) +{ + FrameView* frameView = renderer->document()->view(); + if (!frameView) + return IntRect(); + + return frameView->visibleContentRect(); +} + +bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) +{ + renderer()->document()->updateLayout(); + + IntRect hitTestArea = result.rectForPoint(result.point()); + if (!request.ignoreClipping()) + hitTestArea.intersect(frameVisibleRect(renderer())); + + RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, result.point(), false); + if (!insideLayer) { + // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, + // return ourselves. We do this so mouse events continue getting delivered after a drag has + // exited the WebView, and so hit testing over a scrollbar hits the content document. + if ((request.active() || request.mouseUp()) && renderer()->isRenderView()) { + renderer()->updateHitTestResult(result, result.point()); + insideLayer = this; + } + } + + // Now determine if the result is inside an anchor - if the urlElement isn't already set. + Node* node = result.innerNode(); + if (node && !result.URLElement()) + result.setURLElement(static_cast(node->enclosingLinkEventParentOrSelf())); + + // Next set up the correct :hover/:active state along the new chain. + updateHoverActiveState(request, result); + + // Now return whether we were inside this layer (this will always be true for the root + // layer). + return insideLayer; +} + +Node* RenderLayer::enclosingElement() const +{ + for (RenderObject* r = renderer(); r; r = r->parent()) { + if (Node* e = r->node()) + return e; + } + ASSERT_NOT_REACHED(); + return 0; +} + +// Compute the z-offset of the point in the transformState. +// This is effectively projecting a ray normal to the plane of ancestor, finding where that +// ray intersects target, and computing the z delta between those two points. +static double computeZOffset(const HitTestingTransformState& transformState) +{ + // We got an affine transform, so no z-offset + if (transformState.m_accumulatedTransform.isAffine()) + return 0; + + // Flatten the point into the target plane + FloatPoint targetPoint = transformState.mappedPoint(); + + // Now map the point back through the transform, which computes Z. + FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint)); + return backmappedPoint.z(); +} + +PassRefPtr RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* containerTransformState) const +{ + RefPtr transformState; + int offsetX = 0; + int offsetY = 0; + if (containerTransformState) { + // If we're already computing transform state, then it's relative to the container (which we know is non-null). + transformState = HitTestingTransformState::create(*containerTransformState); + convertToLayerCoords(containerLayer, offsetX, offsetY); + } else { + // If this is the first time we need to make transform state, then base it off of hitTestPoint, + // which is relative to rootLayer. + transformState = HitTestingTransformState::create(hitTestPoint, FloatQuad(hitTestRect)); + convertToLayerCoords(rootLayer, offsetX, offsetY); + } + + RenderObject* containerRenderer = containerLayer ? containerLayer->renderer() : 0; + if (renderer()->shouldUseTransformFromContainer(containerRenderer)) { + TransformationMatrix containerTransform; + renderer()->getTransformFromContainer(containerRenderer, IntSize(offsetX, offsetY), containerTransform); + transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform); + } else { + transformState->translate(offsetX, offsetY, HitTestingTransformState::AccumulateTransform); + } + + return transformState; +} + + +static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState) +{ + if (!hitLayer) + return false; + + // The hit layer is depth-sorting with other layers, so just say that it was hit. + if (canDepthSort) + return true; + + // We need to look at z-depth to decide if this layer was hit. + if (zOffset) { + ASSERT(transformState); + // This is actually computing our z, but that's OK because the hitLayer is coplanar with us. + double childZOffset = computeZOffset(*transformState); + if (childZOffset > *zOffset) { + *zOffset = childZOffset; + return true; + } + return false; + } + + return true; +} + +// hitTestPoint and hitTestRect are relative to rootLayer. +// A 'flattening' layer is one preserves3D() == false. +// transformState.m_accumulatedTransform holds the transform from the containing flattening layer. +// transformState.m_lastPlanarPoint is the hitTestPoint in the plane of the containing flattening layer. +// transformState.m_lastPlanarQuad is the hitTestRect as a quad in the plane of the containing flattening layer. +// +// If zOffset is non-null (which indicates that the caller wants z offset information), +// *zOffset on return is the z offset of the hit point relative to the containing flattening layer. +RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform, + const HitTestingTransformState* transformState, double* zOffset) +{ + // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. + + bool useTemporaryClipRects = false; +#if USE(ACCELERATED_COMPOSITING) + useTemporaryClipRects = compositor()->inCompositingMode(); +#endif + + IntRect hitTestArea = result.rectForPoint(hitTestPoint); + + // Apply a transform if we have one. + if (transform() && !appliedTransform) { + // Make sure the parent's clip rects have been calculated. + if (parent()) { + IntRect clipRect = backgroundClipRect(rootLayer, useTemporaryClipRects); + // Go ahead and test the enclosing clip now. + if (!clipRect.intersects(hitTestArea)) + return 0; + } + + // Create a transform state to accumulate this transform. + RefPtr newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + + // If the transform can't be inverted, then don't hit test this layer at all. + if (!newTransformState->m_accumulatedTransform.isInvertible()) + return 0; + + // Compute the point and the hit test rect in the coords of this layer by using the values + // from the transformState, which store the point and quad in the coords of the last flattened + // layer, and the accumulated transform which lets up map through preserve-3d layers. + // + // We can't just map hitTestPoint and hitTestRect because they may have been flattened (losing z) + // by our container. + IntPoint localPoint = roundedIntPoint(newTransformState->mappedPoint()); + IntRect localHitTestRect; +#if USE(ACCELERATED_COMPOSITING) + if (isComposited()) { + // It doesn't make sense to project hitTestRect into the plane of this layer, so use the same bounds we use for painting. + localHitTestRect = backing()->compositedBounds(); + } else +#endif + localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox(); + + // Now do a hit test with the root layer shifted to be us. + return hitTestLayer(this, containerLayer, request, result, localHitTestRect, localPoint, true, newTransformState.get(), zOffset); + } + + // Ensure our lists and 3d status are up-to-date. + updateCompositingAndLayerListsIfNeeded(); + update3DTransformedDescendantStatus(); + + RefPtr localTransformState; + if (appliedTransform) { + // We computed the correct state in the caller (above code), so just reference it. + ASSERT(transformState); + localTransformState = const_cast(transformState); + } else if (transformState || m_has3DTransformedDescendant || preserves3D()) { + // We need transform state for the first time, or to offset the container state, so create it here. + localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestPoint, transformState); + } + + // Check for hit test on backface if backface-visibility is 'hidden' + if (localTransformState && renderer()->style()->backfaceVisibility() == BackfaceVisibilityHidden) { + TransformationMatrix invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); + // If the z-vector of the matrix is negative, the back is facing towards the viewer. + if (invertedMatrix.m33() < 0) + return 0; + } + + RefPtr unflattenedTransformState = localTransformState; + if (localTransformState && !preserves3D()) { + // Keep a copy of the pre-flattening state, for computing z-offsets for the container + unflattenedTransformState = HitTestingTransformState::create(*localTransformState); + // This layer is flattening, so flatten the state passed to descendants. + localTransformState->flatten(); + } + + // Calculate the clip rects we should use. + IntRect layerBounds; + IntRect bgRect; + IntRect fgRect; + IntRect outlineRect; + calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect, useTemporaryClipRects); + + // The following are used for keeping track of the z-depth of the hit point of 3d-transformed + // descendants. + double localZOffset = -numeric_limits::infinity(); + double* zOffsetForDescendantsPtr = 0; + double* zOffsetForContentsPtr = 0; + + bool depthSortDescendants = false; + if (preserves3D()) { + depthSortDescendants = true; + // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down. + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (m_has3DTransformedDescendant) { + // Flattening layer with 3d children; use a local zOffset pointer to depth-test children and foreground. + depthSortDescendants = true; + zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; + zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; + } else if (zOffset) { + zOffsetForDescendantsPtr = 0; + // Container needs us to give back a z offset for the hit layer. + zOffsetForContentsPtr = zOffset; + } + + // This variable tracks which layer the mouse ends up being inside. + RenderLayer* candidateLayer = 0; + + // Begin by walking our list of positive layers from highest z-index down to the lowest z-index. + RenderLayer* hitLayer = hitTestList(m_posZOrderList, rootLayer, request, result, hitTestRect, hitTestPoint, + localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); + if (hitLayer) { + if (!depthSortDescendants) + return hitLayer; + candidateLayer = hitLayer; + } + + // Now check our overflow objects. + hitLayer = hitTestList(m_normalFlowList, rootLayer, request, result, hitTestRect, hitTestPoint, + localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); + if (hitLayer) { + if (!depthSortDescendants) + return hitLayer; + candidateLayer = hitLayer; + } + +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (hasOverflowParent()) { + ClipRects clipRects; + calculateClipRects(rootLayer, clipRects, useTemporaryClipRects); + fgRect.intersect(clipRects.hitTestClip()); + bgRect.intersect(clipRects.hitTestClip()); + } +#endif + // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. + if (fgRect.intersects(hitTestArea) && isSelfPaintingLayer()) { + // Hit test with a temporary HitTestResult, because we only want to commit to 'result' if we know we're frontmost. + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestDescendants) && + isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { + if (result.isRectBasedTest()) + result.append(tempResult); + else + result = tempResult; + if (!depthSortDescendants) + return this; + // Foreground can depth-sort with descendant layers, so keep this as a candidate. + candidateLayer = this; + } else if (result.isRectBasedTest()) + result.append(tempResult); + } + + // Now check our negative z-index children. + hitLayer = hitTestList(m_negZOrderList, rootLayer, request, result, hitTestRect, hitTestPoint, + localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); + if (hitLayer) { + if (!depthSortDescendants) + return hitLayer; + candidateLayer = hitLayer; + } + + // If we found a layer, return. Child layers, and foreground always render in front of background. + if (candidateLayer) + return candidateLayer; + + if (bgRect.intersects(hitTestArea) && isSelfPaintingLayer()) { + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + if (hitTestContents(request, tempResult, layerBounds, hitTestPoint, HitTestSelf) && + isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) { + if (result.isRectBasedTest()) + result.append(tempResult); + else + result = tempResult; + return this; + } else if (result.isRectBasedTest()) + result.append(tempResult); + } + + return 0; +} + +bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const IntRect& layerBounds, const IntPoint& hitTestPoint, HitTestFilter hitTestFilter) const +{ + if (!renderer()->hitTest(request, result, hitTestPoint, + layerBounds.x() - renderBoxX(), + layerBounds.y() - renderBoxY(), + hitTestFilter)) { + // It's wrong to set innerNode, but then claim that you didn't hit anything, unless it is + // a rect-based test. + ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBasedTestResult().size())); + return false; + } + + // For positioned generated content, we might still not have a + // node by the time we get to the layer level, since none of + // the content in the layer has an element. So just walk up + // the tree. + if (!result.innerNode() || !result.innerNonSharedNode()) { + Node* e = enclosingElement(); + if (!result.innerNode()) + result.setInnerNode(e); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(e); + } + + return true; +} + +RenderLayer* RenderLayer::hitTestList(Vector* list, RenderLayer* rootLayer, + const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* transformState, + double* zOffsetForDescendants, double* zOffset, + const HitTestingTransformState* unflattenedTransformState, + bool depthSortDescendants) +{ + if (!list) + return 0; + + RenderLayer* resultLayer = 0; + for (int i = list->size() - 1; i >= 0; --i) { + RenderLayer* childLayer = list->at(i); + RenderLayer* hitLayer = 0; + HitTestResult tempResult(result.point(), result.topPadding(), result.rightPadding(), result.bottomPadding(), result.leftPadding()); + if (childLayer->isPaginated()) + hitLayer = hitTestPaginatedChildLayer(childLayer, rootLayer, request, tempResult, hitTestRect, hitTestPoint, transformState, zOffsetForDescendants); + else + hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestPoint, false, transformState, zOffsetForDescendants); + + // If it a rect-based test, we can safely append the temporary result since it might had hit + // nodes but not necesserily had hitLayer set. + if (result.isRectBasedTest()) + result.append(tempResult); + + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) { + resultLayer = hitLayer; + if (!result.isRectBasedTest()) + result = tempResult; + if (!depthSortDescendants) + break; + } + } + + return resultLayer; +} + +RenderLayer* RenderLayer::hitTestPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, const HitTestingTransformState* transformState, double* zOffset) +{ + Vector columnLayers; + RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContext(); + for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { + if (curr->renderer()->hasColumns()) + columnLayers.append(curr); + if (curr == ancestorLayer || (curr->parent() && curr->parent()->renderer()->isPositioned())) + break; + } + + ASSERT(columnLayers.size()); + return hitTestChildLayerColumns(childLayer, rootLayer, request, result, hitTestRect, hitTestPoint, transformState, zOffset, + columnLayers, columnLayers.size() - 1); +} + +RenderLayer* RenderLayer::hitTestChildLayerColumns(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, const HitTestingTransformState* transformState, double* zOffset, + const Vector& columnLayers, size_t columnIndex) +{ + RenderBlock* columnBlock = toRenderBlock(columnLayers[columnIndex]->renderer()); + + ASSERT(columnBlock && columnBlock->hasColumns()); + if (!columnBlock || !columnBlock->hasColumns()) + return 0; + + int layerX = 0; + int layerY = 0; + columnBlock->layer()->convertToLayerCoords(rootLayer, layerX, layerY); + + ColumnInfo* colInfo = columnBlock->columnInfo(); + int colCount = columnBlock->columnCount(colInfo); + + // We have to go backwards from the last column to the first. + int left = columnBlock->borderLeft() + columnBlock->paddingLeft(); + int currYOffset = 0; + int i; + for (i = 0; i < colCount; i++) + currYOffset -= columnBlock->columnRectAt(colInfo, i).height(); + for (i = colCount - 1; i >= 0; i--) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = columnBlock->columnRectAt(colInfo, i); + int currXOffset = colRect.x() - left; + currYOffset += colRect.height(); + colRect.move(layerX, layerY); + + IntRect localClipRect(hitTestRect); + localClipRect.intersect(colRect); + + if (!localClipRect.isEmpty() && localClipRect.intersects(result.rectForPoint(hitTestPoint))) { + RenderLayer* hitLayer = 0; + if (!columnIndex) { + // Apply a translation transform to change where the layer paints. + TransformationMatrix oldTransform; + bool oldHasTransform = childLayer->transform(); + if (oldHasTransform) + oldTransform = *childLayer->transform(); + TransformationMatrix newTransform(oldTransform); + newTransform.translateRight(currXOffset, currYOffset); + + childLayer->m_transform.set(new TransformationMatrix(newTransform)); + hitLayer = childLayer->hitTestLayer(rootLayer, columnLayers[0], request, result, localClipRect, hitTestPoint, false, transformState, zOffset); + if (oldHasTransform) + childLayer->m_transform.set(new TransformationMatrix(oldTransform)); + else + childLayer->m_transform.clear(); + } else { + // Adjust the transform such that the renderer's upper left corner will be at (0,0) in user space. + // This involves subtracting out the position of the layer in our current coordinate space. + RenderLayer* nextLayer = columnLayers[columnIndex - 1]; + RefPtr newTransformState = nextLayer->createLocalTransformState(rootLayer, nextLayer, localClipRect, hitTestPoint, transformState); + newTransformState->translate(currXOffset, currYOffset, HitTestingTransformState::AccumulateTransform); + IntPoint localPoint = roundedIntPoint(newTransformState->mappedPoint()); + IntRect localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox(); + newTransformState->flatten(); + + hitLayer = hitTestChildLayerColumns(childLayer, columnLayers[columnIndex - 1], request, result, localHitTestRect, localPoint, + newTransformState.get(), zOffset, columnLayers, columnIndex - 1); + } + + if (hitLayer) + return hitLayer; + } + } + + return 0; +} + +void RenderLayer::updateClipRects(const RenderLayer* rootLayer) +{ + if (m_clipRects) { + ASSERT(rootLayer == m_clipRectsRoot); + return; // We have the correct cached value. + } + + // For transformed layers, the root layer was shifted to be us, so there is no need to + // examine the parent. We want to cache clip rects with us as the root. + RenderLayer* parentLayer = rootLayer != this ? parent() : 0; + if (parentLayer) + parentLayer->updateClipRects(rootLayer); + + ClipRects clipRects; + calculateClipRects(rootLayer, clipRects, true); + + if (parentLayer && parentLayer->clipRects() && clipRects == *parentLayer->clipRects()) + m_clipRects = parentLayer->clipRects(); + else + m_clipRects = new (renderer()->renderArena()) ClipRects(clipRects); + m_clipRects->ref(); +#ifndef NDEBUG + m_clipRectsRoot = rootLayer; +#endif +} + +void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool useCached) const +{ + if (!parent()) { + // The root layer's clip rect is always infinite. + clipRects.reset(PaintInfo::infiniteRect()); + return; + } + + // For transformed layers, the root layer was shifted to be us, so there is no need to + // examine the parent. We want to cache clip rects with us as the root. + RenderLayer* parentLayer = rootLayer != this ? parent() : 0; + + // Ensure that our parent's clip has been calculated so that we can examine the values. + if (parentLayer) { + if (useCached && parentLayer->clipRects()) + clipRects = *parentLayer->clipRects(); + else + parentLayer->calculateClipRects(rootLayer, clipRects); + } + else + clipRects.reset(PaintInfo::infiniteRect()); + + // A fixed object is essentially the root of its containing block hierarchy, so when + // we encounter such an object, we reset our clip rects to the fixedClipRect. + if (renderer()->style()->position() == FixedPosition) { + clipRects.setPosClipRect(clipRects.fixedClipRect()); + clipRects.setOverflowClipRect(clipRects.fixedClipRect()); + clipRects.setFixed(true); + } + else if (renderer()->style()->position() == RelativePosition) + clipRects.setPosClipRect(clipRects.overflowClipRect()); + else if (renderer()->style()->position() == AbsolutePosition) + clipRects.setOverflowClipRect(clipRects.posClipRect()); + + // Update the clip rects that will be passed to child layers. + if (renderer()->hasOverflowClip() || renderer()->hasClip()) { + // This layer establishes a clip of some kind. + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && clipRects.fixed() && rootLayer->renderer() == view) { + x -= view->frameView()->scrollX(); + y -= view->frameView()->scrollY(); + } + + if (renderer()->hasOverflowClip()) { + IntRect newOverflowClip = toRenderBox(renderer())->overflowClipRect(x, y); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + clipRects.setHitTestClip(intersection(clipRects.fixed() ? clipRects.fixedClipRect() + : newOverflowClip, clipRects.hitTestClip())); + if (hasOverflowScroll()) { + RenderBox* box = toRenderBox(renderer()); + newOverflowClip = + IntRect(x + box->borderLeft(), y + box->borderTop(), + m_scrollWidth, m_scrollHeight); + } +#endif + clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); + if (renderer()->isPositioned() || renderer()->isRelPositioned()) + clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); + } + if (renderer()->hasClip()) { + IntRect newPosClip = toRenderBox(renderer())->clipRect(x, y); + clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); + clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); + clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); + } + } +} + +void RenderLayer::parentClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool temporaryClipRects) const +{ + ASSERT(parent()); + if (temporaryClipRects) { + parent()->calculateClipRects(rootLayer, clipRects); + return; + } + + parent()->updateClipRects(rootLayer); + clipRects = *parent()->clipRects(); +} + +IntRect RenderLayer::backgroundClipRect(const RenderLayer* rootLayer, bool temporaryClipRects) const +{ + IntRect backgroundRect; + if (parent()) { + ClipRects parentRects; + parentClipRects(rootLayer, parentRects, temporaryClipRects); + backgroundRect = renderer()->style()->position() == FixedPosition ? parentRects.fixedClipRect() : + (renderer()->isPositioned() ? parentRects.posClipRect() : + parentRects.overflowClipRect()); + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && parentRects.fixed() && rootLayer->renderer() == view) + backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY()); + } + return backgroundRect; +} + +void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, + IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const +{ + if (rootLayer != this && parent()) { + backgroundRect = backgroundClipRect(rootLayer, temporaryClipRects); + backgroundRect.intersect(paintDirtyRect); + } else + backgroundRect = paintDirtyRect; + + foregroundRect = backgroundRect; + outlineRect = backgroundRect; + + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + layerBounds = IntRect(x, y, width(), height()); + + // Update the clip rects that will be passed to child layers. + if (renderer()->hasOverflowClip() || renderer()->hasClip()) { + // This layer establishes a clip of some kind. +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (hasOverflowScroll()) { + // Use the entire foreground rectangle to record the contents. + RenderBox* box = toRenderBox(renderer()); + foregroundRect = + IntRect(x + box->borderLeft(), y + box->borderTop(), + m_scrollWidth, m_scrollHeight); + } else +#endif + if (renderer()->hasOverflowClip()) + foregroundRect.intersect(toRenderBox(renderer())->overflowClipRect(x, y)); + if (renderer()->hasClip()) { + // Clip applies to *us* as well, so go ahead and update the damageRect. + IntRect newPosClip = toRenderBox(renderer())->clipRect(x, y); + backgroundRect.intersect(newPosClip); + foregroundRect.intersect(newPosClip); + outlineRect.intersect(newPosClip); + } + + // If we establish a clip at all, then go ahead and make sure our background + // rect is intersected with our layer's bounds. + // FIXME: This could be changed to just use generic visual overflow. + // See https://bugs.webkit.org/show_bug.cgi?id=37467 for more information. + if (const ShadowData* boxShadow = renderer()->style()->boxShadow()) { + IntRect overflow = layerBounds; + do { + if (boxShadow->style() == Normal) { + IntRect shadowRect = layerBounds; + shadowRect.move(boxShadow->x(), boxShadow->y()); + shadowRect.inflate(boxShadow->blur() + boxShadow->spread()); + overflow.unite(shadowRect); + } + + boxShadow = boxShadow->next(); + } while (boxShadow); + backgroundRect.intersect(overflow); + } else + backgroundRect.intersect(layerBounds); + } +} + +IntRect RenderLayer::childrenClipRect() const +{ + RenderView* renderView = renderer()->view(); + RenderLayer* clippingRootLayer = clippingRoot(); + IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; + calculateRects(clippingRootLayer, renderView->documentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(foregroundRect)).enclosingBoundingBox(); +} + +IntRect RenderLayer::selfClipRect() const +{ + RenderView* renderView = renderer()->view(); + RenderLayer* clippingRootLayer = clippingRoot(); + IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; + calculateRects(clippingRootLayer, renderView->documentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + return clippingRootLayer->renderer()->localToAbsoluteQuad(FloatQuad(backgroundRect)).enclosingBoundingBox(); +} + +void RenderLayer::addBlockSelectionGapsBounds(const IntRect& bounds) +{ + m_blockSelectionGapsBounds.unite(bounds); +} + +void RenderLayer::clearBlockSelectionGapsBounds() +{ + m_blockSelectionGapsBounds = IntRect(); + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->clearBlockSelectionGapsBounds(); +} + +void RenderLayer::repaintBlockSelectionGaps() +{ + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->repaintBlockSelectionGaps(); + + if (m_blockSelectionGapsBounds.isEmpty()) + return; + + IntRect rect = m_blockSelectionGapsBounds; + rect.move(-scrolledContentOffset()); + if (renderer()->hasOverflowClip()) + rect.intersect(toRenderBox(renderer())->overflowClipRect(0, 0)); + if (renderer()->hasClip()) + rect.intersect(toRenderBox(renderer())->clipRect(0, 0)); + if (!rect.isEmpty()) + renderer()->repaintRectangle(rect); +} + +bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const +{ + // Always examine the canvas and the root. + // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // paints the root's background. + if (renderer()->isRenderView() || renderer()->isRoot()) + return true; + + // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we + // can go ahead and return true. + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && !renderer()->isRenderInline()) { + IntRect b = layerBounds; + b.inflate(view->maximalOutlineSize()); + if (b.intersects(damageRect)) + return true; + } + + // Otherwise we need to compute the bounding box of this single layer and see if it intersects + // the damage rect. + return boundingBox(rootLayer).intersects(damageRect); +} + +IntRect RenderLayer::localBoundingBox() const +{ + // There are three special cases we need to consider. + // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the + // inline. In other words, if some wraps to three lines, we'll create a bounding box that fully encloses the + // line boxes of all three lines (including overflow on those lines). + // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top + // overflow, we have to create a bounding box that will extend to include this overflow. + // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats + // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those + // floats. + IntRect result; + if (renderer()->isRenderInline()) { + // Go from our first line box to our last line box. + RenderInline* inlineFlow = toRenderInline(renderer()); + InlineFlowBox* firstBox = inlineFlow->firstLineBox(); + if (!firstBox) + return result; + int top = firstBox->topVisualOverflow(); + int bottom = inlineFlow->lastLineBox()->bottomVisualOverflow(); + int left = firstBox->x(); + for (InlineFlowBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) + left = min(left, curr->x()); + result = IntRect(left, top, width(), bottom - top); + } else if (renderer()->isTableRow()) { + // Our bounding box is just the union of all of our cells' border/overflow rects. + for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { + if (child->isTableCell()) { + IntRect bbox = toRenderBox(child)->borderBoxRect(); + result.unite(bbox); + IntRect overflowRect = renderBox()->visualOverflowRect(); + if (bbox != overflowRect) + result.unite(overflowRect); + } + } + } else { + RenderBox* box = renderBox(); + ASSERT(box); + if (box->hasMask()) + result = box->maskClipRect(); + else { + IntRect bbox = box->borderBoxRect(); + result = bbox; + IntRect overflowRect = box->visualOverflowRect(); + if (bbox != overflowRect) + result.unite(overflowRect); + } + } + + RenderView* view = renderer()->view(); + ASSERT(view); + if (view) + result.inflate(view->maximalOutlineSize()); // Used to apply a fudge factor to dirty-rect checks on blocks/tables. + + return result; +} + +IntRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer) const +{ + IntRect result = localBoundingBox(); + + int deltaX = 0, deltaY = 0; + convertToLayerCoords(ancestorLayer, deltaX, deltaY); + result.move(deltaX, deltaY); + return result; +} + +IntRect RenderLayer::absoluteBoundingBox() const +{ + return boundingBox(root()); +} + +void RenderLayer::clearClipRectsIncludingDescendants() +{ + if (!m_clipRects) + return; + + clearClipRects(); + + for (RenderLayer* l = firstChild(); l; l = l->nextSibling()) + l->clearClipRectsIncludingDescendants(); +} + +void RenderLayer::clearClipRects() +{ + if (m_clipRects) { + m_clipRects->deref(renderer()->renderArena()); + m_clipRects = 0; +#ifndef NDEBUG + m_clipRectsRoot = 0; +#endif + } +} + +#if USE(ACCELERATED_COMPOSITING) +RenderLayerBacking* RenderLayer::ensureBacking() +{ + if (!m_backing) + m_backing.set(new RenderLayerBacking(this)); + return m_backing.get(); +} + +void RenderLayer::clearBacking() +{ + m_backing.clear(); +} + +bool RenderLayer::hasCompositedMask() const +{ + return m_backing && m_backing->hasMaskLayer(); +} +#endif + +void RenderLayer::setParent(RenderLayer* parent) +{ + if (parent == m_parent) + return; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWillBeRemoved(m_parent, this); +#endif + + m_parent = parent; + +#if USE(ACCELERATED_COMPOSITING) + if (m_parent && !renderer()->documentBeingDestroyed()) + compositor()->layerWasAdded(m_parent, this); +#endif +} + +static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) +{ + if (!obj1 || !obj2) + return 0; + + for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) + for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) + if (currObj1 == currObj2) + return currObj1; + + return 0; +} + +void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result) +{ + // We don't update :hover/:active state when the result is marked as readOnly. + if (request.readOnly()) + return; + + Document* doc = renderer()->document(); + + Node* activeNode = doc->activeNode(); + if (activeNode && !request.active()) { + // We are clearing the :active chain because the mouse has been released. + for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) { + if (curr->node() && !curr->isText()) + curr->node()->clearInActiveChain(); + } + doc->setActiveNode(0); + } else { + Node* newActiveNode = result.innerNode(); + if (!activeNode && newActiveNode && request.active()) { + // We are setting the :active chain and freezing it. If future moves happen, they + // will need to reference this chain. + for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) { + if (curr->node() && !curr->isText()) { + curr->node()->setInActiveChain(); + } + } + doc->setActiveNode(newActiveNode); + } + } + + // If the mouse is down and if this is a mouse move event, we want to restrict changes in + // :hover/:active to only apply to elements that are in the :active chain that we froze + // at the time the mouse went down. + bool mustBeInActiveChain = request.active() && request.mouseMove(); + + // Check to see if the hovered node has changed. If not, then we don't need to + // do anything. + RefPtr oldHoverNode = doc->hoverNode(); + Node* newHoverNode = result.innerNode(); + + // Update our current hover node. + doc->setHoverNode(newHoverNode); + + // We have two different objects. Fetch their renderers. + RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0; + RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0; + + // Locate the common ancestor render object for the two renderers. + RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj); + + Vector, 32> nodesToRemoveFromChain; + Vector, 32> nodesToAddToChain; + + if (oldHoverObj != newHoverObj) { + // The old hover path only needs to be cleared up to (and not including) the common ancestor; + for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) { + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) + nodesToRemoveFromChain.append(curr->node()); + } + } + + // Now set the hover state for our new object up to the root. + for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) { + if (curr->node() && !curr->isText() && (!mustBeInActiveChain || curr->node()->inActiveChain())) + nodesToAddToChain.append(curr->node()); + } + + size_t removeCount = nodesToRemoveFromChain.size(); + for (size_t i = 0; i < removeCount; ++i) { + nodesToRemoveFromChain[i]->setActive(false); + nodesToRemoveFromChain[i]->setHovered(false); + } + + size_t addCount = nodesToAddToChain.size(); + for (size_t i = 0; i < addCount; ++i) { + nodesToAddToChain[i]->setActive(request.active()); + nodesToAddToChain[i]->setHovered(true); + } +} + +// Helper for the sorting of layers by z-index. +static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) +{ + return first->zIndex() < second->zIndex(); +} + +void RenderLayer::dirtyZOrderLists() +{ + if (m_posZOrderList) + m_posZOrderList->clear(); + if (m_negZOrderList) + m_negZOrderList->clear(); + m_zOrderListsDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedRebuild(); +#endif +} + +void RenderLayer::dirtyStackingContextZOrderLists() +{ + RenderLayer* sc = stackingContext(); + if (sc) + sc->dirtyZOrderLists(); +} + +void RenderLayer::dirtyNormalFlowList() +{ + if (m_normalFlowList) + m_normalFlowList->clear(); + m_normalFlowListDirty = true; + +#if USE(ACCELERATED_COMPOSITING) + if (!renderer()->documentBeingDestroyed()) + compositor()->setCompositingLayersNeedRebuild(); +#endif +} + +void RenderLayer::updateZOrderLists() +{ + if (!isStackingContext() || !m_zOrderListsDirty) + return; + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + if (!m_reflection || reflectionLayer() != child) + child->collectLayers(m_posZOrderList, m_negZOrderList); + + // Sort the two lists. + if (m_posZOrderList) + std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); + + if (m_negZOrderList) + std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); + + m_zOrderListsDirty = false; +} + +void RenderLayer::updateNormalFlowList() +{ + if (!m_normalFlowListDirty) + return; + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + // Ignore non-overflow layers and reflections. + if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) { + if (!m_normalFlowList) + m_normalFlowList = new Vector; + m_normalFlowList->append(child); + } + } + + m_normalFlowListDirty = false; +} + +void RenderLayer::collectLayers(Vector*& posBuffer, Vector*& negBuffer) +{ + updateVisibilityStatus(); + + // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. + if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isNormalFlowOnly()) { + // Determine which buffer the child should be in. + Vector*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; + + // Create the buffer if it doesn't exist yet. + if (!buffer) + buffer = new Vector; + + // Append ourselves at the end of the appropriate buffer. + buffer->append(this); + } + + // Recur into our children to collect more layers, but only if we don't establish + // a stacking context. + if (m_hasVisibleDescendant && !isStackingContext()) { + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + // Ignore reflections. + if (!m_reflection || reflectionLayer() != child) + child->collectLayers(posBuffer, negBuffer); + } + } +} + +void RenderLayer::updateLayerListsIfNeeded() +{ + updateZOrderLists(); + updateNormalFlowList(); +} + +void RenderLayer::updateCompositingAndLayerListsIfNeeded() +{ +#if USE(ACCELERATED_COMPOSITING) + if (compositor()->inCompositingMode()) { + if ((isStackingContext() && m_zOrderListsDirty) || m_normalFlowListDirty) + compositor()->updateCompositingLayers(CompositingUpdateOnPaitingOrHitTest, this); + return; + } +#endif + updateLayerListsIfNeeded(); +} + +void RenderLayer::repaintIncludingDescendants() +{ + renderer()->repaint(); + for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->repaintIncludingDescendants(); +} + +#if USE(ACCELERATED_COMPOSITING) +void RenderLayer::setBackingNeedsRepaint() +{ + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absoluteBoundingBox()); + } else + backing()->setContentsNeedDisplay(); +} + +void RenderLayer::setBackingNeedsRepaintInRect(const IntRect& r) +{ + ASSERT(isComposited()); + if (backing()->paintingGoesToWindow()) { + // If we're trying to repaint the placeholder document layer, propagate the + // repaint to the native view system. + IntRect absRect(r); + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + absRect.move(x, y); + + RenderView* view = renderer()->view(); + if (view) + view->repaintViewRectangle(absRect); + } else + backing()->setContentsNeedDisplayInRect(r); +} + +// Since we're only painting non-composited layers, we know that they all share the same repaintContainer. +void RenderLayer::repaintIncludingNonCompositingDescendants(RenderBoxModelObject* repaintContainer) +{ + renderer()->repaintUsingContainer(repaintContainer, renderer()->clippedOverflowRectForRepaint(repaintContainer)); + + for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isComposited()) + curr->repaintIncludingNonCompositingDescendants(repaintContainer); + } +} +#endif + +bool RenderLayer::shouldBeNormalFlowOnly() const +{ + return (renderer()->hasOverflowClip() + || renderer()->hasReflection() + || renderer()->hasMask() + || renderer()->isVideo() + || renderer()->isEmbeddedObject() + || renderer()->isApplet() + || renderer()->isRenderIFrame() + || renderer()->style()->specifiesColumns()) + && !renderer()->isPositioned() + && !renderer()->isRelPositioned() + && !renderer()->hasTransform() + && !isTransparent(); +} + +bool RenderLayer::isSelfPaintingLayer() const +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (hasOverflowScroll()) + return true; +#endif + return !isNormalFlowOnly() + || renderer()->hasReflection() + || renderer()->hasMask() + || renderer()->isTableRow() + || renderer()->isVideo() + || renderer()->isEmbeddedObject() + || renderer()->isApplet() + || renderer()->isRenderIFrame(); +} + +void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle*) +{ + bool isNormalFlowOnly = shouldBeNormalFlowOnly(); + if (isNormalFlowOnly != m_isNormalFlowOnly) { + m_isNormalFlowOnly = isNormalFlowOnly; + RenderLayer* p = parent(); + if (p) + p->dirtyNormalFlowList(); + dirtyStackingContextZOrderLists(); + } + + if (renderer()->style()->overflowX() == OMARQUEE && renderer()->style()->marqueeBehavior() != MNONE && renderer()->isBox()) { + if (!m_marquee) + m_marquee = new RenderMarquee(this); + m_marquee->updateMarqueeStyle(); + } + else if (m_marquee) { + delete m_marquee; + m_marquee = 0; + } + + if (!hasReflection() && m_reflection) + removeReflection(); + else if (hasReflection()) { + if (!m_reflection) + createReflection(); + updateReflectionStyle(); + } + + // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa). + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + + updateScrollCornerStyle(); + updateResizerStyle(); + +#if USE(ACCELERATED_COMPOSITING) + updateTransform(); + + if (compositor()->updateLayerCompositingState(this)) + compositor()->setCompositingLayersNeedRebuild(); + else if (m_backing) + m_backing->updateGraphicsLayerGeometry(); + + if (m_backing && diff >= StyleDifferenceRepaint) + m_backing->setContentsNeedDisplay(); +#else + UNUSED_PARAM(diff); +#endif +} + +void RenderLayer::updateScrollCornerStyle() +{ + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr corner = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(SCROLLBAR_CORNER, actualRenderer->style()) : 0; + if (corner) { + if (!m_scrollCorner) { + m_scrollCorner = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); + m_scrollCorner->setParent(renderer()); + } + m_scrollCorner->setStyle(corner.release()); + } else if (m_scrollCorner) { + m_scrollCorner->destroy(); + m_scrollCorner = 0; + } +} + +void RenderLayer::updateResizerStyle() +{ + RenderObject* actualRenderer = renderer()->node() ? renderer()->node()->shadowAncestorNode()->renderer() : renderer(); + RefPtr resizer = renderer()->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RESIZER, actualRenderer->style()) : 0; + if (resizer) { + if (!m_resizer) { + m_resizer = new (renderer()->renderArena()) RenderScrollbarPart(renderer()->document()); + m_resizer->setParent(renderer()); + } + m_resizer->setStyle(resizer.release()); + } else if (m_resizer) { + m_resizer->destroy(); + m_resizer = 0; + } +} + +RenderLayer* RenderLayer::reflectionLayer() const +{ + return m_reflection ? m_reflection->layer() : 0; +} + +void RenderLayer::createReflection() +{ + ASSERT(!m_reflection); + m_reflection = new (renderer()->renderArena()) RenderReplica(renderer()->document()); + m_reflection->setParent(renderer()); // We create a 1-way connection. +} + +void RenderLayer::removeReflection() +{ + if (!m_reflection->documentBeingDestroyed()) + m_reflection->removeLayers(this); + + m_reflection->setParent(0); + m_reflection->destroy(); + m_reflection = 0; +} + +void RenderLayer::updateReflectionStyle() +{ + RefPtr newStyle = RenderStyle::create(); + newStyle->inheritFrom(renderer()->style()); + + // Map in our transform. + TransformOperations transform; + switch (renderer()->style()->boxReflect()->direction()) { + case ReflectionBelow: + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE)); + transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE)); + break; + case ReflectionAbove: + transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE)); + break; + case ReflectionRight: + transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE)); + break; + case ReflectionLeft: + transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE)); + transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE)); + break; + } + newStyle->setTransform(transform); + + // Map in our mask. + newStyle->setMaskBoxImage(renderer()->style()->boxReflect()->mask()); + + m_reflection->setStyle(newStyle.release()); +} + +} // namespace WebCore + +#ifndef NDEBUG +void showLayerTree(const WebCore::RenderLayer* layer) +{ + if (!layer) + return; + + if (WebCore::Frame* frame = layer->renderer()->frame()) { + WTF::String output = externalRepresentation(frame, WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers | WebCore::RenderAsTextShowAddresses | WebCore::RenderAsTextShowIDAndClass | WebCore::RenderAsTextDontUpdateLayout | WebCore::RenderAsTextShowLayoutState); + fprintf(stderr, "%s\n", output.utf8().data()); + } +} + +void showLayerTree(const WebCore::RenderObject* renderer) +{ + if (!renderer) + return; + showLayerTree(renderer->enclosingLayer()); +} +#endif diff --git a/Source/WebCore/rendering/RenderLayer.h b/Source/WebCore/rendering/RenderLayer.h new file mode 100644 index 0000000..cc3b21c --- /dev/null +++ b/Source/WebCore/rendering/RenderLayer.h @@ -0,0 +1,771 @@ +/* + * Copyright (C) 2003, 2009 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan + * David Baron + * Christian Biesinger + * Randall Jesup + * Roland Mainz + * Josh Soref + * Boris Zbarsky + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef RenderLayer_h +#define RenderLayer_h + +#include "RenderBox.h" +#include "ScrollBehavior.h" +#include "ScrollbarClient.h" +#include + +namespace WebCore { + +class HitTestRequest; +class HitTestResult; +class HitTestingTransformState; +class RenderMarquee; +class RenderReplica; +class RenderScrollbarPart; +class RenderStyle; +class RenderView; +class Scrollbar; +class TransformationMatrix; + +#if USE(ACCELERATED_COMPOSITING) +class RenderLayerBacking; +class RenderLayerCompositor; +#endif + +class ClipRects { +public: + ClipRects() + : m_refCnt(0) + , m_fixed(false) + { + } + + ClipRects(const IntRect& r) + : m_overflowClipRect(r) + , m_fixedClipRect(r) + , m_posClipRect(r) +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + , m_hitTestClip(r) +#endif + , m_refCnt(0) + , m_fixed(false) + { + } + + ClipRects(const ClipRects& other) + : m_overflowClipRect(other.overflowClipRect()) + , m_fixedClipRect(other.fixedClipRect()) + , m_posClipRect(other.posClipRect()) +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + , m_hitTestClip(other.hitTestClip()) +#endif + , m_refCnt(0) + , m_fixed(other.fixed()) + { + } + + void reset(const IntRect& r) + { + m_overflowClipRect = r; + m_fixedClipRect = r; + m_posClipRect = r; +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + m_hitTestClip = r; +#endif + m_fixed = false; + } + + const IntRect& overflowClipRect() const { return m_overflowClipRect; } + void setOverflowClipRect(const IntRect& r) { m_overflowClipRect = r; } + + const IntRect& fixedClipRect() const { return m_fixedClipRect; } + void setFixedClipRect(const IntRect&r) { m_fixedClipRect = r; } + + const IntRect& posClipRect() const { return m_posClipRect; } + void setPosClipRect(const IntRect& r) { m_posClipRect = r; } +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + const IntRect& hitTestClip() const { return m_hitTestClip; } + void setHitTestClip(const IntRect& r) { m_hitTestClip = r; } +#endif + + bool fixed() const { return m_fixed; } + void setFixed(bool fixed) { m_fixed = fixed; } + + void ref() { m_refCnt++; } + void deref(RenderArena* renderArena) { if (--m_refCnt == 0) destroy(renderArena); } + + void destroy(RenderArena*); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + bool operator==(const ClipRects& other) const + { + return m_overflowClipRect == other.overflowClipRect() && + m_fixedClipRect == other.fixedClipRect() && + m_posClipRect == other.posClipRect() && +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + m_hitTestClip == other.hitTestClip() && +#endif + m_fixed == other.fixed(); + } + + ClipRects& operator=(const ClipRects& other) + { + m_overflowClipRect = other.overflowClipRect(); + m_fixedClipRect = other.fixedClipRect(); + m_posClipRect = other.posClipRect(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + m_hitTestClip = other.hitTestClip(); +#endif + m_fixed = other.fixed(); + return *this; + } + +private: + // The normal operator new is disallowed on all render objects. + void* operator new(size_t) throw(); + +private: + IntRect m_overflowClipRect; + IntRect m_fixedClipRect; + IntRect m_posClipRect; +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + IntRect m_hitTestClip; +#endif + unsigned m_refCnt : 31; + bool m_fixed : 1; +}; + +class RenderLayer : public ScrollbarClient { +public: + friend class RenderReplica; + + RenderLayer(RenderBoxModelObject*); + ~RenderLayer(); + + RenderBoxModelObject* renderer() const { return m_renderer; } + RenderBox* renderBox() const { return m_renderer && m_renderer->isBox() ? toRenderBox(m_renderer) : 0; } + RenderLayer* parent() const { return m_parent; } + RenderLayer* previousSibling() const { return m_previous; } + RenderLayer* nextSibling() const { return m_next; } + RenderLayer* firstChild() const { return m_first; } + RenderLayer* lastChild() const { return m_last; } + + void addChild(RenderLayer* newChild, RenderLayer* beforeChild = 0); + RenderLayer* removeChild(RenderLayer*); + + void removeOnlyThisLayer(); + void insertOnlyThisLayer(); + + void repaintIncludingDescendants(); + +#if USE(ACCELERATED_COMPOSITING) + // Indicate that the layer contents need to be repainted. Only has an effect + // if layer compositing is being used, + void setBackingNeedsRepaint(); + void setBackingNeedsRepaintInRect(const IntRect& r); // r is in the coordinate space of the layer's render object + void repaintIncludingNonCompositingDescendants(RenderBoxModelObject* repaintContainer); +#endif + + void styleChanged(StyleDifference, const RenderStyle*); + + RenderMarquee* marquee() const { return m_marquee; } + + bool isNormalFlowOnly() const { return m_isNormalFlowOnly; } + bool isSelfPaintingLayer() const; + + bool requiresSlowRepaints() const; + + bool isTransparent() const; + RenderLayer* transparentPaintingAncestor(); + void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer, PaintBehavior); + + bool hasReflection() const { return renderer()->hasReflection(); } + bool isReflection() const { return renderer()->isReplica(); } + RenderReplica* reflection() const { return m_reflection; } + RenderLayer* reflectionLayer() const; + + const RenderLayer* root() const + { + const RenderLayer* curr = this; + while (curr->parent()) + curr = curr->parent(); + return curr; + } + + int x() const { return m_x; } + int y() const { return m_y; } + void setLocation(int x, int y) + { + m_x = x; + m_y = y; + } + + int width() const { return m_width; } + int height() const { return m_height; } + IntSize size() const { return IntSize(m_width, m_height); } + +#if PLATFORM(ANDROID) + void setWidth(int w); + void setHeight(int h); +#else + void setWidth(int w) { m_width = w; } + void setHeight(int h) { m_height = h; } +#endif + + int scrollWidth(); + int scrollHeight(); + + void panScrollFromPoint(const IntPoint&); + + // Scrolling methods for layers that can scroll their overflow. + void scrollByRecursively(int xDelta, int yDelta); + + IntSize scrolledContentOffset() const { return IntSize(scrollXOffset() + m_scrollLeftOverflow, scrollYOffset() + m_scrollTopOverflow); } + + int scrollXOffset() const { return m_scrollX + m_scrollOrigin.x(); } + int scrollYOffset() const { return m_scrollY + m_scrollOrigin.y(); } + + void scrollToOffset(int x, int y, bool updateScrollbars = true, bool repaint = true); + void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY + m_scrollOrigin.y()); } + void scrollToYOffset(int y) { scrollToOffset(m_scrollX + m_scrollOrigin.x(), y); } + void scrollRectToVisible(const IntRect&, bool scrollToAnchor = false, const ScrollAlignment& alignX = ScrollAlignment::alignCenterIfNeeded, const ScrollAlignment& alignY = ScrollAlignment::alignCenterIfNeeded); + + IntRect getRectToExpose(const IntRect& visibleRect, const IntRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); + + void setHasHorizontalScrollbar(bool); + void setHasVerticalScrollbar(bool); + + PassRefPtr createScrollbar(ScrollbarOrientation); + void destroyScrollbar(ScrollbarOrientation); + + Scrollbar* horizontalScrollbar() const { return m_hBar.get(); } + Scrollbar* verticalScrollbar() const { return m_vBar.get(); } + + int verticalScrollbarWidth() const; + int horizontalScrollbarHeight() const; + + bool hasOverflowControls() const; +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + bool hasOverflowScroll() const { return m_hasOverflowScroll; } + bool hasOverflowParent() const; +#endif + void positionOverflowControls(int tx, int ty); + bool isPointInResizeControl(const IntPoint& absolutePoint) const; + bool hitTestOverflowControls(HitTestResult&, const IntPoint& localPoint); + IntSize offsetFromResizeCorner(const IntPoint& absolutePoint) const; + + void paintOverflowControls(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + void paintScrollCorner(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + void paintResizer(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + + void updateScrollInfoAfterLayout(); + + bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1); + void autoscroll(); + + void resize(const PlatformMouseEvent&, const IntSize&); + bool inResizeMode() const { return m_inResizeMode; } + void setInResizeMode(bool b) { m_inResizeMode = b; } + + bool isRootLayer() const { return renderer()->isRenderView(); } + +#if USE(ACCELERATED_COMPOSITING) + RenderLayerCompositor* compositor() const; + + // Notification from the renderer that its content changed (e.g. current frame of image changed). + // Allows updates of layer content without repainting. + enum ContentChangeType { ImageChanged, MaskImageChanged, CanvasChanged, VideoChanged, FullScreenChanged }; + void contentChanged(ContentChangeType); +#endif + + // Returns true if the accelerated compositing is enabled + bool hasAcceleratedCompositing() const; + + bool canRender3DTransforms() const; + + void updateLayerPosition(); + + enum UpdateLayerPositionsFlag { + DoFullRepaint = 1, + CheckForRepaint = 1 << 1, + IsCompositingUpdateRoot = 1 << 2, + UpdateCompositingLayers = 1 << 3, + UpdatePagination = 1 << 4 + }; + typedef unsigned UpdateLayerPositionsFlags; + void updateLayerPositions(UpdateLayerPositionsFlags = DoFullRepaint | IsCompositingUpdateRoot | UpdateCompositingLayers, IntPoint* cachedOffset = 0); + + void updateTransform(); + + void relativePositionOffset(int& relX, int& relY) const { relX += m_relX; relY += m_relY; } + IntSize relativePositionOffset() const { return IntSize(m_relX, m_relY); } + + void clearClipRectsIncludingDescendants(); + void clearClipRects(); + + void addBlockSelectionGapsBounds(const IntRect&); + void clearBlockSelectionGapsBounds(); + void repaintBlockSelectionGaps(); + + // Get the enclosing stacking context for this layer. A stacking context is a layer + // that has a non-auto z-index. + RenderLayer* stackingContext() const; +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + bool isFixed() const { return renderer()->isPositioned() && renderer()->style()->position() == FixedPosition; } + // If fixed elements are composited, they will be containing children + bool isStackingContext() const { +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (hasOverflowScroll()) + return true; +#endif + return !hasAutoZIndex() || renderer()->isRenderView() || (isComposited() && isFixed()); + } +#else +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isRenderView() || hasOverflowScroll(); } +#else + bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isRenderView() ; } +#endif +#endif + + void dirtyZOrderLists(); + void dirtyStackingContextZOrderLists(); + void updateZOrderLists(); + Vector* posZOrderList() const { return m_posZOrderList; } + Vector* negZOrderList() const { return m_negZOrderList; } + + void dirtyNormalFlowList(); + void updateNormalFlowList(); + Vector* normalFlowList() const { return m_normalFlowList; } + + bool hasVisibleContent() const { return m_hasVisibleContent; } + bool hasVisibleDescendant() const { return m_hasVisibleDescendant; } + void setHasVisibleContent(bool); + void dirtyVisibleContentStatus(); + + // Gets the nearest enclosing positioned ancestor layer (also includes + // the layer and the root layer). + RenderLayer* enclosingPositionedAncestor() const; + + // The layer relative to which clipping rects for this layer are computed. + RenderLayer* clippingRoot() const; + +#if USE(ACCELERATED_COMPOSITING) + // Enclosing compositing layer; if includeSelf is true, may return this. + RenderLayer* enclosingCompositingLayer(bool includeSelf = true) const; + // Ancestor compositing layer, excluding this. + RenderLayer* ancestorCompositingLayer() const { return enclosingCompositingLayer(false); } +#endif + + void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const; + + bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); } + int zIndex() const { return renderer()->style()->zIndex(); } + + // The two main functions that use the layer system. The paint method + // paints the layers that intersect the damage rect from back to + // front. The hitTest method looks for mouse events by walking + // layers that intersect the point from front to back. + void paint(GraphicsContext*, const IntRect& damageRect, PaintBehavior = PaintBehaviorNormal, RenderObject* paintingRoot = 0); + bool hitTest(const HitTestRequest&, HitTestResult&); + + // This method figures out our layerBounds in coordinates relative to + // |rootLayer}. It also computes our background and foreground clip rects + // for painting/event handling. + void calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, + IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects = false) const; + + // Compute and cache clip rects computed with the given layer as the root + void updateClipRects(const RenderLayer* rootLayer); + // Compute and return the clip rects. If useCached is true, will used previously computed clip rects on ancestors + // (rather than computing them all from scratch up the parent chain). + void calculateClipRects(const RenderLayer* rootLayer, ClipRects&, bool useCached = false) const; + ClipRects* clipRects() const { return m_clipRects; } + + IntRect childrenClipRect() const; // Returns the foreground clip rect of the layer in the document's coordinate space. + IntRect selfClipRect() const; // Returns the background clip rect of the layer in the document's coordinate space. + + bool intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const; + + // Bounding box relative to some ancestor layer. + IntRect boundingBox(const RenderLayer* rootLayer) const; + // Bounding box in the coordinates of this layer. + IntRect localBoundingBox() const; + // Bounding box relative to the root. + IntRect absoluteBoundingBox() const; + + void updateHoverActiveState(const HitTestRequest&, HitTestResult&); + + // Return a cached repaint rect, computed relative to the layer renderer's containerForRepaint. + IntRect repaintRect() const { return m_repaintRect; } + IntRect repaintRectIncludingDescendants() const; + void computeRepaintRects(); + void updateRepaintRectsAfterScroll(bool fixed = false); + void setNeedsFullRepaint(bool f = true) { m_needsFullRepaint = f; } + + int staticX() const { return m_staticX; } + int staticY() const { return m_staticY; } + void setStaticX(int staticX) { m_staticX = staticX; } + void setStaticY(int staticY) { m_staticY = staticY; } + + bool hasTransform() const { return renderer()->hasTransform(); } + // Note that this transform has the transform-origin baked in. + TransformationMatrix* transform() const { return m_transform.get(); } + // currentTransform computes a transform which takes accelerated animations into account. The + // resulting transform has transform-origin baked in. If the layer does not have a transform, + // returns the identity matrix. + TransformationMatrix currentTransform() const; + TransformationMatrix renderableTransform(PaintBehavior) const; + + // Get the perspective transform, which is applied to transformed sublayers. + // Returns true if the layer has a -webkit-perspective. + // Note that this transform has the perspective-origin baked in. + TransformationMatrix perspectiveTransform() const; + FloatPoint perspectiveOrigin() const; + bool preserves3D() const { return renderer()->style()->transformStyle3D() == TransformStyle3DPreserve3D; } + bool has3DTransform() const { return m_transform && !m_transform->isAffine(); } + + // Overloaded new operator. Derived classes must override operator new + // in order to allocate out of the RenderArena. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +#if USE(ACCELERATED_COMPOSITING) + bool isComposited() const { return m_backing != 0; } + bool hasCompositedMask() const; + RenderLayerBacking* backing() const { return m_backing.get(); } + RenderLayerBacking* ensureBacking(); + void clearBacking(); +#else + bool isComposited() const { return false; } + bool hasCompositedMask() const { return false; } +#endif + + bool paintsWithTransparency(PaintBehavior paintBehavior) const + { + return isTransparent() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || !isComposited()); + } + + bool paintsWithTransform(PaintBehavior paintBehavior) const + { + return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || !isComposited()); + } + +private: + // The normal operator new is disallowed on all render objects. + void* operator new(size_t) throw(); + +private: + void setNextSibling(RenderLayer* next) { m_next = next; } + void setPreviousSibling(RenderLayer* prev) { m_previous = prev; } + void setParent(RenderLayer* parent); + void setFirstChild(RenderLayer* first) { m_first = first; } + void setLastChild(RenderLayer* last) { m_last = last; } + + int renderBoxX() const { return renderer()->isBox() ? toRenderBox(renderer())->x() : 0; } + int renderBoxY() const { return renderer()->isBox() ? toRenderBox(renderer())->y() : 0; } + + void collectLayers(Vector*&, Vector*&); + + void updateLayerListsIfNeeded(); + void updateCompositingAndLayerListsIfNeeded(); + + enum PaintLayerFlag { + PaintLayerHaveTransparency = 1, + PaintLayerAppliedTransform = 1 << 1, + PaintLayerTemporaryClipRects = 1 << 2, + PaintLayerPaintingReflection = 1 << 3 + }; + + typedef unsigned PaintLayerFlags; + + void paintLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, + PaintBehavior, RenderObject* paintingRoot, OverlapTestRequestMap* = 0, + PaintLayerFlags = 0); + void paintList(Vector*, RenderLayer* rootLayer, GraphicsContext* p, + const IntRect& paintDirtyRect, PaintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap*, + PaintLayerFlags); + void paintPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, GraphicsContext*, + const IntRect& paintDirtyRect, PaintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap*, + PaintLayerFlags); + void paintChildLayerIntoColumns(RenderLayer* childLayer, RenderLayer* rootLayer, GraphicsContext*, + const IntRect& paintDirtyRect, PaintBehavior, + RenderObject* paintingRoot, OverlapTestRequestMap*, + PaintLayerFlags, const Vector& columnLayers, size_t columnIndex); + + RenderLayer* hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform, + const HitTestingTransformState* transformState = 0, double* zOffset = 0); + RenderLayer* hitTestList(Vector*, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* transformState, double* zOffsetForDescendants, double* zOffset, + const HitTestingTransformState* unflattenedTransformState, bool depthSortDescendants); + RenderLayer* hitTestPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* transformState, double* zOffset); + RenderLayer* hitTestChildLayerColumns(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* transformState, double* zOffset, + const Vector& columnLayers, size_t columnIndex); + + PassRefPtr createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, + const HitTestingTransformState* containerTransformState) const; + + bool hitTestContents(const HitTestRequest&, HitTestResult&, const IntRect& layerBounds, const IntPoint& hitTestPoint, HitTestFilter) const; + + void computeScrollDimensions(bool* needHBar = 0, bool* needVBar = 0); + + bool shouldBeNormalFlowOnly() const; + + // ScrollBarClient interface + virtual int scrollSize(ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const IntPoint&); + virtual void valueChanged(Scrollbar*); + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual bool isActive() const; + virtual bool scrollbarCornerPresent() const; + virtual IntRect convertFromScrollbarToContainingView(const Scrollbar*, const IntRect&) const; + virtual IntRect convertFromContainingViewToScrollbar(const Scrollbar*, const IntRect&) const; + virtual IntPoint convertFromScrollbarToContainingView(const Scrollbar*, const IntPoint&) const; + virtual IntPoint convertFromContainingViewToScrollbar(const Scrollbar*, const IntPoint&) const; + + IntSize scrollbarOffset(const Scrollbar*) const; + + void updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow); + + void childVisibilityChanged(bool newVisibility); + void dirtyVisibleDescendantStatus(); + void updateVisibilityStatus(); + + // This flag is computed by RenderLayerCompositor, which knows more about 3d hierarchies than we do. + void setHas3DTransformedDescendant(bool b) { m_has3DTransformedDescendant = b; } + bool has3DTransformedDescendant() const { return m_has3DTransformedDescendant; } + + void dirty3DTransformedDescendantStatus(); + // Both updates the status, and returns true if descendants of this have 3d. + bool update3DTransformedDescendantStatus(); + + Node* enclosingElement() const; + + void createReflection(); + void removeReflection(); + + void updateReflectionStyle(); + bool paintingInsideReflection() const { return m_paintingInsideReflection; } + void setPaintingInsideReflection(bool b) { m_paintingInsideReflection = b; } + + void parentClipRects(const RenderLayer* rootLayer, ClipRects&, bool temporaryClipRects = false) const; + IntRect backgroundClipRect(const RenderLayer* rootLayer, bool temporaryClipRects) const; + + RenderLayer* enclosingTransformedAncestor() const; + + // Convert a point in absolute coords into layer coords, taking transforms into account + IntPoint absoluteToContents(const IntPoint&) const; + + void updateScrollCornerStyle(); + void updateResizerStyle(); + + void updatePagination(); + bool isPaginated() const { return m_isPaginated; } + +#if USE(ACCELERATED_COMPOSITING) + bool hasCompositingDescendant() const { return m_hasCompositingDescendant; } + void setHasCompositingDescendant(bool b) { m_hasCompositingDescendant = b; } + + bool mustOverlapCompositedLayers() const { return m_mustOverlapCompositedLayers; } + void setMustOverlapCompositedLayers(bool b) { m_mustOverlapCompositedLayers = b; } +#endif + +private: + friend class RenderLayerBacking; + friend class RenderLayerCompositor; + friend class RenderBoxModelObject; + + // Only safe to call from RenderBoxModelObject::destroyLayer(RenderArena*) + void destroy(RenderArena*); + + int overflowTop() const; + int overflowBottom() const; + int overflowLeft() const; + int overflowRight() const; + +protected: + RenderBoxModelObject* m_renderer; + + RenderLayer* m_parent; + RenderLayer* m_previous; + RenderLayer* m_next; + RenderLayer* m_first; + RenderLayer* m_last; + + IntRect m_repaintRect; // Cached repaint rects. Used by layout. + IntRect m_outlineBox; + + // Our current relative position offset. + int m_relX; + int m_relY; + + // Our (x,y) coordinates are in our parent layer's coordinate space. + int m_x; + int m_y; + + // The layer's width/height + int m_width; + int m_height; + + // Our scroll offsets if the view is scrolled. + int m_scrollX; + int m_scrollY; + + // There are 8 possible combinations of writing mode and direction. Scroll origin (and its corresponding left/top overflow) + // will be non-zero in the x or y axis if there is any reversed direction or writing-mode. The combinations are: + // writing-mode / direction scrollOrigin.x() set scrollOrigin.y() set + // horizontal-tb / ltr NO NO + // horizontal-tb / rtl YES NO + // horizontal-bt / ltr NO YES + // horizontal-bt / rtl YES YES + // vertical-lr / ltr NO NO + // vertical-lr / rtl NO YES + // vertical-rl / ltr YES NO + // vertical-rl / rtl YES YES + IntPoint m_scrollOrigin; + int m_scrollLeftOverflow; + int m_scrollTopOverflow; + + // The width/height of our scrolled area. + int m_scrollWidth; + int m_scrollHeight; + + // For layers with overflow, we have a pair of scrollbars. + RefPtr m_hBar; + RefPtr m_vBar; + + // Keeps track of whether the layer is currently resizing, so events can cause resizing to start and stop. + bool m_inResizeMode; + + // For layers that establish stacking contexts, m_posZOrderList holds a sorted list of all the + // descendant layers within the stacking context that have z-indices of 0 or greater + // (auto will count as 0). m_negZOrderList holds descendants within our stacking context with negative + // z-indices. + Vector* m_posZOrderList; + Vector* m_negZOrderList; + + // This list contains child layers that cannot create stacking contexts. For now it is just + // overflow layers, but that may change in the future. + Vector* m_normalFlowList; + + ClipRects* m_clipRects; // Cached clip rects used when painting and hit testing. +#ifndef NDEBUG + const RenderLayer* m_clipRectsRoot; // Root layer used to compute clip rects. +#endif + + bool m_scrollDimensionsDirty : 1; + bool m_zOrderListsDirty : 1; + bool m_normalFlowListDirty: 1; + bool m_isNormalFlowOnly : 1; + + bool m_usedTransparency : 1; // Tracks whether we need to close a transparent layer, i.e., whether + // we ended up painting this layer or any descendants (and therefore need to + // blend). + bool m_paintingInsideReflection : 1; // A state bit tracking if we are painting inside a replica. + bool m_inOverflowRelayout : 1; + bool m_needsFullRepaint : 1; + + bool m_overflowStatusDirty : 1; + bool m_horizontalOverflow : 1; + bool m_verticalOverflow : 1; + bool m_visibleContentStatusDirty : 1; + bool m_hasVisibleContent : 1; + bool m_visibleDescendantStatusDirty : 1; + bool m_hasVisibleDescendant : 1; + + bool m_isPaginated : 1; // If we think this layer is split by a multi-column ancestor, then this bit will be set. + + bool m_3DTransformedDescendantStatusDirty : 1; + bool m_has3DTransformedDescendant : 1; // Set on a stacking context layer that has 3D descendants anywhere + // in a preserves3D hierarchy. Hint to do 3D-aware hit testing. +#if USE(ACCELERATED_COMPOSITING) + bool m_hasCompositingDescendant : 1; + bool m_mustOverlapCompositedLayers : 1; +#endif +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + bool m_hasOverflowScroll : 1; +#endif + + RenderMarquee* m_marquee; // Used by layers with overflow:marquee + + // Cached normal flow values for absolute positioned elements with static left/top values. + int m_staticX; + int m_staticY; + + OwnPtr m_transform; + + // May ultimately be extended to many replicas (with their own paint order). + RenderReplica* m_reflection; + + // Renderers to hold our custom scroll corner and resizer. + RenderScrollbarPart* m_scrollCorner; + RenderScrollbarPart* m_resizer; + +private: + IntRect m_blockSelectionGapsBounds; + +#if USE(ACCELERATED_COMPOSITING) + OwnPtr m_backing; +#endif +}; + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showLayerTree(const WebCore::RenderLayer*); +void showLayerTree(const WebCore::RenderObject*); +#endif + +#endif // RenderLayer_h diff --git a/Source/WebCore/rendering/RenderLayerBacking.cpp b/Source/WebCore/rendering/RenderLayerBacking.cpp new file mode 100644 index 0000000..c430bae --- /dev/null +++ b/Source/WebCore/rendering/RenderLayerBacking.cpp @@ -0,0 +1,1366 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "RenderLayerBacking.h" + +#include "AnimationController.h" +#include "CanvasRenderingContext.h" +#include "CanvasRenderingContext2D.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "GraphicsContext3D.h" +#include "GraphicsLayer.h" +#include "HTMLCanvasElement.h" +#include "HTMLElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "InspectorInstrumentation.h" +#include "KeyframeList.h" +#include "PluginViewBase.h" +#include "RenderApplet.h" +#include "RenderBox.h" +#include "RenderIFrame.h" +#include "RenderImage.h" +#include "RenderLayerCompositor.h" +#include "RenderEmbeddedObject.h" +#include "RenderVideo.h" +#include "RenderView.h" +#include "Settings.h" +#include "WebGLRenderingContext.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +static bool hasBorderOutlineOrShadow(const RenderStyle*); +static bool hasBoxDecorationsOrBackground(const RenderObject*); +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle*); +static IntRect clipBox(RenderBox* renderer); + +static inline bool isAcceleratedCanvas(RenderObject* renderer) +{ +#if ENABLE(3D_CANVAS) || ENABLE(ACCELERATED_2D_CANVAS) + if (renderer->isCanvas()) { + HTMLCanvasElement* canvas = static_cast(renderer->node()); + if (CanvasRenderingContext* context = canvas->renderingContext()) + return context->isAccelerated(); + } +#else + UNUSED_PARAM(renderer); +#endif + return false; +} + +RenderLayerBacking::RenderLayerBacking(RenderLayer* layer) + : m_owningLayer(layer) + , m_artificiallyInflatedBounds(false) +{ + createGraphicsLayer(); +} + +RenderLayerBacking::~RenderLayerBacking() +{ + updateClippingLayers(false, false); + updateForegroundLayer(false); + updateMaskLayer(false); + destroyGraphicsLayer(); +} + +void RenderLayerBacking::createGraphicsLayer() +{ + m_graphicsLayer = GraphicsLayer::create(this); + +#ifndef NDEBUG + m_graphicsLayer->setName(nameForLayer()); +#endif // NDEBUG + + updateLayerOpacity(renderer()->style()); + updateLayerTransform(renderer()->style()); +} + +void RenderLayerBacking::destroyGraphicsLayer() +{ + if (m_graphicsLayer) + m_graphicsLayer->removeFromParent(); + + m_graphicsLayer = 0; + m_foregroundLayer = 0; + m_clippingLayer = 0; + m_maskLayer = 0; +} + +void RenderLayerBacking::updateLayerOpacity(const RenderStyle* style) +{ + m_graphicsLayer->setOpacity(compositingOpacity(style->opacity())); +} + +void RenderLayerBacking::updateLayerTransform(const RenderStyle* style) +{ + // FIXME: This could use m_owningLayer->transform(), but that currently has transform-origin + // baked into it, and we don't want that. + TransformationMatrix t; + if (m_owningLayer->hasTransform()) { + style->applyTransform(t, toRenderBox(renderer())->borderBoxRect().size(), RenderStyle::ExcludeTransformOrigin); + makeMatrixRenderable(t, compositor()->canRender3DTransforms()); + } + + m_graphicsLayer->setTransform(t); +} + +static bool hasNonZeroTransformOrigin(const RenderObject* renderer) +{ + RenderStyle* style = renderer->style(); + return (style->transformOriginX().type() == Fixed && style->transformOriginX().value()) + || (style->transformOriginY().type() == Fixed && style->transformOriginY().value()); +} + +static bool layerOrAncestorIsTransformed(RenderLayer* layer) +{ + for (RenderLayer* curr = layer; curr; curr = curr->parent()) { + if (curr->hasTransform()) + return true; + } + + return false; +} + +#if ENABLE(FULLSCREEN_API) +static bool layerOrAncestorIsFullScreen(RenderLayer* layer) +{ + // Don't traverse through the render layer tree if we do not yet have a full screen renderer. + if (!layer->renderer()->document()->fullScreenRenderer()) + return false; + + for (RenderLayer* curr = layer; curr; curr = curr->parent()) { + if (curr->renderer()->isRenderFullScreen()) + return true; + } + + return false; +} +#endif + +void RenderLayerBacking::updateCompositedBounds() +{ + IntRect layerBounds = compositor()->calculateCompositedBounds(m_owningLayer, m_owningLayer); + + // Clip to the size of the document or enclosing overflow-scroll layer. + // If this or an ancestor is transformed, we can't currently compute the correct rect to intersect with. + // We'd need RenderObject::convertContainerToLocalQuad(), which doesn't yet exist. If this + // is a fullscreen renderer, don't clip to the viewport, as the renderer will be asked to + // display outside of the viewport bounds. + if (compositor()->compositingConsultsOverlap() && !layerOrAncestorIsTransformed(m_owningLayer) +#if ENABLE(FULLSCREEN_API) + && !layerOrAncestorIsFullScreen(m_owningLayer) +#endif + ) { + RenderView* view = m_owningLayer->renderer()->view(); + RenderLayer* rootLayer = view->layer(); + + // Start by clipping to the view's bounds. + IntRect clippingBounds = view->layoutOverflowRect(); + + if (m_owningLayer != rootLayer) + clippingBounds.intersect(m_owningLayer->backgroundClipRect(rootLayer, true)); + + int deltaX = 0; + int deltaY = 0; + m_owningLayer->convertToLayerCoords(rootLayer, deltaX, deltaY); + clippingBounds.move(-deltaX, -deltaY); + + layerBounds.intersect(clippingBounds); + } + + // If the element has a transform-origin that has fixed lengths, and the renderer has zero size, + // then we need to ensure that the compositing layer has non-zero size so that we can apply + // the transform-origin via the GraphicsLayer anchorPoint (which is expressed as a fractional value). + if (layerBounds.isEmpty() && hasNonZeroTransformOrigin(renderer())) { + layerBounds.setWidth(1); + layerBounds.setHeight(1); + m_artificiallyInflatedBounds = true; + } else + m_artificiallyInflatedBounds = false; + + setCompositedBounds(layerBounds); +} + +void RenderLayerBacking::updateAfterWidgetResize() +{ + if (renderer()->isRenderIFrame()) { + if (RenderLayerCompositor* innerCompositor = RenderLayerCompositor::iframeContentsCompositor(toRenderIFrame(renderer()))) + innerCompositor->frameViewDidChangeSize(contentsBox().location()); + } +} + +void RenderLayerBacking::updateAfterLayout(UpdateDepth updateDepth, bool isUpdateRoot) +{ + RenderLayerCompositor* layerCompositor = compositor(); + if (!layerCompositor->compositingLayersNeedRebuild()) { + // Calling updateGraphicsLayerGeometry() here gives incorrect results, because the + // position of this layer's GraphicsLayer depends on the position of our compositing + // ancestor's GraphicsLayer. That cannot be determined until all the descendant + // RenderLayers of that ancestor have been processed via updateLayerPositions(). + // + // The solution is to update compositing children of this layer here, + // via updateCompositingChildrenGeometry(). + updateCompositedBounds(); + layerCompositor->updateCompositingDescendantGeometry(m_owningLayer, m_owningLayer, updateDepth); + + if (isUpdateRoot) { + updateGraphicsLayerGeometry(); + layerCompositor->updateRootLayerPosition(); + } + } +} + +bool RenderLayerBacking::updateGraphicsLayerConfiguration() +{ + RenderLayerCompositor* compositor = this->compositor(); + RenderObject* renderer = this->renderer(); + + bool layerConfigChanged = false; + if (updateForegroundLayer(compositor->needsContentsCompositingLayer(m_owningLayer))) + layerConfigChanged = true; + + if (updateClippingLayers(compositor->clippedByAncestor(m_owningLayer), compositor->clipsCompositingDescendants(m_owningLayer))) + layerConfigChanged = true; + + if (updateMaskLayer(renderer->hasMask())) + m_graphicsLayer->setMaskLayer(m_maskLayer.get()); + + if (m_owningLayer->hasReflection()) { + if (m_owningLayer->reflectionLayer()->backing()) { + GraphicsLayer* reflectionLayer = m_owningLayer->reflectionLayer()->backing()->graphicsLayer(); + m_graphicsLayer->setReplicatedByLayer(reflectionLayer); + } + } else + m_graphicsLayer->setReplicatedByLayer(0); + + if (isDirectlyCompositedImage()) + updateImageContents(); + + if ((renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing()) + || (renderer->isApplet() && toRenderApplet(renderer)->allowsAcceleratedCompositing())) { + PluginViewBase* pluginViewBase = static_cast(toRenderWidget(renderer)->widget()); + m_graphicsLayer->setContentsToMedia(pluginViewBase->platformLayer()); + } +#if ENABLE(VIDEO) + else if (renderer->isVideo()) { + HTMLMediaElement* mediaElement = static_cast(renderer->node()); + m_graphicsLayer->setContentsToMedia(mediaElement->platformLayer()); + } +#endif +#if ENABLE(3D_CANVAS) || ENABLE(ACCELERATED_2D_CANVAS) + else if (isAcceleratedCanvas(renderer)) { + HTMLCanvasElement* canvas = static_cast(renderer->node()); + if (CanvasRenderingContext* context = canvas->renderingContext()) + m_graphicsLayer->setContentsToCanvas(context->platformLayer()); + layerConfigChanged = true; + } +#endif + + if (renderer->isRenderIFrame()) + layerConfigChanged = RenderLayerCompositor::parentIFrameContentLayers(toRenderIFrame(renderer)); + + return layerConfigChanged; +} + +static IntRect clipBox(RenderBox* renderer) +{ + IntRect result = PaintInfo::infiniteRect(); + if (renderer->hasOverflowClip()) + result = renderer->overflowClipRect(0, 0); + + if (renderer->hasClip()) + result.intersect(renderer->clipRect(0, 0)); + + return result; +} + +void RenderLayerBacking::updateGraphicsLayerGeometry() +{ + // If we haven't built z-order lists yet, wait until later. + if (m_owningLayer->isStackingContext() && m_owningLayer->m_zOrderListsDirty) + return; + + // Set transform property, if it is not animating. We have to do this here because the transform + // is affected by the layer dimensions. + if (!renderer()->animation()->isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyWebkitTransform)) + updateLayerTransform(renderer()->style()); + + // Set opacity, if it is not animating. + if (!renderer()->animation()->isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyOpacity)) + updateLayerOpacity(renderer()->style()); + + RenderStyle* style = renderer()->style(); + m_graphicsLayer->setPreserves3D(style->transformStyle3D() == TransformStyle3DPreserve3D && !renderer()->hasReflection()); + m_graphicsLayer->setBackfaceVisibility(style->backfaceVisibility() == BackfaceVisibilityVisible); + + RenderLayer* compAncestor = m_owningLayer->ancestorCompositingLayer(); + + // We compute everything relative to the enclosing compositing layer. + IntRect ancestorCompositingBounds; + if (compAncestor) { + ASSERT(compAncestor->backing()); + ancestorCompositingBounds = compAncestor->backing()->compositedBounds(); + } + + IntRect localCompositingBounds = compositedBounds(); + + IntRect relativeCompositingBounds(localCompositingBounds); + int deltaX = 0, deltaY = 0; + m_owningLayer->convertToLayerCoords(compAncestor, deltaX, deltaY); + relativeCompositingBounds.move(deltaX, deltaY); + + IntPoint graphicsLayerParentLocation; + if (compAncestor && compAncestor->backing()->hasClippingLayer()) { + // If the compositing ancestor has a layer to clip children, we parent in that, and therefore + // position relative to it. + IntRect clippingBox = clipBox(toRenderBox(compAncestor->renderer())); + graphicsLayerParentLocation = clippingBox.location(); + } else + graphicsLayerParentLocation = ancestorCompositingBounds.location(); + + if (compAncestor && m_ancestorClippingLayer) { + // Call calculateRects to get the backgroundRect which is what is used to clip the contents of this + // layer. Note that we call it with temporaryClipRects = true because normally when computing clip rects + // for a compositing layer, rootLayer is the layer itself. + IntRect parentClipRect = m_owningLayer->backgroundClipRect(compAncestor, true); + m_ancestorClippingLayer->setPosition(FloatPoint() + (parentClipRect.location() - graphicsLayerParentLocation)); + m_ancestorClippingLayer->setSize(parentClipRect.size()); + + // backgroundRect is relative to compAncestor, so subtract deltaX/deltaY to get back to local coords. + IntSize rendererOffset(parentClipRect.location().x() - deltaX, parentClipRect.location().y() - deltaY); + m_ancestorClippingLayer->setOffsetFromRenderer(rendererOffset); + + // The primary layer is then parented in, and positioned relative to this clipping layer. + graphicsLayerParentLocation = parentClipRect.location(); + } + + m_graphicsLayer->setPosition(FloatPoint() + (relativeCompositingBounds.location() - graphicsLayerParentLocation)); + + IntSize oldOffsetFromRenderer = m_graphicsLayer->offsetFromRenderer(); + m_graphicsLayer->setOffsetFromRenderer(localCompositingBounds.location() - IntPoint()); + // If the compositing layer offset changes, we need to repaint. + if (oldOffsetFromRenderer != m_graphicsLayer->offsetFromRenderer()) + m_graphicsLayer->setNeedsDisplay(); + + FloatSize oldSize = m_graphicsLayer->size(); + FloatSize newSize = relativeCompositingBounds.size(); + if (oldSize != newSize) { + m_graphicsLayer->setSize(newSize); + // A bounds change will almost always require redisplay. Usually that redisplay + // will happen because of a repaint elsewhere, but not always: + // e.g. see RenderView::setMaximalOutlineSize() + m_graphicsLayer->setNeedsDisplay(); + } + + // If we have a layer that clips children, position it. + IntRect clippingBox; + if (m_clippingLayer) { + clippingBox = clipBox(toRenderBox(renderer())); + m_clippingLayer->setPosition(FloatPoint() + (clippingBox.location() - localCompositingBounds.location())); + m_clippingLayer->setSize(clippingBox.size()); + m_clippingLayer->setOffsetFromRenderer(clippingBox.location() - IntPoint()); + } + + if (m_maskLayer) { + if (m_maskLayer->size() != m_graphicsLayer->size()) { + m_maskLayer->setSize(m_graphicsLayer->size()); + m_maskLayer->setNeedsDisplay(); + } + m_maskLayer->setPosition(FloatPoint()); + } + + if (m_owningLayer->hasTransform()) { + const IntRect borderBox = toRenderBox(renderer())->borderBoxRect(); + + // Get layout bounds in the coords of compAncestor to match relativeCompositingBounds. + IntRect layerBounds = IntRect(deltaX, deltaY, borderBox.width(), borderBox.height()); + + // Update properties that depend on layer dimensions + FloatPoint3D transformOrigin = computeTransformOrigin(borderBox); + // Compute the anchor point, which is in the center of the renderer box unless transform-origin is set. + FloatPoint3D anchor(relativeCompositingBounds.width() != 0.0f ? ((layerBounds.x() - relativeCompositingBounds.x()) + transformOrigin.x()) / relativeCompositingBounds.width() : 0.5f, + relativeCompositingBounds.height() != 0.0f ? ((layerBounds.y() - relativeCompositingBounds.y()) + transformOrigin.y()) / relativeCompositingBounds.height() : 0.5f, + transformOrigin.z()); + m_graphicsLayer->setAnchorPoint(anchor); + + RenderStyle* style = renderer()->style(); + if (style->hasPerspective()) { + TransformationMatrix t = owningLayer()->perspectiveTransform(); + + if (m_clippingLayer) { + m_clippingLayer->setChildrenTransform(t); + m_graphicsLayer->setChildrenTransform(TransformationMatrix()); + } + else + m_graphicsLayer->setChildrenTransform(t); + } else { + if (m_clippingLayer) + m_clippingLayer->setChildrenTransform(TransformationMatrix()); + else + m_graphicsLayer->setChildrenTransform(TransformationMatrix()); + } + } else { + m_graphicsLayer->setAnchorPoint(FloatPoint3D(0.5f, 0.5f, 0)); + } + + if (m_foregroundLayer) { + FloatPoint foregroundPosition; + FloatSize foregroundSize = newSize; + IntSize foregroundOffset = m_graphicsLayer->offsetFromRenderer(); + // If we have a clipping layer (which clips descendants), then the foreground layer is a child of it, + // so that it gets correctly sorted with children. In that case, position relative to the clipping layer. + if (m_clippingLayer) { + foregroundPosition = FloatPoint() + (localCompositingBounds.location() - clippingBox.location()); + foregroundSize = FloatSize(clippingBox.size()); + foregroundOffset = clippingBox.location() - IntPoint(); + } + + m_foregroundLayer->setPosition(foregroundPosition); + m_foregroundLayer->setSize(foregroundSize); + m_foregroundLayer->setOffsetFromRenderer(foregroundOffset); + } + + if (m_owningLayer->reflectionLayer() && m_owningLayer->reflectionLayer()->isComposited()) { + RenderLayerBacking* reflectionBacking = m_owningLayer->reflectionLayer()->backing(); + reflectionBacking->updateGraphicsLayerGeometry(); + + // The reflection layer has the bounds of m_owningLayer->reflectionLayer(), + // but the reflected layer is the bounds of this layer, so we need to position it appropriately. + FloatRect layerBounds = compositedBounds(); + FloatRect reflectionLayerBounds = reflectionBacking->compositedBounds(); + reflectionBacking->graphicsLayer()->setReplicatedLayerPosition(FloatPoint() + (layerBounds.location() - reflectionLayerBounds.location())); + } + + m_graphicsLayer->setContentsRect(contentsBox()); + updateDrawsContent(); + updateAfterWidgetResize(); +} + +void RenderLayerBacking::updateInternalHierarchy() +{ + // m_foregroundLayer has to be inserted in the correct order with child layers, + // so it's not inserted here. + if (m_ancestorClippingLayer) { + m_ancestorClippingLayer->removeAllChildren(); + m_graphicsLayer->removeFromParent(); + m_ancestorClippingLayer->addChild(m_graphicsLayer.get()); + } + + if (m_clippingLayer) { + m_clippingLayer->removeFromParent(); + m_graphicsLayer->addChild(m_clippingLayer.get()); + } +} + +void RenderLayerBacking::updateDrawsContent() +{ + m_graphicsLayer->setDrawsContent(containsPaintedContent()); +} + +// Return true if the layers changed. +bool RenderLayerBacking::updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip) +{ + bool layersChanged = false; + + if (needsAncestorClip) { + if (!m_ancestorClippingLayer) { + m_ancestorClippingLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_ancestorClippingLayer->setName("Ancestor clipping Layer"); +#endif + m_ancestorClippingLayer->setMasksToBounds(true); + layersChanged = true; + } + } else if (m_ancestorClippingLayer) { + m_ancestorClippingLayer->removeFromParent(); + m_ancestorClippingLayer = 0; + layersChanged = true; + } + + if (needsDescendantClip) { + if (!m_clippingLayer) { + m_clippingLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_clippingLayer->setName("Child clipping Layer"); +#endif + m_clippingLayer->setMasksToBounds(true); + layersChanged = true; + } + } else if (m_clippingLayer) { + m_clippingLayer->removeFromParent(); + m_clippingLayer = 0; + layersChanged = true; + } + + if (layersChanged) + updateInternalHierarchy(); + + return layersChanged; +} + +bool RenderLayerBacking::updateForegroundLayer(bool needsForegroundLayer) +{ + bool layerChanged = false; + if (needsForegroundLayer) { + if (!m_foregroundLayer) { + m_foregroundLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_foregroundLayer->setName(nameForLayer() + " (foreground)"); +#endif + m_foregroundLayer->setDrawsContent(true); + m_foregroundLayer->setPaintingPhase(GraphicsLayerPaintForeground); + layerChanged = true; + } + } else if (m_foregroundLayer) { + m_foregroundLayer->removeFromParent(); + m_foregroundLayer = 0; + layerChanged = true; + } + + if (layerChanged) + m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); + + return layerChanged; +} + +bool RenderLayerBacking::updateMaskLayer(bool needsMaskLayer) +{ + bool layerChanged = false; + if (needsMaskLayer) { + if (!m_maskLayer) { + m_maskLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_maskLayer->setName("Mask"); +#endif + m_maskLayer->setDrawsContent(true); + m_maskLayer->setPaintingPhase(GraphicsLayerPaintMask); + layerChanged = true; + } + } else if (m_maskLayer) { + m_maskLayer = 0; + layerChanged = true; + } + + if (layerChanged) + m_graphicsLayer->setPaintingPhase(paintingPhaseForPrimaryLayer()); + + return layerChanged; +} + +GraphicsLayerPaintingPhase RenderLayerBacking::paintingPhaseForPrimaryLayer() const +{ + unsigned phase = GraphicsLayerPaintBackground; + if (!m_foregroundLayer) + phase |= GraphicsLayerPaintForeground; + if (!m_maskLayer) + phase |= GraphicsLayerPaintMask; + + return static_cast(phase); +} + +float RenderLayerBacking::compositingOpacity(float rendererOpacity) const +{ + float finalOpacity = rendererOpacity; + + for (RenderLayer* curr = m_owningLayer->parent(); curr; curr = curr->parent()) { + // We only care about parents that are stacking contexts. + // Recall that opacity creates stacking context. + if (!curr->isStackingContext()) + continue; + + // If we found a compositing layer, we want to compute opacity + // relative to it. So we can break here. + if (curr->isComposited()) + break; + + finalOpacity *= curr->renderer()->opacity(); + } + + return finalOpacity; +} + +static bool hasBorderOutlineOrShadow(const RenderStyle* style) +{ + return style->hasBorder() || style->hasBorderRadius() || style->hasOutline() || style->hasAppearance() || style->boxShadow(); +} + +static bool hasBoxDecorationsOrBackground(const RenderObject* renderer) +{ + return hasBorderOutlineOrShadow(renderer->style()) || renderer->hasBackground(); +} + +static bool hasBoxDecorationsOrBackgroundImage(const RenderStyle* style) +{ + return hasBorderOutlineOrShadow(style) || style->hasBackgroundImage(); +} + +bool RenderLayerBacking::rendererHasBackground() const +{ + // FIXME: share more code here + if (renderer()->node() && renderer()->node()->isDocumentNode()) { + RenderObject* htmlObject = renderer()->firstChild(); + if (!htmlObject) + return false; + + if (htmlObject->hasBackground()) + return true; + + RenderObject* bodyObject = htmlObject->firstChild(); + if (!bodyObject) + return false; + + return bodyObject->hasBackground(); + } + + return renderer()->hasBackground(); +} + +const Color RenderLayerBacking::rendererBackgroundColor() const +{ + // FIXME: share more code here + if (renderer()->node() && renderer()->node()->isDocumentNode()) { + RenderObject* htmlObject = renderer()->firstChild(); + if (htmlObject->hasBackground()) + return htmlObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); + + RenderObject* bodyObject = htmlObject->firstChild(); + return bodyObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); + } + + return renderer()->style()->visitedDependentColor(CSSPropertyBackgroundColor); +} + +// A "simple container layer" is a RenderLayer which has no visible content to render. +// It may have no children, or all its children may be themselves composited. +// This is a useful optimization, because it allows us to avoid allocating backing store. +bool RenderLayerBacking::isSimpleContainerCompositingLayer() const +{ + RenderObject* renderObject = renderer(); + if (renderObject->isReplaced() || // replaced objects are not containers + renderObject->hasMask()) // masks require special treatment + return false; + + RenderStyle* style = renderObject->style(); + + // Reject anything that has a border, a border-radius or outline, + // or any background (color or image). + // FIXME: we could optimize layers for simple backgrounds. + if (hasBoxDecorationsOrBackground(renderObject)) + return false; + + if (m_owningLayer->hasOverflowControls()) + return false; + + // If we have got this far and the renderer has no children, then we're ok. + if (!renderObject->firstChild()) + return true; + + if (renderObject->node() && renderObject->node()->isDocumentNode()) { + // Look to see if the root object has a non-simple backgound + RenderObject* rootObject = renderObject->document()->documentElement()->renderer(); + if (!rootObject) + return false; + + style = rootObject->style(); + + // Reject anything that has a border, a border-radius or outline, + // or is not a simple background (no background, or solid color). + if (hasBoxDecorationsOrBackgroundImage(style)) + return false; + + // Now look at the body's renderer. + HTMLElement* body = renderObject->document()->body(); + RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; + if (!bodyObject) + return false; + + style = bodyObject->style(); + + if (hasBoxDecorationsOrBackgroundImage(style)) + return false; + + // Check to see if all the body's children are compositing layers. + if (hasNonCompositingDescendants()) + return false; + + return true; + } + + // Check to see if all the renderer's children are compositing layers. + if (hasNonCompositingDescendants()) + return false; + + return true; +} + +// Conservative test for having no rendered children. +bool RenderLayerBacking::hasNonCompositingDescendants() const +{ + // Some HTML can cause whitespace text nodes to have renderers, like: + //

    + // + //
    + // so test for 0x0 RenderTexts here + for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { + if (!child->hasLayer()) { + if (child->isRenderInline() || !child->isBox()) + return true; + + if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0) + return true; + } + } + + if (m_owningLayer->isStackingContext()) { + // Use the m_hasCompositingDescendant bit to optimize? + if (Vector* negZOrderList = m_owningLayer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + if (!curLayer->isComposited()) + return true; + } + } + + if (Vector* posZOrderList = m_owningLayer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + if (!curLayer->isComposited()) + return true; + } + } + } + + if (Vector* normalFlowList = m_owningLayer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + if (!curLayer->isComposited()) + return true; + } + } + + return false; +} + +bool RenderLayerBacking::containsPaintedContent() const +{ + if (isSimpleContainerCompositingLayer() || paintingGoesToWindow() || m_artificiallyInflatedBounds || m_owningLayer->isReflection()) + return false; + + if (isDirectlyCompositedImage()) + return false; + + // FIXME: we could optimize cases where the image, video or canvas is known to fill the border box entirely, + // and set background color on the layer in that case, instead of allocating backing store and painting. +#if ENABLE(VIDEO) + if (renderer()->isVideo() && toRenderVideo(renderer())->shouldDisplayVideo()) + return hasBoxDecorationsOrBackground(renderer()); +#endif +#if PLATFORM(MAC) && PLATFORM(CA) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) +#elif ENABLE(3D_CANVAS) || ENABLE(ACCELERATED_2D_CANVAS) + if (isAcceleratedCanvas(renderer())) + return hasBoxDecorationsOrBackground(renderer()); +#endif + + return true; +} + +// An image can be directly compositing if it's the sole content of the layer, and has no box decorations +// that require painting. Direct compositing saves backing store. +bool RenderLayerBacking::isDirectlyCompositedImage() const +{ + RenderObject* renderObject = renderer(); + + if (!renderObject->isImage() || hasBoxDecorationsOrBackground(renderObject) || renderObject->hasClip()) + return false; + + RenderImage* imageRenderer = toRenderImage(renderObject); + if (CachedImage* cachedImage = imageRenderer->cachedImage()) { + if (cachedImage->hasImage()) + return cachedImage->image()->isBitmapImage(); + } + + return false; +} + +void RenderLayerBacking::contentChanged(RenderLayer::ContentChangeType changeType) +{ + if ((changeType == RenderLayer::ImageChanged) && isDirectlyCompositedImage()) { + updateImageContents(); + return; + } + + if ((changeType == RenderLayer::MaskImageChanged) && m_maskLayer) { + // The composited layer bounds relies on box->maskClipRect(), which changes + // when the mask image becomes available. + bool isUpdateRoot = true; + updateAfterLayout(CompositingChildren, isUpdateRoot); + } + +#if ENABLE(3D_CANVAS) || ENABLE(ACCELERATED_2D_CANVAS) + if ((changeType == RenderLayer::CanvasChanged) && isAcceleratedCanvas(renderer())) { + m_graphicsLayer->setContentsNeedsDisplay(); + return; + } +#endif +} + +void RenderLayerBacking::updateImageContents() +{ + ASSERT(renderer()->isImage()); + RenderImage* imageRenderer = toRenderImage(renderer()); + + CachedImage* cachedImage = imageRenderer->cachedImage(); + if (!cachedImage) + return; + + Image* image = cachedImage->image(); + if (!image) + return; + + // We have to wait until the image is fully loaded before setting it on the layer. + if (!cachedImage->isLoaded()) + return; + + // This is a no-op if the layer doesn't have an inner layer for the image. + m_graphicsLayer->setContentsToImage(image); + + // Image animation is "lazy", in that it automatically stops unless someone is drawing + // the image. So we have to kick the animation each time; this has the downside that the + // image will keep animating, even if its layer is not visible. + image->startAnimation(); +} + +FloatPoint3D RenderLayerBacking::computeTransformOrigin(const IntRect& borderBox) const +{ + RenderStyle* style = renderer()->style(); + + FloatPoint3D origin; + origin.setX(style->transformOriginX().calcFloatValue(borderBox.width())); + origin.setY(style->transformOriginY().calcFloatValue(borderBox.height())); + origin.setZ(style->transformOriginZ()); + + return origin; +} + +FloatPoint RenderLayerBacking::computePerspectiveOrigin(const IntRect& borderBox) const +{ + RenderStyle* style = renderer()->style(); + + float boxWidth = borderBox.width(); + float boxHeight = borderBox.height(); + + FloatPoint origin; + origin.setX(style->perspectiveOriginX().calcFloatValue(boxWidth)); + origin.setY(style->perspectiveOriginY().calcFloatValue(boxHeight)); + + return origin; +} + +// Return the offset from the top-left of this compositing layer at which the renderer's contents are painted. +IntSize RenderLayerBacking::contentOffsetInCompostingLayer() const +{ + return IntSize(-m_compositedBounds.x(), -m_compositedBounds.y()); +} + +IntRect RenderLayerBacking::contentsBox() const +{ + if (!renderer()->isBox()) + return IntRect(); + + IntRect contentsRect; +#if ENABLE(VIDEO) + if (renderer()->isVideo()) { + RenderVideo* videoRenderer = toRenderVideo(renderer()); + contentsRect = videoRenderer->videoBox(); + } else +#endif + contentsRect = toRenderBox(renderer())->contentBoxRect(); + + IntSize contentOffset = contentOffsetInCompostingLayer(); + contentsRect.move(contentOffset); + return contentsRect; +} + +// Map the given point from coordinates in the GraphicsLayer to RenderLayer coordinates. +FloatPoint RenderLayerBacking::graphicsLayerToContentsCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) +{ + return point + FloatSize(graphicsLayer->offsetFromRenderer()); +} + +// Map the given point from coordinates in the RenderLayer to GraphicsLayer coordinates. +FloatPoint RenderLayerBacking::contentsToGraphicsLayerCoordinates(const GraphicsLayer* graphicsLayer, const FloatPoint& point) +{ + return point - FloatSize(graphicsLayer->offsetFromRenderer()); +} + +bool RenderLayerBacking::paintingGoesToWindow() const +{ + if (m_owningLayer->isRootLayer()) + return !m_owningLayer->hasTransform() && (compositor()->rootLayerAttachment() != RenderLayerCompositor::RootLayerAttachedViaEnclosingIframe); + + return false; +} + +void RenderLayerBacking::setContentsNeedDisplay() +{ + if (m_graphicsLayer && m_graphicsLayer->drawsContent()) + m_graphicsLayer->setNeedsDisplay(); + + if (m_foregroundLayer && m_foregroundLayer->drawsContent()) + m_foregroundLayer->setNeedsDisplay(); + + if (m_maskLayer && m_maskLayer->drawsContent()) + m_maskLayer->setNeedsDisplay(); +} + +// r is in the coordinate space of the layer's render object +void RenderLayerBacking::setContentsNeedDisplayInRect(const IntRect& r) +{ + if (m_graphicsLayer && m_graphicsLayer->drawsContent()) { + FloatPoint dirtyOrigin = contentsToGraphicsLayerCoordinates(m_graphicsLayer.get(), FloatPoint(r.x(), r.y())); + FloatRect dirtyRect(dirtyOrigin, r.size()); + FloatRect bounds(FloatPoint(), m_graphicsLayer->size()); + if (bounds.intersects(dirtyRect)) + m_graphicsLayer->setNeedsDisplayInRect(dirtyRect); + } + + if (m_foregroundLayer && m_foregroundLayer->drawsContent()) { + // FIXME: do incremental repaint + m_foregroundLayer->setNeedsDisplay(); + } + + if (m_maskLayer && m_maskLayer->drawsContent()) { + // FIXME: do incremental repaint + m_maskLayer->setNeedsDisplay(); + } +} + +static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->save(); + p->clip(clipRect); +} + +static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->restore(); +} + +// Share this with RenderLayer::paintLayer, which would have to be educated about GraphicsLayerPaintingPhase? +void RenderLayerBacking::paintIntoLayer(RenderLayer* rootLayer, GraphicsContext* context, + const IntRect& paintDirtyRect, // in the coords of rootLayer + PaintBehavior paintBehavior, GraphicsLayerPaintingPhase paintingPhase, + RenderObject* paintingRoot) +{ + if (paintingGoesToWindow()) { + ASSERT_NOT_REACHED(); + return; + } + + m_owningLayer->updateLayerListsIfNeeded(); + + // Calculate the clip rects we should use. + IntRect layerBounds, damageRect, clipRectToApply, outlineRect; + m_owningLayer->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); + + int x = layerBounds.x(); // layerBounds is computed relative to rootLayer + int y = layerBounds.y(); + int tx = x - m_owningLayer->renderBoxX(); + int ty = y - m_owningLayer->renderBoxY(); + + // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which + // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). + // Else, our renderer tree may or may not contain the painting root, so we pass that root along + // so it will be tested against as we decend through the renderers. + RenderObject *paintingRootForRenderer = 0; + if (paintingRoot && !renderer()->isDescendantOf(paintingRoot)) + paintingRootForRenderer = paintingRoot; + + bool shouldPaint = (m_owningLayer->hasVisibleContent() || m_owningLayer->hasVisibleDescendant()) && m_owningLayer->isSelfPaintingLayer(); + + if (shouldPaint && (paintingPhase & GraphicsLayerPaintBackground)) { + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + setClip(context, paintDirtyRect, damageRect); + + PaintInfo info(context, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); + renderer()->paint(info, tx, ty); + + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + m_owningLayer->paintOverflowControls(context, x, y, damageRect); + + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + // Paint the outline as part of the background phase in order for the + // outline to not be a part of the scrollable content. + if (!outlineRect.isEmpty()) { + // Paint our own outline + PaintInfo paintInfo(context, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); + setClip(context, paintDirtyRect, outlineRect); + renderer()->paint(paintInfo, tx, ty); + restoreClip(context, paintDirtyRect, outlineRect); + } +#endif + + // Now walk the sorted list of children with negative z-indices. Only RenderLayers without compositing layers will paint. + m_owningLayer->paintList(m_owningLayer->negZOrderList(), rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot, 0, 0); + } + + bool forceBlackText = paintBehavior & PaintBehaviorForceBlackText; + bool selectionOnly = paintBehavior & PaintBehaviorSelectionOnly; + + if (shouldPaint && (paintingPhase & GraphicsLayerPaintForeground)) { + // Set up the clip used when painting our children. + setClip(context, paintDirtyRect, clipRectToApply); + PaintInfo paintInfo(context, clipRectToApply, + selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, + forceBlackText, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + if (!selectionOnly) { + paintInfo.phase = PaintPhaseFloat; + renderer()->paint(paintInfo, tx, ty); + + paintInfo.phase = PaintPhaseForeground; + renderer()->paint(paintInfo, tx, ty); + + paintInfo.phase = PaintPhaseChildOutlines; + renderer()->paint(paintInfo, tx, ty); + } + + // Now restore our clip. + restoreClip(context, paintDirtyRect, clipRectToApply); + +#if !ENABLE(ANDROID_OVERFLOW_SCROLL) + // Do not paint the outline as part of the foreground since it will + // appear inside the scrollable content. + if (!outlineRect.isEmpty()) { + // Paint our own outline + PaintInfo paintInfo(context, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); + setClip(context, paintDirtyRect, outlineRect); + renderer()->paint(paintInfo, tx, ty); + restoreClip(context, paintDirtyRect, outlineRect); + } +#endif + + // Paint any child layers that have overflow. + m_owningLayer->paintList(m_owningLayer->normalFlowList(), rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot, 0, 0); + + // Now walk the sorted list of children with positive z-indices. + m_owningLayer->paintList(m_owningLayer->posZOrderList(), rootLayer, context, paintDirtyRect, paintBehavior, paintingRoot, 0, 0); + } + + if (shouldPaint && (paintingPhase & GraphicsLayerPaintMask)) { + if (renderer()->hasMask() && !selectionOnly && !damageRect.isEmpty()) { + setClip(context, paintDirtyRect, damageRect); + + // Paint the mask. + PaintInfo paintInfo(context, damageRect, PaintPhaseMask, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Restore the clip. + restoreClip(context, paintDirtyRect, damageRect); + } + } + + ASSERT(!m_owningLayer->m_usedTransparency); +} + +// Up-call from compositing layer drawing callback. +void RenderLayerBacking::paintContents(const GraphicsLayer*, GraphicsContext& context, GraphicsLayerPaintingPhase paintingPhase, const IntRect& clip) +{ + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willPaint(m_owningLayer->renderer()->frame(), clip); + + // We have to use the same root as for hit testing, because both methods + // can compute and cache clipRects. + IntRect enclosingBBox = compositedBounds(); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + // If we encounter a scrollable layer, layers inside the scrollable layer + // will need their entire content recorded. + if (m_owningLayer->hasOverflowParent()) + enclosingBBox.setSize(clip.size()); +#endif + + IntRect clipRect(clip); + + // Set up the coordinate space to be in the layer's rendering coordinates. + context.translate(-enclosingBBox.x(), -enclosingBBox.y()); + + // Offset the clip. + clipRect.move(enclosingBBox.x(), enclosingBBox.y()); + + // The dirtyRect is in the coords of the painting root. + IntRect dirtyRect = enclosingBBox; + dirtyRect.intersect(clipRect); + + paintIntoLayer(m_owningLayer, &context, dirtyRect, PaintBehaviorNormal, paintingPhase, renderer()); + + InspectorInstrumentation::didPaint(cookie); +} + +bool RenderLayerBacking::showDebugBorders() const +{ + return compositor() ? compositor()->compositorShowDebugBorders() : false; +} + +bool RenderLayerBacking::showRepaintCounter() const +{ + return compositor() ? compositor()->compositorShowRepaintCounter() : false; +} + +bool RenderLayerBacking::startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes) +{ + bool hasOpacity = keyframes.containsProperty(CSSPropertyOpacity); + bool hasTransform = renderer()->isBox() && keyframes.containsProperty(CSSPropertyWebkitTransform); + + if (!hasOpacity && !hasTransform) + return false; + + KeyframeValueList transformVector(AnimatedPropertyWebkitTransform); + KeyframeValueList opacityVector(AnimatedPropertyOpacity); + + size_t numKeyframes = keyframes.size(); + for (size_t i = 0; i < numKeyframes; ++i) { + const KeyframeValue& currentKeyframe = keyframes[i]; + const RenderStyle* keyframeStyle = currentKeyframe.style(); + float key = currentKeyframe.key(); + + if (!keyframeStyle) + continue; + + // Get timing function. + RefPtr tf = keyframeStyle->hasAnimations() ? (*keyframeStyle->animations()).animation(0)->timingFunction() : 0; + + bool isFirstOrLastKeyframe = key == 0 || key == 1; + if ((hasTransform && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyWebkitTransform)) + transformVector.insert(new TransformAnimationValue(key, &(keyframeStyle->transform()), tf)); + + if ((hasOpacity && isFirstOrLastKeyframe) || currentKeyframe.containsProperty(CSSPropertyOpacity)) + opacityVector.insert(new FloatAnimationValue(key, keyframeStyle->opacity(), tf)); + } + + bool didAnimateTransform = false; + bool didAnimateOpacity = false; + + if (hasTransform && m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), anim, keyframes.animationName(), timeOffset)) { + didAnimateTransform = true; + compositor()->didStartAcceleratedAnimation(CSSPropertyWebkitTransform); + } + + if (hasOpacity && m_graphicsLayer->addAnimation(opacityVector, IntSize(), anim, keyframes.animationName(), timeOffset)) { + didAnimateOpacity = true; + compositor()->didStartAcceleratedAnimation(CSSPropertyOpacity); + } + + return didAnimateTransform || didAnimateOpacity; +} + +void RenderLayerBacking::animationPaused(double timeOffset, const String& animationName) +{ + m_graphicsLayer->pauseAnimation(animationName, timeOffset); +} + +void RenderLayerBacking::animationFinished(const String& animationName) +{ + m_graphicsLayer->removeAnimation(animationName); +} + +bool RenderLayerBacking::startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle) +{ + bool didAnimateOpacity = false; + bool didAnimateTransform = false; + ASSERT(property != cAnimateAll); + + if (property == (int)CSSPropertyOpacity) { + const Animation* opacityAnim = toStyle->transitionForProperty(CSSPropertyOpacity); + if (opacityAnim && !opacityAnim->isEmptyOrZeroDuration()) { + KeyframeValueList opacityVector(AnimatedPropertyOpacity); + opacityVector.insert(new FloatAnimationValue(0, compositingOpacity(fromStyle->opacity()))); + opacityVector.insert(new FloatAnimationValue(1, compositingOpacity(toStyle->opacity()))); + // The boxSize param is only used for transform animations (which can only run on RenderBoxes), so we pass an empty size here. + if (m_graphicsLayer->addAnimation(opacityVector, IntSize(), opacityAnim, GraphicsLayer::animationNameForTransition(AnimatedPropertyOpacity), timeOffset)) { + // To ensure that the correct opacity is visible when the animation ends, also set the final opacity. + updateLayerOpacity(toStyle); + didAnimateOpacity = true; + } + } + } + + if (property == (int)CSSPropertyWebkitTransform && m_owningLayer->hasTransform()) { + const Animation* transformAnim = toStyle->transitionForProperty(CSSPropertyWebkitTransform); + if (transformAnim && !transformAnim->isEmptyOrZeroDuration()) { + KeyframeValueList transformVector(AnimatedPropertyWebkitTransform); + transformVector.insert(new TransformAnimationValue(0, &fromStyle->transform())); + transformVector.insert(new TransformAnimationValue(1, &toStyle->transform())); + if (m_graphicsLayer->addAnimation(transformVector, toRenderBox(renderer())->borderBoxRect().size(), transformAnim, GraphicsLayer::animationNameForTransition(AnimatedPropertyWebkitTransform), timeOffset)) { + // To ensure that the correct transform is visible when the animation ends, also set the final opacity. + updateLayerTransform(toStyle); + didAnimateTransform = true; + } + } + } + + if (didAnimateOpacity) + compositor()->didStartAcceleratedAnimation(CSSPropertyOpacity); + + if (didAnimateTransform) + compositor()->didStartAcceleratedAnimation(CSSPropertyWebkitTransform); + + return didAnimateOpacity || didAnimateTransform; +} + +void RenderLayerBacking::transitionPaused(double timeOffset, int property) +{ + AnimatedPropertyID animatedProperty = cssToGraphicsLayerProperty(property); + if (animatedProperty != AnimatedPropertyInvalid) + m_graphicsLayer->pauseAnimation(GraphicsLayer::animationNameForTransition(animatedProperty), timeOffset); +} + +void RenderLayerBacking::transitionFinished(int property) +{ + AnimatedPropertyID animatedProperty = cssToGraphicsLayerProperty(property); + if (animatedProperty != AnimatedPropertyInvalid) + m_graphicsLayer->removeAnimation(GraphicsLayer::animationNameForTransition(animatedProperty)); +} + +void RenderLayerBacking::notifyAnimationStarted(const GraphicsLayer*, double time) +{ + renderer()->animation()->notifyAnimationStarted(renderer(), time); +} + +void RenderLayerBacking::notifySyncRequired(const GraphicsLayer*) +{ + if (!renderer()->documentBeingDestroyed()) + compositor()->scheduleSync(); +} + +// This is used for the 'freeze' API, for testing only. +void RenderLayerBacking::suspendAnimations(double time) +{ + m_graphicsLayer->suspendAnimations(time); +} + +void RenderLayerBacking::resumeAnimations() +{ + m_graphicsLayer->resumeAnimations(); +} + +IntRect RenderLayerBacking::compositedBounds() const +{ + return m_compositedBounds; +} + +void RenderLayerBacking::setCompositedBounds(const IntRect& bounds) +{ + m_compositedBounds = bounds; + +} +int RenderLayerBacking::graphicsLayerToCSSProperty(AnimatedPropertyID property) +{ + int cssProperty = CSSPropertyInvalid; + switch (property) { + case AnimatedPropertyWebkitTransform: + cssProperty = CSSPropertyWebkitTransform; + break; + case AnimatedPropertyOpacity: + cssProperty = CSSPropertyOpacity; + break; + case AnimatedPropertyBackgroundColor: + cssProperty = CSSPropertyBackgroundColor; + break; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + return cssProperty; +} + +AnimatedPropertyID RenderLayerBacking::cssToGraphicsLayerProperty(int cssProperty) +{ + switch (cssProperty) { + case CSSPropertyWebkitTransform: + return AnimatedPropertyWebkitTransform; + case CSSPropertyOpacity: + return AnimatedPropertyOpacity; + case CSSPropertyBackgroundColor: + return AnimatedPropertyBackgroundColor; + // It's fine if we see other css properties here; they are just not accelerated. + } + return AnimatedPropertyInvalid; +} + +#ifndef NDEBUG +String RenderLayerBacking::nameForLayer() const +{ + String name = renderer()->renderName(); + if (Node* node = renderer()->node()) { + if (node->isElementNode()) + name += " " + static_cast(node)->tagName(); + if (node->hasID()) + name += " \'" + static_cast(node)->getIdAttribute() + "\'"; + } + + if (m_owningLayer->isReflection()) + name += " (reflection)"; + + return name; +} +#endif + +CompositingLayerType RenderLayerBacking::compositingLayerType() const +{ + if (m_graphicsLayer->hasContentsLayer()) + return MediaCompositingLayer; + + if (m_graphicsLayer->drawsContent()) + return m_graphicsLayer->usingTiledLayer() ? TiledCompositingLayer : NormalCompositingLayer; + + return ContainerCompositingLayer; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/rendering/RenderLayerBacking.h b/Source/WebCore/rendering/RenderLayerBacking.h new file mode 100644 index 0000000..c5489f3 --- /dev/null +++ b/Source/WebCore/rendering/RenderLayerBacking.h @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderLayerBacking_h +#define RenderLayerBacking_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "FloatPoint.h" +#include "FloatPoint3D.h" +#include "GraphicsLayer.h" +#include "GraphicsLayerClient.h" +#include "RenderLayer.h" +#include "TransformationMatrix.h" + +namespace WebCore { + +class KeyframeList; +class RenderLayerCompositor; + +enum CompositingLayerType { + NormalCompositingLayer, // non-tiled layer with backing store + TiledCompositingLayer, // tiled layer (always has backing store) + MediaCompositingLayer, // layer that contains an image, video, webGL or plugin + ContainerCompositingLayer // layer with no backing store +}; + +// RenderLayerBacking controls the compositing behavior for a single RenderLayer. +// It holds the various GraphicsLayers, and makes decisions about intra-layer rendering +// optimizations. +// +// There is one RenderLayerBacking for each RenderLayer that is composited. + +class RenderLayerBacking : public GraphicsLayerClient, public Noncopyable { +public: + RenderLayerBacking(RenderLayer*); + ~RenderLayerBacking(); + + RenderLayer* owningLayer() const { return m_owningLayer; } + + enum UpdateDepth { CompositingChildren, AllDescendants }; + void updateAfterLayout(UpdateDepth, bool isUpdateRoot); + + // Returns true if layer configuration changed. + bool updateGraphicsLayerConfiguration(); + // Update graphics layer position and bounds. + void updateGraphicsLayerGeometry(); // make private + // Update contents and clipping structure. + void updateInternalHierarchy(); // make private + void updateDrawsContent(); + + GraphicsLayer* graphicsLayer() const { return m_graphicsLayer.get(); } + + // Layer to clip children + bool hasClippingLayer() const { return m_clippingLayer != 0; } + GraphicsLayer* clippingLayer() const { return m_clippingLayer.get(); } + + // Layer to get clipped by ancestor + bool hasAncestorClippingLayer() const { return m_ancestorClippingLayer != 0; } + GraphicsLayer* ancestorClippingLayer() const { return m_ancestorClippingLayer.get(); } + + bool hasContentsLayer() const { return m_foregroundLayer != 0; } + GraphicsLayer* foregroundLayer() const { return m_foregroundLayer.get(); } + + bool hasMaskLayer() const { return m_maskLayer != 0; } + + GraphicsLayer* parentForSublayers() const { return m_clippingLayer ? m_clippingLayer.get() : m_graphicsLayer.get(); } + GraphicsLayer* childForSuperlayers() const { return m_ancestorClippingLayer ? m_ancestorClippingLayer.get() : m_graphicsLayer.get(); } + + // RenderLayers with backing normally short-circuit paintLayer() because + // their content is rendered via callbacks from GraphicsLayer. However, the document + // layer is special, because it has a GraphicsLayer to act as a container for the GraphicsLayers + // for descendants, but its contents usually render into the window (in which case this returns true). + // This returns false for other layers, and when the document layer actually needs to paint into its backing store + // for some reason. + bool paintingGoesToWindow() const; + + void setContentsNeedDisplay(); + // r is in the coordinate space of the layer's render object + void setContentsNeedDisplayInRect(const IntRect& r); + + // Notification from the renderer that its content changed. + void contentChanged(RenderLayer::ContentChangeType); + + // Interface to start, finish, suspend and resume animations and transitions + bool startTransition(double timeOffset, int property, const RenderStyle* fromStyle, const RenderStyle* toStyle); + void transitionPaused(double timeOffset, int property); + void transitionFinished(int property); + + bool startAnimation(double timeOffset, const Animation* anim, const KeyframeList& keyframes); + void animationPaused(double timeOffset, const String& name); + void animationFinished(const String& name); + + void suspendAnimations(double time = 0); + void resumeAnimations(); + + IntRect compositedBounds() const; + void setCompositedBounds(const IntRect&); + void updateCompositedBounds(); + + void updateAfterWidgetResize(); + + FloatPoint graphicsLayerToContentsCoordinates(const GraphicsLayer*, const FloatPoint&); + FloatPoint contentsToGraphicsLayerCoordinates(const GraphicsLayer*, const FloatPoint&); + + // GraphicsLayerClient interface + virtual void notifyAnimationStarted(const GraphicsLayer*, double startTime); + virtual void notifySyncRequired(const GraphicsLayer*); + + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& clip); + + virtual bool showDebugBorders() const; + virtual bool showRepaintCounter() const; + + IntRect contentsBox() const; + + // For informative purposes only. + CompositingLayerType compositingLayerType() const; + +private: + void createGraphicsLayer(); + void destroyGraphicsLayer(); + + RenderBoxModelObject* renderer() const { return m_owningLayer->renderer(); } + RenderLayerCompositor* compositor() const { return m_owningLayer->compositor(); } + + bool updateClippingLayers(bool needsAncestorClip, bool needsDescendantClip); + bool updateForegroundLayer(bool needsForegroundLayer); + bool updateMaskLayer(bool needsMaskLayer); + + GraphicsLayerPaintingPhase paintingPhaseForPrimaryLayer() const; + + IntSize contentOffsetInCompostingLayer() const; + // Result is transform origin in pixels. + FloatPoint3D computeTransformOrigin(const IntRect& borderBox) const; + // Result is perspective origin in pixels. + FloatPoint computePerspectiveOrigin(const IntRect& borderBox) const; + + void updateLayerOpacity(const RenderStyle*); + void updateLayerTransform(const RenderStyle*); + + // Return the opacity value that this layer should use for compositing. + float compositingOpacity(float rendererOpacity) const; + + // Returns true if this compositing layer has no visible content. + bool isSimpleContainerCompositingLayer() const; + // Returns true if this layer has content that needs to be rendered by painting into the backing store. + bool containsPaintedContent() const; + // Returns true if the RenderLayer just contains an image that we can composite directly. + bool isDirectlyCompositedImage() const; + void updateImageContents(); + + bool rendererHasBackground() const; + const Color rendererBackgroundColor() const; + + bool hasNonCompositingDescendants() const; + + void paintIntoLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, + PaintBehavior paintBehavior, GraphicsLayerPaintingPhase, RenderObject* paintingRoot); + + static int graphicsLayerToCSSProperty(AnimatedPropertyID); + static AnimatedPropertyID cssToGraphicsLayerProperty(int); + +#ifndef NDEBUG + String nameForLayer() const; +#endif + +private: + RenderLayer* m_owningLayer; + + OwnPtr m_ancestorClippingLayer; // only used if we are clipped by an ancestor which is not a stacking context + OwnPtr m_graphicsLayer; + OwnPtr m_foregroundLayer; // only used in cases where we need to draw the foreground separately + OwnPtr m_clippingLayer; // only used if we have clipping on a stacking context, with compositing children + OwnPtr m_maskLayer; // only used if we have a mask + + IntRect m_compositedBounds; + + bool m_artificiallyInflatedBounds; // bounds had to be made non-zero to make transform-origin work +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // RenderLayerBacking_h diff --git a/Source/WebCore/rendering/RenderLayerCompositor.cpp b/Source/WebCore/rendering/RenderLayerCompositor.cpp new file mode 100644 index 0000000..960aa37 --- /dev/null +++ b/Source/WebCore/rendering/RenderLayerCompositor.cpp @@ -0,0 +1,1668 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayerCompositor.h" + +#include "AnimationController.h" +#include "CanvasRenderingContext.h" +#include "CSSPropertyNames.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsLayer.h" +#include "HTMLCanvasElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "NodeList.h" +#include "Page.h" +#include "RenderApplet.h" +#include "RenderEmbeddedObject.h" +#include "RenderFullScreen.h" +#include "RenderIFrame.h" +#include "RenderLayerBacking.h" +#include "RenderReplica.h" +#include "RenderVideo.h" +#include "RenderView.h" +#include "Settings.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" +#endif + +#if PROFILE_LAYER_REBUILD +#include +#endif + +#ifndef NDEBUG +#include "RenderTreeAsText.h" +#endif + +#if ENABLE(3D_RENDERING) +// This symbol is used to determine from a script whether 3D rendering is enabled (via 'nm'). +bool WebCoreHas3DRendering = true; +#endif + +namespace WebCore { + +using namespace HTMLNames; + +struct CompositingState { + CompositingState(RenderLayer* compAncestor) + : m_compositingAncestor(compAncestor) + , m_subtreeIsCompositing(false) +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + , m_fixedSibling(false) +#endif +#ifndef NDEBUG + , m_depth(0) +#endif + { + } + + RenderLayer* m_compositingAncestor; + bool m_subtreeIsCompositing; +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + bool m_fixedSibling; +#endif +#ifndef NDEBUG + int m_depth; +#endif +}; + +RenderLayerCompositor::RenderLayerCompositor(RenderView* renderView) + : m_renderView(renderView) + , m_rootPlatformLayer(0) + , m_updateCompositingLayersTimer(this, &RenderLayerCompositor::updateCompositingLayersTimerFired) + , m_hasAcceleratedCompositing(true) + , m_compositingTriggers(static_cast(ChromeClient::AllTriggers)) + , m_showDebugBorders(false) + , m_showRepaintCounter(false) + , m_compositingConsultsOverlap(true) + , m_compositingDependsOnGeometry(false) + , m_compositing(false) + , m_compositingLayersNeedRebuild(false) + , m_rootLayerAttachment(RootLayerUnattached) +#if PROFILE_LAYER_REBUILD + , m_rootLayerUpdateCount(0) +#endif // PROFILE_LAYER_REBUILD +{ +} + +RenderLayerCompositor::~RenderLayerCompositor() +{ + ASSERT(m_rootLayerAttachment == RootLayerUnattached); +} + +void RenderLayerCompositor::enableCompositingMode(bool enable /* = true */) +{ + if (enable != m_compositing) { + m_compositing = enable; + + if (m_compositing) { + ensureRootPlatformLayer(); + notifyIFramesOfCompositingChange(); + } else + destroyRootPlatformLayer(); + } +} + +void RenderLayerCompositor::cacheAcceleratedCompositingFlags() +{ + bool hasAcceleratedCompositing = false; + bool showDebugBorders = false; + bool showRepaintCounter = false; + + if (Settings* settings = m_renderView->document()->settings()) { + hasAcceleratedCompositing = settings->acceleratedCompositingEnabled(); + showDebugBorders = settings->showDebugBorders(); + showRepaintCounter = settings->showRepaintCounter(); + } + + // We allow the chrome to override the settings, in case the page is rendered + // on a chrome that doesn't allow accelerated compositing. + if (hasAcceleratedCompositing) { + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (page) { + ChromeClient* chromeClient = page->chrome()->client(); + m_compositingTriggers = chromeClient->allowedCompositingTriggers(); + hasAcceleratedCompositing = m_compositingTriggers; + } + } + + if (hasAcceleratedCompositing != m_hasAcceleratedCompositing || showDebugBorders != m_showDebugBorders || showRepaintCounter != m_showRepaintCounter) + setCompositingLayersNeedRebuild(); + + m_hasAcceleratedCompositing = hasAcceleratedCompositing; + m_showDebugBorders = showDebugBorders; + m_showRepaintCounter = showRepaintCounter; +} + +bool RenderLayerCompositor::canRender3DTransforms() const +{ + return hasAcceleratedCompositing() && (m_compositingTriggers & ChromeClient::ThreeDTransformTrigger); +} + +void RenderLayerCompositor::setCompositingLayersNeedRebuild(bool needRebuild) +{ + if (inCompositingMode()) + m_compositingLayersNeedRebuild = needRebuild; +} + +void RenderLayerCompositor::scheduleSync() +{ + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + page->chrome()->client()->scheduleCompositingLayerSync(); +} + +void RenderLayerCompositor::scheduleCompositingLayerUpdate() +{ + if (!m_updateCompositingLayersTimer.isActive()) + m_updateCompositingLayersTimer.startOneShot(0); +} + +bool RenderLayerCompositor::compositingLayerUpdatePending() const +{ + return m_updateCompositingLayersTimer.isActive(); +} + +void RenderLayerCompositor::updateCompositingLayersTimerFired(Timer*) +{ + updateCompositingLayers(); +} + +void RenderLayerCompositor::updateCompositingLayers(CompositingUpdateType updateType, RenderLayer* updateRoot) +{ + m_updateCompositingLayersTimer.stop(); + + if (!m_compositingDependsOnGeometry && !m_compositing) + return; + + bool checkForHierarchyUpdate = m_compositingDependsOnGeometry; + bool needGeometryUpdate = false; + + switch (updateType) { + case CompositingUpdateAfterLayoutOrStyleChange: + case CompositingUpdateOnPaitingOrHitTest: + checkForHierarchyUpdate = true; + break; + case CompositingUpdateOnScroll: + if (m_compositingConsultsOverlap) + checkForHierarchyUpdate = true; // Overlap can change with scrolling, so need to check for hierarchy updates. + + needGeometryUpdate = true; + break; + } + + if (!checkForHierarchyUpdate && !needGeometryUpdate) + return; + + bool needHierarchyUpdate = m_compositingLayersNeedRebuild; + if (!updateRoot || m_compositingConsultsOverlap) { + // Only clear the flag if we're updating the entire hierarchy. + m_compositingLayersNeedRebuild = false; + updateRoot = rootRenderLayer(); + } + +#if PROFILE_LAYER_REBUILD + ++m_rootLayerUpdateCount; + + double startTime = WTF::currentTime(); +#endif + + if (checkForHierarchyUpdate) { + // Go through the layers in presentation order, so that we can compute which RenderLayers need compositing layers. + // FIXME: we could maybe do this and the hierarchy udpate in one pass, but the parenting logic would be more complex. + CompositingState compState(updateRoot); + bool layersChanged = false; + if (m_compositingConsultsOverlap) { + OverlapMap overlapTestRequestMap; + computeCompositingRequirements(updateRoot, &overlapTestRequestMap, compState, layersChanged); + } else + computeCompositingRequirements(updateRoot, 0, compState, layersChanged); + + needHierarchyUpdate |= layersChanged; + } + + if (needHierarchyUpdate) { + // Update the hierarchy of the compositing layers. + CompositingState compState(updateRoot); + Vector childList; + rebuildCompositingLayerTree(updateRoot, compState, childList); + + // Host the document layer in the RenderView's root layer. + if (updateRoot == rootRenderLayer()) { + if (childList.isEmpty()) + destroyRootPlatformLayer(); + else + m_rootPlatformLayer->setChildren(childList); + } + } else if (needGeometryUpdate) { + // We just need to do a geometry update. This is only used for position:fixed scrolling; + // most of the time, geometry is updated via RenderLayer::styleChanged(). + updateLayerTreeGeometry(updateRoot); + } + +#if PROFILE_LAYER_REBUILD + double endTime = WTF::currentTime(); + if (updateRoot == rootRenderLayer()) + fprintf(stderr, "Update %d: computeCompositingRequirements for the world took %fms\n", + m_rootLayerUpdateCount, 1000.0 * (endTime - startTime)); +#endif + ASSERT(updateRoot || !m_compositingLayersNeedRebuild); + + if (!hasAcceleratedCompositing()) + enableCompositingMode(false); +} + +bool RenderLayerCompositor::updateBacking(RenderLayer* layer, CompositingChangeRepaint shouldRepaint) +{ + bool layerChanged = false; + + if (needsToBeComposited(layer)) { + enableCompositingMode(); + + // 3D transforms turn off the testing of overlap. + if (requiresCompositingForTransform(layer->renderer())) + setCompositingConsultsOverlap(false); +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + // If we are a child of a scrollable layer, ignore the overlap from the + // scrollable layer as it can cause child layers to become composited + // siblings and will not scroll with the main content layer. + if (layer->hasOverflowParent()) + setCompositingConsultsOverlap(false); +#endif + + if (!layer->backing()) { + + // If we need to repaint, do so before making backing + if (shouldRepaint == CompositingChangeRepaintNow) + repaintOnCompositingChange(layer); + + layer->ensureBacking(); + +#if PLATFORM(MAC) && PLATFORM(CA) + if (layer->renderer()->isCanvas()) { + HTMLCanvasElement* canvas = static_cast(layer->renderer()->node()); + if (canvas->renderingContext() && canvas->renderingContext()->isAccelerated()) + layer->backing()->graphicsLayer()->setAcceleratesDrawing(true); + } +#endif + layerChanged = true; + } + } else { + if (layer->backing()) { + // If we're removing backing on a reflection, clear the source GraphicsLayer's pointer to + // its replica GraphicsLayer. In practice this should never happen because reflectee and reflection + // are both either composited, or not composited. + if (layer->isReflection()) { + RenderLayer* sourceLayer = toRenderBoxModelObject(layer->renderer()->parent())->layer(); + if (RenderLayerBacking* backing = sourceLayer->backing()) { + ASSERT(backing->graphicsLayer()->replicaLayer() == layer->backing()->graphicsLayer()); + backing->graphicsLayer()->setReplicatedByLayer(0); + } + } + + layer->clearBacking(); + layerChanged = true; + + // The layer's cached repaints rects are relative to the repaint container, so change when + // compositing changes; we need to update them here. + layer->computeRepaintRects(); + + // If we need to repaint, do so now that we've removed the backing + if (shouldRepaint == CompositingChangeRepaintNow) + repaintOnCompositingChange(layer); + } + } + +#if ENABLE(VIDEO) + if (layerChanged && layer->renderer()->isVideo()) { + // If it's a video, give the media player a chance to hook up to the layer. + RenderVideo* video = toRenderVideo(layer->renderer()); + video->acceleratedRenderingStateChanged(); + } +#endif + + if (layerChanged && layer->renderer()->isRenderIFrame()) { + RenderLayerCompositor* innerCompositor = iframeContentsCompositor(toRenderIFrame(layer->renderer())); + if (innerCompositor && innerCompositor->inCompositingMode()) + innerCompositor->updateRootLayerAttachment(); + } + + return layerChanged; +} + +bool RenderLayerCompositor::updateLayerCompositingState(RenderLayer* layer, CompositingChangeRepaint shouldRepaint) +{ + bool layerChanged = updateBacking(layer, shouldRepaint); + + // See if we need content or clipping layers. Methods called here should assume + // that the compositing state of descendant layers has not been updated yet. + if (layer->backing() && layer->backing()->updateGraphicsLayerConfiguration()) + layerChanged = true; + + return layerChanged; +} + +void RenderLayerCompositor::repaintOnCompositingChange(RenderLayer* layer) +{ + // If the renderer is not attached yet, no need to repaint. + if (layer->renderer() != m_renderView && !layer->renderer()->parent()) + return; + + RenderBoxModelObject* repaintContainer = layer->renderer()->containerForRepaint(); + if (!repaintContainer) + repaintContainer = m_renderView; + + layer->repaintIncludingNonCompositingDescendants(repaintContainer); + if (repaintContainer == m_renderView) { + // The contents of this layer may be moving between the window + // and a GraphicsLayer, so we need to make sure the window system + // synchronizes those changes on the screen. + m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); + } +} + +// The bounds of the GraphicsLayer created for a compositing layer is the union of the bounds of all the descendant +// RenderLayers that are rendered by the composited RenderLayer. +IntRect RenderLayerCompositor::calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer) +{ + if (!canBeComposited(layer)) + return IntRect(); + + IntRect boundingBoxRect = layer->localBoundingBox(); + if (layer->renderer()->isRoot()) { + // If the root layer becomes composited (e.g. because some descendant with negative z-index is composited), + // then it has to be big enough to cover the viewport in order to display the background. This is akin + // to the code in RenderBox::paintRootBoxDecorations(). + if (m_renderView->frameView()) { + int rw = m_renderView->frameView()->contentsWidth(); + int rh = m_renderView->frameView()->contentsHeight(); + + boundingBoxRect.setWidth(max(boundingBoxRect.width(), rw - boundingBoxRect.x())); + boundingBoxRect.setHeight(max(boundingBoxRect.height(), rh - boundingBoxRect.y())); + } + } + + IntRect unionBounds = boundingBoxRect; + + if (layer->renderer()->hasOverflowClip() || layer->renderer()->hasMask()) { + int ancestorRelX = 0, ancestorRelY = 0; + layer->convertToLayerCoords(ancestorLayer, ancestorRelX, ancestorRelY); + boundingBoxRect.move(ancestorRelX, ancestorRelY); + return boundingBoxRect; + } + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (!reflection->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(reflection, layer); + unionBounds.unite(childUnionBounds); + } + } + + ASSERT(layer->isStackingContext() || (!layer->m_posZOrderList || layer->m_posZOrderList->size() == 0)); + + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + if (!curLayer->isComposited()) { + IntRect childUnionBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(childUnionBounds); + } + } + } + + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + if (!curLayer->isComposited()) { + IntRect curAbsBounds = calculateCompositedBounds(curLayer, layer); + unionBounds.unite(curAbsBounds); + } + } + } + + if (layer->paintsWithTransform(PaintBehaviorNormal)) { + TransformationMatrix* affineTrans = layer->transform(); + boundingBoxRect = affineTrans->mapRect(boundingBoxRect); + unionBounds = affineTrans->mapRect(unionBounds); + } + + int ancestorRelX = 0, ancestorRelY = 0; + layer->convertToLayerCoords(ancestorLayer, ancestorRelX, ancestorRelY); + unionBounds.move(ancestorRelX, ancestorRelY); + + return unionBounds; +} + +void RenderLayerCompositor::layerWasAdded(RenderLayer* /*parent*/, RenderLayer* /*child*/) +{ + setCompositingLayersNeedRebuild(); +} + +void RenderLayerCompositor::layerWillBeRemoved(RenderLayer* parent, RenderLayer* child) +{ + if (!child->isComposited() || parent->renderer()->documentBeingDestroyed()) + return; + + setCompositingParent(child, 0); + + RenderLayer* compLayer = parent->enclosingCompositingLayer(); + if (compLayer) { + ASSERT(compLayer->backing()); + IntRect compBounds = child->backing()->compositedBounds(); + + int offsetX = 0, offsetY = 0; + child->convertToLayerCoords(compLayer, offsetX, offsetY); + compBounds.move(offsetX, offsetY); + + compLayer->setBackingNeedsRepaintInRect(compBounds); + + // The contents of this layer may be moving from a GraphicsLayer to the window, + // so we need to make sure the window system synchronizes those changes on the screen. + m_renderView->frameView()->setNeedsOneShotDrawingSynchronization(); + } + + setCompositingLayersNeedRebuild(); +} + +RenderLayer* RenderLayerCompositor::enclosingNonStackingClippingLayer(const RenderLayer* layer) const +{ + for (RenderLayer* curr = layer->parent(); curr != 0; curr = curr->parent()) { + if (curr->isStackingContext()) + return 0; + + if (curr->renderer()->hasOverflowClip() || curr->renderer()->hasClip()) + return curr; + } + return 0; +} + +void RenderLayerCompositor::addToOverlapMap(OverlapMap& overlapMap, RenderLayer* layer, IntRect& layerBounds, bool& boundsComputed) +{ + if (layer->isRootLayer()) + return; + + if (!boundsComputed) { + layerBounds = layer->renderer()->localToAbsoluteQuad(FloatRect(layer->localBoundingBox())).enclosingBoundingBox(); + // Empty rects never intersect, but we need them to for the purposes of overlap testing. + if (layerBounds.isEmpty()) + layerBounds.setSize(IntSize(1, 1)); + boundsComputed = true; + } + + overlapMap.add(layer, layerBounds); +} + +bool RenderLayerCompositor::overlapsCompositedLayers(OverlapMap& overlapMap, const IntRect& layerBounds) +{ + RenderLayerCompositor::OverlapMap::const_iterator end = overlapMap.end(); + for (RenderLayerCompositor::OverlapMap::const_iterator it = overlapMap.begin(); it != end; ++it) { + const IntRect& bounds = it->second; + if (layerBounds.intersects(bounds)) { +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + RenderLayer* intersectedLayer = it->first; + if (intersectedLayer && intersectedLayer->isFixed()) { + if (bounds.contains(layerBounds)) { + continue; + } + } +#endif + return true; + } + } + + return false; +} + +// Recurse through the layers in z-index and overflow order (which is equivalent to painting order) +// For the z-order children of a compositing layer: +// If a child layers has a compositing layer, then all subsequent layers must +// be compositing in order to render above that layer. +// +// If a child in the negative z-order list is compositing, then the layer itself +// must be compositing so that its contents render over that child. +// This implies that its positive z-index children must also be compositing. +// +void RenderLayerCompositor::computeCompositingRequirements(RenderLayer* layer, OverlapMap* overlapMap, struct CompositingState& compositingState, bool& layersChanged) +{ + layer->updateLayerPosition(); + layer->updateZOrderLists(); + layer->updateNormalFlowList(); + + // Clear the flag + layer->setHasCompositingDescendant(false); + + bool mustOverlapCompositedLayers = compositingState.m_subtreeIsCompositing; + + bool haveComputedBounds = false; + IntRect absBounds; + if (overlapMap && !overlapMap->isEmpty()) { + // If we're testing for overlap, we only need to composite if we overlap something that is already composited. + absBounds = layer->renderer()->localToAbsoluteQuad(FloatRect(layer->localBoundingBox())).enclosingBoundingBox(); + // Empty rects never intersect, but we need them to for the purposes of overlap testing. + if (absBounds.isEmpty()) + absBounds.setSize(IntSize(1, 1)); + haveComputedBounds = true; + mustOverlapCompositedLayers = overlapsCompositedLayers(*overlapMap, absBounds); + } + + layer->setMustOverlapCompositedLayers(mustOverlapCompositedLayers); + + // The children of this layer don't need to composite, unless there is + // a compositing layer among them, so start by inheriting the compositing + // ancestor with m_subtreeIsCompositing set to false. + CompositingState childState(compositingState.m_compositingAncestor); +#ifndef NDEBUG + ++childState.m_depth; +#endif + + bool willBeComposited = needsToBeComposited(layer); + +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + // If we are a fixed layer, signal it to our siblings + if (willBeComposited && layer->isFixed()) + compositingState.m_fixedSibling = true; + + if (!willBeComposited && compositingState.m_fixedSibling) { + layer->setMustOverlapCompositedLayers(true); + willBeComposited = true; + } +#endif + if (willBeComposited) { + // Tell the parent it has compositing descendants. + compositingState.m_subtreeIsCompositing = true; + // This layer now acts as the ancestor for kids. + childState.m_compositingAncestor = layer; + if (overlapMap) + addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); + } + +#if ENABLE(VIDEO) + // Video is special. It's a replaced element with a content layer, but has shadow content + // for the controller that must render in front. Without this, the controls fail to show + // when the video element is a stacking context (e.g. due to opacity or transform). + if (willBeComposited && layer->renderer()->isVideo()) + childState.m_subtreeIsCompositing = true; +#endif + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; + + // For the negative z-order, if we have a fixed layer + // we need to make all the siblings composited layers. + // Otherwise a negative layer (below the fixed layer) could + // still be drawn onto a higher z-order layer (e.g. the body) + // if not immediately intersecting with our fixed layer. + // So it's not enough here to only set m_fixedSibling for + // subsequent siblings as we do for the normal flow + // and positive z-order. + for (size_t j = 0; j < listSize; ++j) { + if ((negZOrderList->at(j))->isFixed() && + needsToBeComposited(negZOrderList->at(j))) { + childState.m_fixedSibling = true; + break; + } + } +#endif + + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); + + // If we have to make a layer for this child, make one now so we can have a contents layer + // (since we need to ensure that the -ve z-order child renders underneath our contents). + if (!willBeComposited && childState.m_subtreeIsCompositing) { + // make layer compositing + layer->setMustOverlapCompositedLayers(true); + childState.m_compositingAncestor = layer; + if (overlapMap) + addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); + willBeComposited = true; + } + } + } + } + + ASSERT(!layer->m_normalFlowListDirty); + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; +#endif + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); + } + } + + if (layer->isStackingContext()) { + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + childState.m_fixedSibling = false; +#endif + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + computeCompositingRequirements(curLayer, overlapMap, childState, layersChanged); + } + } + } + + // If we just entered compositing mode, the root will have become composited (as long as accelerated compositing is enabled). + if (layer->isRootLayer()) { + if (inCompositingMode() && m_hasAcceleratedCompositing) + willBeComposited = true; + } + + ASSERT(willBeComposited == needsToBeComposited(layer)); + + // If we have a software transform, and we have layers under us, we need to also + // be composited. Also, if we have opacity < 1, then we need to be a layer so that + // the child layers are opaque, then rendered with opacity on this layer. + if (!willBeComposited && canBeComposited(layer) && childState.m_subtreeIsCompositing && requiresCompositingWhenDescendantsAreCompositing(layer->renderer())) { + layer->setMustOverlapCompositedLayers(true); + if (overlapMap) + addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); + willBeComposited = true; + } + + ASSERT(willBeComposited == needsToBeComposited(layer)); + if (layer->reflectionLayer()) + layer->reflectionLayer()->setMustOverlapCompositedLayers(willBeComposited); + + // Subsequent layers in the parent stacking context also need to composite. + if (childState.m_subtreeIsCompositing) + compositingState.m_subtreeIsCompositing = true; + + // Set the flag to say that this SC has compositing children. + layer->setHasCompositingDescendant(childState.m_subtreeIsCompositing); + + // setHasCompositingDescendant() may have changed the answer to needsToBeComposited() when clipping, + // so test that again. + if (!willBeComposited && canBeComposited(layer) && clipsCompositingDescendants(layer)) { + if (overlapMap) + addToOverlapMap(*overlapMap, layer, absBounds, haveComputedBounds); + willBeComposited = true; + } + + // If we're back at the root, and no other layers need to be composited, and the root layer itself doesn't need + // to be composited, then we can drop out of compositing mode altogether. + if (layer->isRootLayer() && !childState.m_subtreeIsCompositing && !requiresCompositingLayer(layer)) { + enableCompositingMode(false); + willBeComposited = false; + } + + // If the layer is going into compositing mode, repaint its old location. + ASSERT(willBeComposited == needsToBeComposited(layer)); + if (!layer->isComposited() && willBeComposited) + repaintOnCompositingChange(layer); + + // Update backing now, so that we can use isComposited() reliably during tree traversal in rebuildCompositingLayerTree(). + if (updateBacking(layer, CompositingChangeRepaintNow)) + layersChanged = true; + + if (layer->reflectionLayer() && updateLayerCompositingState(layer->reflectionLayer(), CompositingChangeRepaintNow)) + layersChanged = true; +} + +void RenderLayerCompositor::setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer) +{ + ASSERT(!parentLayer || childLayer->ancestorCompositingLayer() == parentLayer); + ASSERT(childLayer->isComposited()); + + // It's possible to be called with a parent that isn't yet composited when we're doing + // partial updates as required by painting or hit testing. Just bail in that case; + // we'll do a full layer update soon. + if (!parentLayer || !parentLayer->isComposited()) + return; + + if (parentLayer) { + GraphicsLayer* hostingLayer = parentLayer->backing()->parentForSublayers(); + GraphicsLayer* hostedLayer = childLayer->backing()->childForSuperlayers(); + + hostingLayer->addChild(hostedLayer); + } else + childLayer->backing()->childForSuperlayers()->removeFromParent(); +} + +void RenderLayerCompositor::removeCompositedChildren(RenderLayer* layer) +{ + ASSERT(layer->isComposited()); + + GraphicsLayer* hostingLayer = layer->backing()->parentForSublayers(); + hostingLayer->removeAllChildren(); +} + +#if ENABLE(VIDEO) +bool RenderLayerCompositor::canAccelerateVideoRendering(RenderVideo* o) const +{ + if (!m_hasAcceleratedCompositing) + return false; + + return o->supportsAcceleratedRendering(); +} +#endif + +void RenderLayerCompositor::rebuildCompositingLayerTree(RenderLayer* layer, const CompositingState& compositingState, Vector& childLayersOfEnclosingLayer) +{ + // Make the layer compositing if necessary, and set up clipping and content layers. + // Note that we can only do work here that is independent of whether the descendant layers + // have been processed. computeCompositingRequirements() will already have done the repaint if necessary. + + RenderLayerBacking* layerBacking = layer->backing(); + if (layerBacking) { + // The compositing state of all our children has been updated already, so now + // we can compute and cache the composited bounds for this layer. + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + + layerBacking->updateGraphicsLayerConfiguration(); + layerBacking->updateGraphicsLayerGeometry(); + + if (!layer->parent()) + updateRootLayerPosition(); + } + + // If this layer has backing, then we are collecting its children, otherwise appending + // to the compositing child list of an enclosing layer. + Vector layerChildren; + Vector& childList = layerBacking ? layerChildren : childLayersOfEnclosingLayer; + + CompositingState childState = compositingState; + if (layer->isComposited()) + childState.m_compositingAncestor = layer; + +#ifndef NDEBUG + ++childState.m_depth; +#endif + + // The children of this stacking context don't need to composite, unless there is + // a compositing layer among them, so start by assuming false. + childState.m_subtreeIsCompositing = false; + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + rebuildCompositingLayerTree(curLayer, childState, childList); + } + } + + // If a negative z-order child is compositing, we get a foreground layer which needs to get parented. + if (layerBacking && layerBacking->foregroundLayer()) + childList.append(layerBacking->foregroundLayer()); + } + + ASSERT(!layer->m_normalFlowListDirty); + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + rebuildCompositingLayerTree(curLayer, childState, childList); + } + } + + if (layer->isStackingContext()) { + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + rebuildCompositingLayerTree(curLayer, childState, childList); + } + } + } + + if (layerBacking) { + bool parented = false; + if (layer->renderer()->isRenderIFrame()) + parented = parentIFrameContentLayers(toRenderIFrame(layer->renderer())); + + if (!parented) + layerBacking->parentForSublayers()->setChildren(layerChildren); + +#if ENABLE(FULLSCREEN_API) + // For the sake of clients of the full screen renderer, don't reparent + // the full screen layer out from under them if they're in the middle of + // animating. + if (layer->renderer()->isRenderFullScreen() && toRenderFullScreen(layer->renderer())->isAnimating()) + return; +#endif + childLayersOfEnclosingLayer.append(layerBacking->childForSuperlayers()); + } +} + +void RenderLayerCompositor::frameViewDidChangeSize(const IntPoint& contentsOffset) +{ + if (m_clipLayer) { + FrameView* frameView = m_renderView->frameView(); + m_clipLayer->setPosition(contentsOffset); + m_clipLayer->setSize(FloatSize(frameView->layoutWidth(), frameView->layoutHeight())); + + IntPoint scrollPosition = frameView->scrollPosition(); + m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); + } +} + +void RenderLayerCompositor::frameViewDidScroll(const IntPoint& scrollPosition) +{ + if (m_scrollLayer) + m_scrollLayer->setPosition(FloatPoint(-scrollPosition.x(), -scrollPosition.y())); +} + +String RenderLayerCompositor::layerTreeAsText() +{ + if (compositingLayerUpdatePending()) + updateCompositingLayers(); + + if (!m_rootPlatformLayer) + return String(); + + // We skip dumping the scroll and clip layers to keep layerTreeAsText output + // similar between platforms. + return m_rootPlatformLayer->layerTreeAsText(); +} + +RenderLayerCompositor* RenderLayerCompositor::iframeContentsCompositor(RenderIFrame* renderer) +{ + HTMLIFrameElement* element = static_cast(renderer->node()); + if (Document* contentDocument = element->contentDocument()) { + if (RenderView* view = contentDocument->renderView()) + return view->compositor(); + } + return 0; +} + +bool RenderLayerCompositor::parentIFrameContentLayers(RenderIFrame* renderer) +{ + RenderLayerCompositor* innerCompositor = iframeContentsCompositor(renderer); + if (!innerCompositor || !innerCompositor->inCompositingMode() || innerCompositor->rootLayerAttachment() != RootLayerAttachedViaEnclosingIframe) + return false; + + RenderLayer* layer = renderer->layer(); + if (!layer->isComposited()) + return false; + + RenderLayerBacking* backing = layer->backing(); + GraphicsLayer* hostingLayer = backing->parentForSublayers(); + GraphicsLayer* rootLayer = innerCompositor->rootPlatformLayer(); + if (hostingLayer->children().size() != 1 || hostingLayer->children()[0] != rootLayer) { + hostingLayer->removeAllChildren(); + hostingLayer->addChild(rootLayer); + } + return true; +} + +// This just updates layer geometry without changing the hierarchy. +void RenderLayerCompositor::updateLayerTreeGeometry(RenderLayer* layer) +{ + if (RenderLayerBacking* layerBacking = layer->backing()) { + // The compositing state of all our children has been updated already, so now + // we can compute and cache the composited bounds for this layer. + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + + layerBacking->updateGraphicsLayerConfiguration(); + layerBacking->updateGraphicsLayerGeometry(); + + if (!layer->parent()) + updateRootLayerPosition(); + } + + if (layer->isStackingContext()) { + ASSERT(!layer->m_zOrderListsDirty); + + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(negZOrderList->at(i)); + } + } + + ASSERT(!layer->m_normalFlowListDirty); + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(normalFlowList->at(i)); + } + + if (layer->isStackingContext()) { + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateLayerTreeGeometry(posZOrderList->at(i)); + } + } +} + +// Recurs down the RenderLayer tree until its finds the compositing descendants of compositingAncestor and updates their geometry. +void RenderLayerCompositor::updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth updateDepth) +{ + if (layer != compositingAncestor) { + if (RenderLayerBacking* layerBacking = layer->backing()) { + layerBacking->updateCompositedBounds(); + + if (RenderLayer* reflection = layer->reflectionLayer()) { + if (reflection->backing()) + reflection->backing()->updateCompositedBounds(); + } + + layerBacking->updateGraphicsLayerGeometry(); + if (updateDepth == RenderLayerBacking::CompositingChildren) + return; + } + } + + if (layer->reflectionLayer()) + updateCompositingDescendantGeometry(compositingAncestor, layer->reflectionLayer(), updateDepth); + + if (!layer->hasCompositingDescendant()) + return; + + if (layer->isStackingContext()) { + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateCompositingDescendantGeometry(compositingAncestor, negZOrderList->at(i), updateDepth); + } + } + + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) + updateCompositingDescendantGeometry(compositingAncestor, normalFlowList->at(i), updateDepth); + } + + if (layer->isStackingContext()) { + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) + updateCompositingDescendantGeometry(compositingAncestor, posZOrderList->at(i), updateDepth); + } + } +} + + +void RenderLayerCompositor::repaintCompositedLayersAbsoluteRect(const IntRect& absRect) +{ + recursiveRepaintLayerRect(rootRenderLayer(), absRect); +} + +void RenderLayerCompositor::recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect) +{ + // FIXME: This method does not work correctly with transforms. + if (layer->isComposited()) + layer->setBackingNeedsRepaintInRect(rect); + + if (layer->hasCompositingDescendant()) { + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + int x = 0; + int y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } + + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + int x = 0; + int y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } + } + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + int x = 0; + int y = 0; + curLayer->convertToLayerCoords(layer, x, y); + IntRect childRect(rect); + childRect.move(-x, -y); + recursiveRepaintLayerRect(curLayer, childRect); + } + } +} + +RenderLayer* RenderLayerCompositor::rootRenderLayer() const +{ + return m_renderView->layer(); +} + +GraphicsLayer* RenderLayerCompositor::rootPlatformLayer() const +{ + return m_clipLayer ? m_clipLayer.get() : m_rootPlatformLayer.get(); +} + +void RenderLayerCompositor::didMoveOnscreen() +{ + if (!inCompositingMode() || m_rootLayerAttachment != RootLayerUnattached) + return; + + RootLayerAttachment attachment = shouldPropagateCompositingToEnclosingIFrame() ? RootLayerAttachedViaEnclosingIframe : RootLayerAttachedViaChromeClient; + attachRootPlatformLayer(attachment); +} + +void RenderLayerCompositor::willMoveOffscreen() +{ + if (!inCompositingMode() || m_rootLayerAttachment == RootLayerUnattached) + return; + + detachRootPlatformLayer(); +} + +void RenderLayerCompositor::updateRootLayerPosition() +{ + if (m_rootPlatformLayer) { + m_rootPlatformLayer->setSize(FloatSize(m_renderView->docWidth(), m_renderView->docHeight())); + m_rootPlatformLayer->setPosition(FloatPoint(m_renderView->docLeft(), m_renderView->docTop())); + } +} + +void RenderLayerCompositor::didStartAcceleratedAnimation(CSSPropertyID property) +{ + // If an accelerated animation or transition runs, we have to turn off overlap checking because + // we don't do layout for every frame, but we have to ensure that the layering is + // correct between the animating object and other objects on the page. + if (property == CSSPropertyWebkitTransform) + setCompositingConsultsOverlap(false); +} + +bool RenderLayerCompositor::has3DContent() const +{ + return layerHas3DContent(rootRenderLayer()); +} + +bool RenderLayerCompositor::allowsIndependentlyCompositedIFrames(const FrameView* view) +{ +#if PLATFORM(MAC) + // iframes are only independently composited in Mac pre-WebKit2. + return view->platformWidget(); +#endif + return false; +} + +bool RenderLayerCompositor::shouldPropagateCompositingToEnclosingIFrame() const +{ +#if PLATFORM(ANDROID) + if (enclosingIFrameElement() && !allowsIndependentlyCompositedIFrames(m_renderView->frameView())) + return true; +#endif + // Parent document content needs to be able to render on top of a composited iframe, so correct behavior + // is to have the parent document become composited too. However, this can cause problems on platforms that + // use native views for frames (like Mac), so disable that behavior on those platforms for now. + HTMLFrameOwnerElement* ownerElement = enclosingIFrameElement(); + RenderObject* renderer = ownerElement ? ownerElement->renderer() : 0; + if (!renderer || !renderer->isRenderIFrame()) + return false; + + if (!allowsIndependentlyCompositedIFrames(m_renderView->frameView())) + return true; + + // On Mac, only propagate compositing if the iframe is overlapped in the parent + // document, or the parent is already compositing. + RenderIFrame* iframeRenderer = toRenderIFrame(renderer); + if (iframeRenderer->widget()) { + ASSERT(iframeRenderer->widget()->isFrameView()); + FrameView* view = static_cast(iframeRenderer->widget()); + if (view->isOverlappedIncludingAncestors() || view->hasCompositingAncestor()) + return true; + } + + return false; +} + +HTMLFrameOwnerElement* RenderLayerCompositor::enclosingIFrameElement() const +{ + if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) + return ownerElement->hasTagName(iframeTag) ? ownerElement : 0; + + return 0; +} + +bool RenderLayerCompositor::needsToBeComposited(const RenderLayer* layer) const +{ + if (!canBeComposited(layer)) + return false; + + // The root layer always has a compositing layer, but it may not have backing. + return requiresCompositingLayer(layer) || layer->mustOverlapCompositedLayers() || (inCompositingMode() && layer->isRootLayer()); +} + +#if PLATFORM(ANDROID) +bool RenderLayerCompositor::requiresCompositingForAndroidLayers(const RenderLayer* layer) const +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (layer->hasOverflowScroll()) + return true; + if (layer->isRootLayer() && m_renderView->frameView()->hasOverflowScroll()) + return true; +#endif +#if ENABLE(COMPOSITED_FIXED_ELEMENTS) + + // Enable composited layers (for fixed elements) + if (layer->isFixed()) + return true; +#endif + return false; +} +#endif + +// Note: this specifies whether the RL needs a compositing layer for intrinsic reasons. +// Use needsToBeComposited() to determine if a RL actually needs a compositing layer. +// static +bool RenderLayerCompositor::requiresCompositingLayer(const RenderLayer* layer) const +{ + RenderObject* renderer = layer->renderer(); + // The compositing state of a reflection should match that of its reflected layer. + if (layer->isReflection()) { + renderer = renderer->parent(); // The RenderReplica's parent is the object being reflected. + layer = toRenderBoxModelObject(renderer)->layer(); + } + return requiresCompositingForTransform(renderer) +#if PLATFORM(ANDROID) + || requiresCompositingForAndroidLayers(layer) +#endif + || requiresCompositingForVideo(renderer) + || requiresCompositingForCanvas(renderer) + || requiresCompositingForPlugin(renderer) + || requiresCompositingForIFrame(renderer) + || (canRender3DTransforms() && renderer->style()->backfaceVisibility() == BackfaceVisibilityHidden) + || clipsCompositingDescendants(layer) + || requiresCompositingForAnimation(renderer) + || requiresCompositingForFullScreen(renderer); +} + +bool RenderLayerCompositor::canBeComposited(const RenderLayer* layer) const +{ + return m_hasAcceleratedCompositing && layer->isSelfPaintingLayer(); +} + +// Return true if the given layer has some ancestor in the RenderLayer hierarchy that clips, +// up to the enclosing compositing ancestor. This is required because compositing layers are parented +// according to the z-order hierarchy, yet clipping goes down the renderer hierarchy. +// Thus, a RenderLayer can be clipped by a RenderLayer that is an ancestor in the renderer hierarchy, +// but a sibling in the z-order hierarchy. +bool RenderLayerCompositor::clippedByAncestor(RenderLayer* layer) const +{ + if (!layer->isComposited() || !layer->parent()) + return false; + + RenderLayer* compositingAncestor = layer->ancestorCompositingLayer(); + if (!compositingAncestor) + return false; + + // If the compositingAncestor clips, that will be taken care of by clipsCompositingDescendants(), + // so we only care about clipping between its first child that is our ancestor (the computeClipRoot), + // and layer. + RenderLayer* computeClipRoot = 0; + RenderLayer* curr = layer; + while (curr) { + RenderLayer* next = curr->parent(); + if (next == compositingAncestor) { + computeClipRoot = curr; + break; + } + curr = next; + } + + if (!computeClipRoot || computeClipRoot == layer) + return false; + + IntRect backgroundRect = layer->backgroundClipRect(computeClipRoot, true); + return backgroundRect != PaintInfo::infiniteRect(); +} + +// Return true if the given layer is a stacking context and has compositing child +// layers that it needs to clip. In this case we insert a clipping GraphicsLayer +// into the hierarchy between this layer and its children in the z-order hierarchy. +bool RenderLayerCompositor::clipsCompositingDescendants(const RenderLayer* layer) const +{ +#if ENABLE(ANDROID_OVERFLOW_SCROLL) + if (layer->hasOverflowScroll()) + return false; +#endif + return layer->hasCompositingDescendant() && + (layer->renderer()->hasOverflowClip() || layer->renderer()->hasClip()); +} + +bool RenderLayerCompositor::requiresCompositingForTransform(RenderObject* renderer) const +{ + if (!(m_compositingTriggers & ChromeClient::ThreeDTransformTrigger)) + return false; + + RenderStyle* style = renderer->style(); + // Note that we ask the renderer if it has a transform, because the style may have transforms, + // but the renderer may be an inline that doesn't suppport them. + return renderer->hasTransform() && (style->transform().has3DOperation() || style->transformStyle3D() == TransformStyle3DPreserve3D || style->hasPerspective()); +} + +bool RenderLayerCompositor::requiresCompositingForVideo(RenderObject* renderer) const +{ + if (!(m_compositingTriggers & ChromeClient::VideoTrigger)) + return false; +#if ENABLE(VIDEO) + if (renderer->isVideo()) { + RenderVideo* video = toRenderVideo(renderer); + return video->shouldDisplayVideo() && canAccelerateVideoRendering(video); + } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (renderer->isRenderPart()) { + if (!m_hasAcceleratedCompositing) + return false; + + Node* node = renderer->node(); + if (!node || (!node->hasTagName(HTMLNames::videoTag) && !node->hasTagName(HTMLNames::audioTag))) + return false; + + HTMLMediaElement* mediaElement = static_cast(node); + return mediaElement->player() ? mediaElement->player()->supportsAcceleratedRendering() : false; + } +#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#else + UNUSED_PARAM(renderer); +#endif + return false; +} + +bool RenderLayerCompositor::requiresCompositingForCanvas(RenderObject* renderer) const +{ + if (!(m_compositingTriggers & ChromeClient::CanvasTrigger)) + return false; + + if (renderer->isCanvas()) { + HTMLCanvasElement* canvas = static_cast(renderer->node()); + return canvas->renderingContext() && canvas->renderingContext()->isAccelerated(); + } + return false; +} + +bool RenderLayerCompositor::requiresCompositingForPlugin(RenderObject* renderer) const +{ + if (!(m_compositingTriggers & ChromeClient::PluginTrigger)) + return false; + + bool composite = (renderer->isEmbeddedObject() && toRenderEmbeddedObject(renderer)->allowsAcceleratedCompositing()) + || (renderer->isApplet() && toRenderApplet(renderer)->allowsAcceleratedCompositing()); + if (!composite) + return false; + + m_compositingDependsOnGeometry = true; + + RenderWidget* pluginRenderer = toRenderWidget(renderer); + // If we can't reliably know the size of the plugin yet, don't change compositing state. + if (pluginRenderer->needsLayout()) + return pluginRenderer->hasLayer() && pluginRenderer->layer()->isComposited(); + + // Don't go into compositing mode if height or width are zero, or size is 1x1. + IntRect contentBox = pluginRenderer->contentBoxRect(); + return contentBox.height() * contentBox.width() > 1; +} + +bool RenderLayerCompositor::requiresCompositingForIFrame(RenderObject* renderer) const +{ + if (!renderer->isRenderIFrame()) + return false; + + RenderIFrame* iframeRenderer = toRenderIFrame(renderer); + + if (!iframeRenderer->requiresAcceleratedCompositing()) + return false; + + m_compositingDependsOnGeometry = true; + + RenderLayerCompositor* innerCompositor = iframeContentsCompositor(iframeRenderer); + if (!innerCompositor->shouldPropagateCompositingToEnclosingIFrame()) + return false; + + // If we can't reliably know the size of the iframe yet, don't change compositing state. + if (renderer->needsLayout()) + return iframeRenderer->hasLayer() && iframeRenderer->layer()->isComposited(); + + // Don't go into compositing mode if height or width are zero. + IntRect contentBox = iframeRenderer->contentBoxRect(); + return contentBox.height() * contentBox.width() > 0; +} + +bool RenderLayerCompositor::requiresCompositingForAnimation(RenderObject* renderer) const +{ + if (!(m_compositingTriggers & ChromeClient::AnimationTrigger)) + return false; + + if (AnimationController* animController = renderer->animation()) { + return (animController->isRunningAnimationOnRenderer(renderer, CSSPropertyOpacity) && inCompositingMode()) + || animController->isRunningAnimationOnRenderer(renderer, CSSPropertyWebkitTransform); + } + return false; +} + +bool RenderLayerCompositor::requiresCompositingWhenDescendantsAreCompositing(RenderObject* renderer) const +{ + return renderer->hasTransform() || renderer->isTransparent() || renderer->hasMask() || renderer->hasReflection(); +} + +bool RenderLayerCompositor::requiresCompositingForFullScreen(RenderObject* renderer) const +{ +#if ENABLE(FULLSCREEN_API) + return renderer->isRenderFullScreen() && toRenderFullScreen(renderer)->isAnimating(); +#else + return false; +#endif +} + +// If an element has negative z-index children, those children render in front of the +// layer background, so we need an extra 'contents' layer for the foreground of the layer +// object. +bool RenderLayerCompositor::needsContentsCompositingLayer(const RenderLayer* layer) const +{ + return (layer->m_negZOrderList && layer->m_negZOrderList->size() > 0); +} + +bool RenderLayerCompositor::requiresScrollLayer(RootLayerAttachment attachment) const +{ + // We need to handle our own scrolling if we're: + return !m_renderView->frameView()->platformWidget() // viewless (i.e. non-Mac, or Mac in WebKit2) + || attachment == RootLayerAttachedViaEnclosingIframe; // a composited iframe on Mac +} + +void RenderLayerCompositor::ensureRootPlatformLayer() +{ + RootLayerAttachment expectedAttachment = shouldPropagateCompositingToEnclosingIFrame() ? RootLayerAttachedViaEnclosingIframe : RootLayerAttachedViaChromeClient; + if (expectedAttachment == m_rootLayerAttachment) + return; + + if (!m_rootPlatformLayer) { + m_rootPlatformLayer = GraphicsLayer::create(0); +#ifndef NDEBUG + m_rootPlatformLayer->setName("Root platform"); +#endif + m_rootPlatformLayer->setSize(FloatSize(m_renderView->rightLayoutOverflow(), m_renderView->bottomLayoutOverflow())); + m_rootPlatformLayer->setPosition(FloatPoint()); + + // Need to clip to prevent transformed content showing outside this frame + m_rootPlatformLayer->setMasksToBounds(true); + } + + if (requiresScrollLayer(expectedAttachment)) { + if (!m_clipLayer) { + ASSERT(!m_scrollLayer); + // Create a clipping layer if this is an iframe + m_clipLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_clipLayer->setName("iframe Clipping"); +#endif + m_clipLayer->setMasksToBounds(true); + + m_scrollLayer = GraphicsLayer::create(this); +#ifndef NDEBUG + m_scrollLayer->setName("iframe scrolling"); +#endif + // Hook them up + m_clipLayer->addChild(m_scrollLayer.get()); + m_scrollLayer->addChild(m_rootPlatformLayer.get()); + + frameViewDidChangeSize(); + frameViewDidScroll(m_renderView->frameView()->scrollPosition()); + } + } else { + if (m_clipLayer) { + m_clipLayer->removeAllChildren(); + m_clipLayer->removeFromParent(); + m_clipLayer = 0; + + m_scrollLayer->removeAllChildren(); + m_scrollLayer = 0; + } + } + + // Check to see if we have to change the attachment + if (m_rootLayerAttachment != RootLayerUnattached) + detachRootPlatformLayer(); + + attachRootPlatformLayer(expectedAttachment); +} + +void RenderLayerCompositor::destroyRootPlatformLayer() +{ + if (!m_rootPlatformLayer) + return; + + detachRootPlatformLayer(); + if (m_clipLayer) { + m_clipLayer->removeAllChildren(); + m_clipLayer = 0; + + m_scrollLayer->removeAllChildren(); + m_scrollLayer = 0; + } + ASSERT(!m_scrollLayer); + m_rootPlatformLayer = 0; +} + +void RenderLayerCompositor::attachRootPlatformLayer(RootLayerAttachment attachment) +{ + if (!m_rootPlatformLayer) + return; + + switch (attachment) { + case RootLayerUnattached: + ASSERT_NOT_REACHED(); + break; + case RootLayerAttachedViaChromeClient: { + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + page->chrome()->client()->attachRootGraphicsLayer(frame, rootPlatformLayer()); + break; + } + case RootLayerAttachedViaEnclosingIframe: { + // The layer will get hooked up via RenderLayerBacking::updateGraphicsLayerConfiguration() + // for the iframe's renderer in the parent document. + scheduleNeedsStyleRecalc(m_renderView->document()->ownerElement()); + break; + } + } + + m_rootLayerAttachment = attachment; + rootLayerAttachmentChanged(); +} + +void RenderLayerCompositor::detachRootPlatformLayer() +{ + if (!m_rootPlatformLayer || m_rootLayerAttachment == RootLayerUnattached) + return; + + switch (m_rootLayerAttachment) { + case RootLayerAttachedViaEnclosingIframe: { + // The layer will get unhooked up via RenderLayerBacking::updateGraphicsLayerConfiguration() + // for the iframe's renderer in the parent document. + if (m_clipLayer) + m_clipLayer->removeFromParent(); + else + m_rootPlatformLayer->removeFromParent(); + + if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) + scheduleNeedsStyleRecalc(ownerElement); + break; + } + case RootLayerAttachedViaChromeClient: { + Frame* frame = m_renderView->frameView()->frame(); + Page* page = frame ? frame->page() : 0; + if (!page) + return; + + page->chrome()->client()->attachRootGraphicsLayer(frame, 0); + } + break; + case RootLayerUnattached: + break; + } + + m_rootLayerAttachment = RootLayerUnattached; + rootLayerAttachmentChanged(); +} + +void RenderLayerCompositor::updateRootLayerAttachment() +{ + ensureRootPlatformLayer(); +} + +void RenderLayerCompositor::rootLayerAttachmentChanged() +{ + // The attachment can affect whether the RenderView layer's paintingGoesToWindow() behavior, + // so call updateGraphicsLayerGeometry() to udpate that. + RenderLayer* layer = m_renderView->layer(); + if (RenderLayerBacking* backing = layer ? layer->backing() : 0) + backing->updateDrawsContent(); +} + +static void needsStyleRecalcCallback(Node* node) +{ + node->setNeedsStyleRecalc(SyntheticStyleChange); +} + +void RenderLayerCompositor::scheduleNeedsStyleRecalc(Element* element) +{ + if (ContainerNode::postAttachCallbacksAreSuspended()) + ContainerNode::queuePostAttachCallback(needsStyleRecalcCallback, element); + else + element->setNeedsStyleRecalc(SyntheticStyleChange); +} + +// IFrames are special, because we hook compositing layers together across iframe boundaries +// when both parent and iframe content are composited. So when this frame becomes composited, we have +// to use a synthetic style change to get the iframes into RenderLayers in order to allow them to composite. +void RenderLayerCompositor::notifyIFramesOfCompositingChange() +{ + Frame* frame = m_renderView->frameView() ? m_renderView->frameView()->frame() : 0; + if (!frame) + return; + + for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->traverseNext(frame)) { + if (child->document() && child->document()->ownerElement()) + scheduleNeedsStyleRecalc(child->document()->ownerElement()); + } + + // Compositing also affects the answer to RenderIFrame::requiresAcceleratedCompositing(), so + // we need to schedule a style recalc in our parent document. + if (HTMLFrameOwnerElement* ownerElement = m_renderView->document()->ownerElement()) + scheduleNeedsStyleRecalc(ownerElement); +} + +bool RenderLayerCompositor::layerHas3DContent(const RenderLayer* layer) const +{ + const RenderStyle* style = layer->renderer()->style(); + + if (style && + (style->transformStyle3D() == TransformStyle3DPreserve3D || + style->hasPerspective() || + style->transform().has3DOperation())) + return true; + + if (layer->isStackingContext()) { + if (Vector* negZOrderList = layer->negZOrderList()) { + size_t listSize = negZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = negZOrderList->at(i); + if (layerHas3DContent(curLayer)) + return true; + } + } + + if (Vector* posZOrderList = layer->posZOrderList()) { + size_t listSize = posZOrderList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = posZOrderList->at(i); + if (layerHas3DContent(curLayer)) + return true; + } + } + } + + if (Vector* normalFlowList = layer->normalFlowList()) { + size_t listSize = normalFlowList->size(); + for (size_t i = 0; i < listSize; ++i) { + RenderLayer* curLayer = normalFlowList->at(i); + if (layerHas3DContent(curLayer)) + return true; + } + } + return false; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/rendering/RenderLayerCompositor.h b/Source/WebCore/rendering/RenderLayerCompositor.h new file mode 100644 index 0000000..53a0f9a --- /dev/null +++ b/Source/WebCore/rendering/RenderLayerCompositor.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderLayerCompositor_h +#define RenderLayerCompositor_h + +#include "ChromeClient.h" +#include "RenderLayer.h" +#include "RenderLayerBacking.h" + +namespace WebCore { + +#define PROFILE_LAYER_REBUILD 0 + +class GraphicsLayer; +class RenderEmbeddedObject; +class RenderIFrame; +#if ENABLE(VIDEO) +class RenderVideo; +#endif + +enum CompositingUpdateType { + CompositingUpdateAfterLayoutOrStyleChange, + CompositingUpdateOnPaitingOrHitTest, + CompositingUpdateOnScroll +}; + +// RenderLayerCompositor manages the hierarchy of +// composited RenderLayers. It determines which RenderLayers +// become compositing, and creates and maintains a hierarchy of +// GraphicsLayers based on the RenderLayer painting order. +// +// There is one RenderLayerCompositor per RenderView. + +class RenderLayerCompositor : public GraphicsLayerClient { +public: + RenderLayerCompositor(RenderView*); + ~RenderLayerCompositor(); + + // Return true if this RenderView is in "compositing mode" (i.e. has one or more + // composited RenderLayers) + bool inCompositingMode() const { return m_compositing; } + // This will make a compositing layer at the root automatically, and hook up to + // the native view/window system. + void enableCompositingMode(bool enable = true); + + // Returns true if the accelerated compositing is enabled + bool hasAcceleratedCompositing() const { return m_hasAcceleratedCompositing; } + + bool canRender3DTransforms() const; + + // Copy the accelerated compositing related flags from Settings + void cacheAcceleratedCompositingFlags(); + + // Called when the layer hierarchy needs to be updated (compositing layers have been + // created, destroyed or re-parented). + void setCompositingLayersNeedRebuild(bool needRebuild = true); + bool compositingLayersNeedRebuild() const { return m_compositingLayersNeedRebuild; } + + // Controls whether or not to consult geometry when deciding which layers need + // to be composited. Defaults to true. + void setCompositingConsultsOverlap(bool b) { m_compositingConsultsOverlap = b; } + bool compositingConsultsOverlap() const { return m_compositingConsultsOverlap; } + + void scheduleSync(); + + // Rebuild the tree of compositing layers + void updateCompositingLayers(CompositingUpdateType = CompositingUpdateAfterLayoutOrStyleChange, RenderLayer* updateRoot = 0); + // This is only used when state changes and we do not exepect a style update or layout to happen soon (e.g. when + // we discover that an iframe is overlapped during painting). + void scheduleCompositingLayerUpdate(); + bool compositingLayerUpdatePending() const; + + // Update the compositing state of the given layer. Returns true if that state changed. + enum CompositingChangeRepaint { CompositingChangeRepaintNow, CompositingChangeWillRepaintLater }; + bool updateLayerCompositingState(RenderLayer*, CompositingChangeRepaint = CompositingChangeRepaintNow); + + // Update the geometry for compositing children of compositingAncestor. + void updateCompositingDescendantGeometry(RenderLayer* compositingAncestor, RenderLayer* layer, RenderLayerBacking::UpdateDepth); + + // Whether layer's backing needs a graphics layer to do clipping by an ancestor (non-stacking-context parent with overflow). + bool clippedByAncestor(RenderLayer*) const; + // Whether layer's backing needs a graphics layer to clip z-order children of the given layer. + bool clipsCompositingDescendants(const RenderLayer*) const; + + // Whether the given layer needs an extra 'contents' layer. + bool needsContentsCompositingLayer(const RenderLayer*) const; + // Return the bounding box required for compositing layer and its childern, relative to ancestorLayer. + // If layerBoundingBox is not 0, on return it contains the bounding box of this layer only. + IntRect calculateCompositedBounds(const RenderLayer* layer, const RenderLayer* ancestorLayer); + + // Repaint the appropriate layers when the given RenderLayer starts or stops being composited. + void repaintOnCompositingChange(RenderLayer*); + + // Notify us that a layer has been added or removed + void layerWasAdded(RenderLayer* parent, RenderLayer* child); + void layerWillBeRemoved(RenderLayer* parent, RenderLayer* child); + + // Get the nearest ancestor layer that has overflow or clip, but is not a stacking context + RenderLayer* enclosingNonStackingClippingLayer(const RenderLayer* layer) const; + + // Repaint parts of all composited layers that intersect the given absolute rectangle. + void repaintCompositedLayersAbsoluteRect(const IntRect&); + + RenderLayer* rootRenderLayer() const; + GraphicsLayer* rootPlatformLayer() const; + + enum RootLayerAttachment { + RootLayerUnattached, + RootLayerAttachedViaChromeClient, + RootLayerAttachedViaEnclosingIframe + }; + + RootLayerAttachment rootLayerAttachment() const { return m_rootLayerAttachment; } + void updateRootLayerAttachment(); + void updateRootLayerPosition(); + + void didMoveOnscreen(); + void willMoveOffscreen(); + + void didStartAcceleratedAnimation(CSSPropertyID); + +#if ENABLE(VIDEO) + // Use by RenderVideo to ask if it should try to use accelerated compositing. + bool canAccelerateVideoRendering(RenderVideo*) const; +#endif + + // Walk the tree looking for layers with 3d transforms. Useful in case you need + // to know if there is non-affine content, e.g. for drawing into an image. + bool has3DContent() const; + + // Most platforms connect compositing layer trees between iframes and their parent document. + // Some (currently just Mac) allow iframes to do their own compositing. + static bool allowsIndependentlyCompositedIFrames(const FrameView*); + bool shouldPropagateCompositingToEnclosingIFrame() const; + + // FIXME: This should be a RenderIFrame* + HTMLFrameOwnerElement* enclosingIFrameElement() const; + + static RenderLayerCompositor* iframeContentsCompositor(RenderIFrame*); + // Return true if the layers changed. + static bool parentIFrameContentLayers(RenderIFrame*); + + // Update the geometry of the layers used for clipping and scrolling in frames. + void frameViewDidChangeSize(const IntPoint& contentsOffset = IntPoint()); + void frameViewDidScroll(const IntPoint& = IntPoint()); + + String layerTreeAsText(); + + // These are named to avoid conflicts with the functions in GraphicsLayerClient + // These return the actual internal variables. + bool compositorShowDebugBorders() const { return m_showDebugBorders; } + bool compositorShowRepaintCounter() const { return m_showRepaintCounter; } + +private: + // GraphicsLayerClient Implementation + virtual void notifyAnimationStarted(const GraphicsLayer*, double) { } + virtual void notifySyncRequired(const GraphicsLayer*) { scheduleSync(); } + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect&) { } + + // These calls return false always. They are saying that the layers associated with this client + // (the clipLayer and scrollLayer) should never show debugging info. + virtual bool showDebugBorders() const { return false; } + virtual bool showRepaintCounter() const { return false; } + + // Whether the given RL needs a compositing layer. + bool needsToBeComposited(const RenderLayer*) const; + // Whether the layer has an intrinsic need for compositing layer. + bool requiresCompositingLayer(const RenderLayer*) const; + // Whether the layer could ever be composited. + bool canBeComposited(const RenderLayer*) const; + + // Make or destroy the backing for this layer; returns true if backing changed. + bool updateBacking(RenderLayer*, CompositingChangeRepaint shouldRepaint); + + // Repaint the given rect (which is layer's coords), and regions of child layers that intersect that rect. + void recursiveRepaintLayerRect(RenderLayer* layer, const IntRect& rect); + + typedef HashMap OverlapMap; + static void addToOverlapMap(OverlapMap&, RenderLayer*, IntRect& layerBounds, bool& boundsComputed); + static bool overlapsCompositedLayers(OverlapMap&, const IntRect& layerBounds); + + void updateCompositingLayersTimerFired(Timer*); + + // Returns true if any layer's compositing changed + void computeCompositingRequirements(RenderLayer*, OverlapMap*, struct CompositingState&, bool& layersChanged); + + // Recurses down the tree, parenting descendant compositing layers and collecting an array of child layers for the current compositing layer. + void rebuildCompositingLayerTree(RenderLayer* layer, const struct CompositingState&, Vector& childGraphicsLayersOfEnclosingLayer); + + // Recurses down the tree, updating layer geometry only. + void updateLayerTreeGeometry(RenderLayer*); + + // Hook compositing layers together + void setCompositingParent(RenderLayer* childLayer, RenderLayer* parentLayer); + void removeCompositedChildren(RenderLayer*); + + bool layerHas3DContent(const RenderLayer*) const; + + void ensureRootPlatformLayer(); + void destroyRootPlatformLayer(); + + void attachRootPlatformLayer(RootLayerAttachment); + void detachRootPlatformLayer(); + + void rootLayerAttachmentChanged(); + + void scheduleNeedsStyleRecalc(Element*); + void notifyIFramesOfCompositingChange(); + + // Whether a running transition or animation enforces the need for a compositing layer. + bool requiresCompositingForAnimation(RenderObject*) const; + bool requiresCompositingForTransform(RenderObject*) const; + bool requiresCompositingForVideo(RenderObject*) const; + bool requiresCompositingForCanvas(RenderObject*) const; + bool requiresCompositingForPlugin(RenderObject*) const; + bool requiresCompositingForIFrame(RenderObject*) const; + bool requiresCompositingWhenDescendantsAreCompositing(RenderObject*) const; + bool requiresCompositingForFullScreen(RenderObject*) const; + +#if PLATFORM(ANDROID) + // Whether we are using layers for new android features (overflow support, fixed elements) + bool requiresCompositingForAndroidLayers(const RenderLayer* layer) const; +#endif + + bool requiresScrollLayer(RootLayerAttachment) const; + +private: + RenderView* m_renderView; + OwnPtr m_rootPlatformLayer; + Timer m_updateCompositingLayersTimer; + + bool m_hasAcceleratedCompositing; + ChromeClient::CompositingTriggerFlags m_compositingTriggers; + + bool m_showDebugBorders; + bool m_showRepaintCounter; + bool m_compositingConsultsOverlap; + + // When true, we have to wait until layout has happened before we can decide whether to enter compositing mode, + // because only then do we know the final size of plugins and iframes. + // FIXME: once set, this is never cleared. + mutable bool m_compositingDependsOnGeometry; + + bool m_compositing; + bool m_compositingLayersNeedRebuild; + + RootLayerAttachment m_rootLayerAttachment; + + // Enclosing clipping layer for iframe content + OwnPtr m_clipLayer; + OwnPtr m_scrollLayer; + +#if PROFILE_LAYER_REBUILD + int m_rootLayerUpdateCount; +#endif +}; + + +} // namespace WebCore + +#endif // RenderLayerCompositor_h diff --git a/Source/WebCore/rendering/RenderLineBoxList.cpp b/Source/WebCore/rendering/RenderLineBoxList.cpp new file mode 100644 index 0000000..85d7f18 --- /dev/null +++ b/Source/WebCore/rendering/RenderLineBoxList.cpp @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderLineBoxList.h" + +#include "HitTestResult.h" +#include "InlineTextBox.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderView.h" +#include "RootInlineBox.h" + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG +RenderLineBoxList::~RenderLineBoxList() +{ + ASSERT(!m_firstLineBox); + ASSERT(!m_lastLineBox); +} +#endif + +void RenderLineBoxList::appendLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = box; + else { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = box; + } + + checkConsistency(); +} + +void RenderLineBoxList::deleteLineBoxTree(RenderArena* arena) +{ + InlineFlowBox* line = m_firstLineBox; + InlineFlowBox* nextLine; + while (line) { + nextLine = line->nextLineBox(); + line->deleteLine(arena); + line = nextLine; + } + m_firstLineBox = m_lastLineBox = 0; +} + +void RenderLineBoxList::extractLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + m_lastLineBox = box->prevLineBox(); + if (box == m_firstLineBox) + m_firstLineBox = 0; + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(0); + box->setPreviousLineBox(0); + for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) + curr->setExtracted(); + + checkConsistency(); +} + +void RenderLineBoxList::attachLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (m_lastLineBox) { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + } else + m_firstLineBox = box; + InlineFlowBox* last = box; + for (InlineFlowBox* curr = box; curr; curr = curr->nextLineBox()) { + curr->setExtracted(false); + last = curr; + } + m_lastLineBox = last; + + checkConsistency(); +} + +void RenderLineBoxList::removeLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (box == m_firstLineBox) + m_firstLineBox = box->nextLineBox(); + if (box == m_lastLineBox) + m_lastLineBox = box->prevLineBox(); + if (box->nextLineBox()) + box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(box->nextLineBox()); + + checkConsistency(); +} + +void RenderLineBoxList::deleteLineBoxes(RenderArena* arena) +{ + if (m_firstLineBox) { + InlineFlowBox* next; + for (InlineFlowBox* curr = m_firstLineBox; curr; curr = next) { + next = curr->nextLineBox(); + curr->destroy(arena); + } + m_firstLineBox = 0; + m_lastLineBox = 0; + } +} + +void RenderLineBoxList::dirtyLineBoxes() +{ + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + curr->dirtyLineBoxes(); +} + +bool RenderLineBoxList::rangeIntersectsRect(RenderBoxModelObject* renderer, int logicalTop, int logicalBottom, const IntRect& rect, int tx, int ty) const +{ + RenderBox* block; + if (renderer->isBox()) + block = toRenderBox(renderer); + else + block = renderer->containingBlock(); + int physicalStart = block->flipForWritingMode(logicalTop); + int physicalEnd = block->flipForWritingMode(logicalBottom); + int physicalExtent = abs(physicalEnd - physicalStart); + physicalStart = min(physicalStart, physicalEnd); + + if (renderer->style()->isHorizontalWritingMode()) { + physicalStart += ty; + if (physicalStart >= rect.bottom() || physicalStart + physicalExtent <= rect.y()) + return false; + } else { + physicalStart += tx; + if (physicalStart >= rect.right() || physicalStart + physicalExtent <= rect.x()) + return false; + } + + return true; +} + +bool RenderLineBoxList::anyLineIntersectsRect(RenderBoxModelObject* renderer, const IntRect& rect, int tx, int ty, bool usePrintRect, int outlineSize) const +{ + // We can check the first box and last box and avoid painting/hit testing if we don't + // intersect. This is a quick short-circuit that we can take to avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend below the last line. + int firstLineTop = firstLineBox()->logicalTopVisualOverflow(); + if (usePrintRect && !firstLineBox()->parent()) + firstLineTop = min(firstLineTop, firstLineBox()->root()->lineTop()); + int lastLineBottom = lastLineBox()->logicalBottomVisualOverflow(); + if (usePrintRect && !lastLineBox()->parent()) + lastLineBottom = max(lastLineBottom, lastLineBox()->root()->lineBottom()); + int logicalTop = firstLineTop - outlineSize; + int logicalBottom = outlineSize + lastLineBottom; + + return rangeIntersectsRect(renderer, logicalTop, logicalBottom, rect, tx, ty); +} + +bool RenderLineBoxList::lineIntersectsDirtyRect(RenderBoxModelObject* renderer, InlineFlowBox* box, const PaintInfo& paintInfo, int tx, int ty) const +{ + int logicalTop = min(box->logicalTopVisualOverflow(), box->root()->selectionTop()) - renderer->maximalOutlineSize(paintInfo.phase); + int logicalBottom = box->logicalBottomVisualOverflow() + renderer->maximalOutlineSize(paintInfo.phase); + + return rangeIntersectsRect(renderer, logicalTop, logicalBottom, paintInfo.rect, tx, ty); +} + +void RenderLineBoxList::paint(RenderBoxModelObject* renderer, PaintInfo& paintInfo, int tx, int ty) const +{ + // Only paint during the foreground/selection phases. + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline + && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines && paintInfo.phase != PaintPhaseTextClip + && paintInfo.phase != PaintPhaseMask) + return; + + ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could paint like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return; + + // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit + // NSViews. Do not add any more code for this. + RenderView* v = renderer->view(); + bool usePrintRect = !v->printRect().isEmpty(); + int outlineSize = renderer->maximalOutlineSize(paintInfo.phase); + if (!anyLineIntersectsRect(renderer, paintInfo.rect, tx, ty, usePrintRect, outlineSize)) + return; + + PaintInfo info(paintInfo); + ListHashSet outlineObjects; + info.outlineObjects = &outlineObjects; + + // See if our root lines intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + if (usePrintRect) { + // FIXME: This is the deprecated pagination model that is still needed + // for embedded views inside AppKit. AppKit is incapable of paginating vertical + // text pages, so we don't have to deal with vertical lines at all here. + int topForPaginationCheck = curr->topVisualOverflow(); + int bottomForPaginationCheck = curr->bottomVisualOverflow(); + if (!curr->parent()) { + // We're a root box. Use lineTop and lineBottom as well here. + topForPaginationCheck = min(topForPaginationCheck, curr->root()->lineTop()); + bottomForPaginationCheck = max(bottomForPaginationCheck, curr->root()->lineBottom()); + } + if (bottomForPaginationCheck - topForPaginationCheck <= v->printRect().height()) { + if (ty + bottomForPaginationCheck > v->printRect().bottom()) { + if (RootInlineBox* nextRootBox = curr->root()->nextRootBox()) + bottomForPaginationCheck = min(bottomForPaginationCheck, min(nextRootBox->topVisualOverflow(), nextRootBox->lineTop())); + } + if (ty + bottomForPaginationCheck > v->printRect().bottom()) { + if (ty + topForPaginationCheck < v->truncatedAt()) + v->setBestTruncatedAt(ty + topForPaginationCheck, renderer); + // If we were able to truncate, don't paint. + if (ty + topForPaginationCheck >= v->truncatedAt()) + break; + } + } + } + + if (lineIntersectsDirtyRect(renderer, curr, info, tx, ty)) + curr->paint(info, tx, ty); + } + + if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) { + ListHashSet::iterator end = info.outlineObjects->end(); + for (ListHashSet::iterator it = info.outlineObjects->begin(); it != end; ++it) { + RenderInline* flow = *it; + flow->paintOutline(info.context, tx, ty); + } + info.outlineObjects->clear(); + } +} + + +bool RenderLineBoxList::hitTest(RenderBoxModelObject* renderer, const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) const +{ + if (hitTestAction != HitTestForeground) + return false; + + ASSERT(renderer->isRenderBlock() || (renderer->isRenderInline() && renderer->hasLayer())); // The only way an inline could hit test like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return false; + + bool isHorizontal = firstLineBox()->isHorizontal(); + + int logicalPointStart = isHorizontal ? y - result.topPadding() : x - result.leftPadding(); + int logicalPointEnd = (isHorizontal ? y + result.bottomPadding() : x + result.rightPadding()) + 1; + IntRect rect(isHorizontal ? x : logicalPointStart, isHorizontal ? logicalPointStart : y, + isHorizontal ? 1 : logicalPointEnd - logicalPointStart, + isHorizontal ? logicalPointEnd - logicalPointStart : 1); + if (!anyLineIntersectsRect(renderer, rect, tx, ty)) + return false; + + // See if our root lines contain the point. If so, then we hit test + // them further. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevLineBox()) { + if (rangeIntersectsRect(renderer, curr->logicalTopVisualOverflow(), curr->logicalBottomVisualOverflow(), rect, tx, ty)) { + bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty); + if (inside) { + renderer->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + } + + return false; +} + +void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child) +{ + if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isBlockFlow()))) + return; + + // If we have no first line box, then just bail early. + if (!firstLineBox()) { + // For an empty inline, go ahead and propagate the check up to our parent, unless the parent + // is already dirty. + if (container->isInline() && !container->parent()->selfNeedsLayout()) + container->parent()->dirtyLinesFromChangedChild(container); + return; + } + + // Try to figure out which line box we belong in. First try to find a previous + // line box by examining our siblings. If we didn't find a line box, then use our + // parent's first line box. + RootInlineBox* box = 0; + RenderObject* curr = 0; + for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { + if (curr->isFloatingOrPositioned()) + continue; + + if (curr->isReplaced()) { + InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper(); + if (wrapper) + box = wrapper->root(); + } else if (curr->isText()) { + InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); + if (textBox) + box = textBox->root(); + } else if (curr->isRenderInline()) { + InlineFlowBox* flowBox = toRenderInline(curr)->lastLineBox(); + if (flowBox) + box = flowBox->root(); + } + + if (box) + break; + } + if (!box) + box = firstLineBox()->root(); + + // If we found a line box, then dirty it. + if (box) { + RootInlineBox* adjacentBox; + box->markDirty(); + + // dirty the adjacent lines that might be affected + // NOTE: we dirty the previous line because RootInlineBox objects cache + // the address of the first object on the next line after a BR, which we may be + // invalidating here. For more info, see how RenderBlock::layoutInlineChildren + // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, + // despite the name, actually returns the first RenderObject after the BR. + // "Typing after pasting line does not appear until after window resize." + adjacentBox = box->prevRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + if (child->isBR() || (curr && curr->isBR())) { + adjacentBox = box->nextRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + } + } +} + +#ifndef NDEBUG + +void RenderLineBoxList::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + const InlineFlowBox* prev = 0; + for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextLineBox()) { + ASSERT(child->prevLineBox() == prev); + prev = child; + } + ASSERT(prev == m_lastLineBox); +#endif +} + +#endif + +} diff --git a/Source/WebCore/rendering/RenderLineBoxList.h b/Source/WebCore/rendering/RenderLineBoxList.h new file mode 100644 index 0000000..9708d67 --- /dev/null +++ b/Source/WebCore/rendering/RenderLineBoxList.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef RenderLineBoxList_h +#define RenderLineBoxList_h + +#include "RenderBox.h" + +namespace WebCore { + +class RenderLineBoxList { +public: + RenderLineBoxList() + : m_firstLineBox(0) + , m_lastLineBox(0) + { + } + +#ifndef NDEBUG + ~RenderLineBoxList(); +#endif + + InlineFlowBox* firstLineBox() const { return m_firstLineBox; } + InlineFlowBox* lastLineBox() const { return m_lastLineBox; } + + void checkConsistency() const; + + void appendLineBox(InlineFlowBox*); + + void deleteLineBoxTree(RenderArena*); + void deleteLineBoxes(RenderArena*); + + void extractLineBox(InlineFlowBox*); + void attachLineBox(InlineFlowBox*); + void removeLineBox(InlineFlowBox*); + + void dirtyLineBoxes(); + void dirtyLinesFromChangedChild(RenderObject* parent, RenderObject* child); + + void paint(RenderBoxModelObject*, PaintInfo&, int x, int y) const; + bool hitTest(RenderBoxModelObject*, const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction) const; + +private: + bool anyLineIntersectsRect(RenderBoxModelObject*, const IntRect&, int tx, int ty, bool usePrintRect = false, int outlineSize = 0) const; + bool lineIntersectsDirtyRect(RenderBoxModelObject*, InlineFlowBox*, const PaintInfo&, int tx, int ty) const; + bool rangeIntersectsRect(RenderBoxModelObject*, int logicalTop, int logicalBottom, const IntRect&, int tx, int ty) const; + + // For block flows, each box represents the root inline box for a line in the + // paragraph. + // For inline flows, each box represents a portion of that inline. + InlineFlowBox* m_firstLineBox; + InlineFlowBox* m_lastLineBox; +}; + + +#ifdef NDEBUG +inline void RenderLineBoxList::checkConsistency() const +{ +} +#endif + +} // namespace WebCore + +#endif // RenderFlow_h diff --git a/Source/WebCore/rendering/RenderListBox.cpp b/Source/WebCore/rendering/RenderListBox.cpp new file mode 100644 index 0000000..ed7f8ee --- /dev/null +++ b/Source/WebCore/rendering/RenderListBox.cpp @@ -0,0 +1,743 @@ +/* + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderListBox.h" + +#include "AXObjectCache.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "OptionGroupElement.h" +#include "OptionElement.h" +#include "Page.h" +#include "RenderScrollbar.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "Scrollbar.h" +#include "SelectElement.h" +#include "SelectionController.h" +#include "NodeRenderStyle.h" +#include + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int rowSpacing = 1; + +const int optionsSpacingHorizontal = 2; + +const int minSize = 4; +const int maxDefaultSize = 10; + +// FIXME: This hardcoded baselineAdjustment is what we used to do for the old +// widget, but I'm not sure this is right for the new control. +const int baselineAdjustment = 7; + +RenderListBox::RenderListBox(Element* element) + : RenderBlock(element) + , m_optionsChanged(true) + , m_scrollToRevealSelectionAfterLayout(false) + , m_inAutoscroll(false) + , m_optionsWidth(0) + , m_indexOffset(0) +{ +} + +RenderListBox::~RenderListBox() +{ + setHasVerticalScrollbar(false); +} + +void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + setReplaced(isInline()); +} + +void RenderListBox::updateFromElement() +{ + if (m_optionsChanged) { + const Vector& listItems = toSelectElement(static_cast(node()))->listItems(); + int size = numItems(); + + float width = 0; + for (int i = 0; i < size; ++i) { + Element* element = listItems[i]; + String text; + Font itemFont = style()->font(); + if (OptionElement* optionElement = toOptionElement(element)) + text = optionElement->textIndentedToRespectGroupLabel(); + else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) { + text = optionGroupElement->groupLabelText(); + FontDescription d = itemFont.fontDescription(); + d.setWeight(d.bolderWeight()); + itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(document()->styleSelector()->fontSelector()); + } + + if (!text.isEmpty()) { + float textWidth = itemFont.floatWidth(TextRun(text.impl(), 0, 0, 0, false, false, false, false)); + width = max(width, textWidth); + } + } + m_optionsWidth = static_cast(ceilf(width)); + m_optionsChanged = false; + + setHasVerticalScrollbar(true); + + setNeedsLayoutAndPrefWidthsRecalc(); + } +} + +void RenderListBox::selectionChanged() +{ + repaint(); + if (!m_inAutoscroll) { + if (m_optionsChanged || needsLayout()) + m_scrollToRevealSelectionAfterLayout = true; + else + scrollToRevealSelection(); + } + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->selectedChildrenChanged(this); +} + +void RenderListBox::layout() +{ + RenderBlock::layout(); + if (m_scrollToRevealSelectionAfterLayout) + scrollToRevealSelection(); +} + +void RenderListBox::scrollToRevealSelection() +{ + SelectElement* select = toSelectElement(static_cast(node())); + + m_scrollToRevealSelectionAfterLayout = false; + + int firstIndex = select->activeSelectionStartListIndex(); + if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex())) + scrollToRevealElementAtListIndex(firstIndex); +} + +void RenderListBox::computePreferredLogicalWidths() +{ + ASSERT(!m_optionsChanged); + + m_minPreferredLogicalWidth = 0; + m_maxPreferredLogicalWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); + else { + m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; + if (m_vBar) + m_maxPreferredLogicalWidth += m_vBar->width(); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPreferredLogicalWidth = 0; + else + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); + } + + int toAdd = borderAndPaddingWidth(); + m_minPreferredLogicalWidth += toAdd; + m_maxPreferredLogicalWidth += toAdd; + + setPreferredLogicalWidthsDirty(false); +} + +int RenderListBox::size() const +{ + int specifiedSize = toSelectElement(static_cast(node()))->size(); + if (specifiedSize > 1) + return max(minSize, specifiedSize); + return min(max(minSize, numItems()), maxDefaultSize); +} + +int RenderListBox::numVisibleItems() const +{ + // Only count fully visible rows. But don't return 0 even if only part of a row shows. + return max(1, (contentHeight() + rowSpacing) / itemHeight()); +} + +int RenderListBox::numItems() const +{ + return toSelectElement(static_cast(node()))->listItems().size(); +} + +int RenderListBox::listHeight() const +{ + return itemHeight() * numItems() - rowSpacing; +} + +void RenderListBox::computeLogicalHeight() +{ + int toAdd = borderAndPaddingHeight(); + + int itemHeight = RenderListBox::itemHeight(); + setHeight(itemHeight * size() - rowSpacing + toAdd); + + RenderBlock::computeLogicalHeight(); + + if (m_vBar) { + bool enabled = numVisibleItems() < numItems(); + m_vBar->setEnabled(enabled); + m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight); + m_vBar->setProportion(numVisibleItems(), numItems()); + if (!enabled) + m_indexOffset = 0; + } +} + +int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const +{ + return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment; +} + +IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index) +{ + return IntRect(tx + borderLeft() + paddingLeft(), + ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), + contentWidth(), itemHeight()); +} + +void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE) + return; + + int listItemsSize = numItems(); + + if (paintInfo.phase == PaintPhaseForeground) { + int index = m_indexOffset; + while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { + paintItemForeground(paintInfo, tx, ty, index); + index++; + } + } + + // Paint the children. + RenderBlock::paintObject(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseBlockBackground) + paintScrollbar(paintInfo, tx, ty); + else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { + int index = m_indexOffset; + while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { + paintItemBackground(paintInfo, tx, ty, index); + index++; + } + } +} + +void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty) +{ + if (m_vBar) { + IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(), + ty + borderTop(), + m_vBar->width(), + height() - (borderTop() + borderBottom())); + m_vBar->setFrameRect(scrollRect); + m_vBar->paint(paintInfo.context, paintInfo.rect); + } +} + +void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) +{ + SelectElement* select = toSelectElement(static_cast(node())); + const Vector& listItems = select->listItems(); + Element* element = listItems[listIndex]; + OptionElement* optionElement = toOptionElement(element); + + String itemText; + if (optionElement) + itemText = optionElement->textIndentedToRespectGroupLabel(); + else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) + itemText = optionGroupElement->groupLabelText(); + + // Determine where the item text should be placed + IntRect r = itemBoundingBoxRect(tx, ty, listIndex); + r.move(optionsSpacingHorizontal, style()->font().ascent()); + + RenderStyle* itemStyle = element->renderStyle(); + if (!itemStyle) + itemStyle = style(); + + Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor); + if (optionElement && optionElement->selected()) { + if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + textColor = theme()->activeListBoxSelectionForegroundColor(); + // Honor the foreground color for disabled items + else if (!element->disabled()) + textColor = theme()->inactiveListBoxSelectionForegroundColor(); + } + + ColorSpace colorSpace = itemStyle->colorSpace(); + paintInfo.context->setFillColor(textColor, colorSpace); + + Font itemFont = style()->font(); + if (isOptionGroupElement(element)) { + FontDescription d = itemFont.fontDescription(); + d.setWeight(d.bolderWeight()); + itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(document()->styleSelector()->fontSelector()); + } + + unsigned length = itemText.length(); + const UChar* string = itemText.characters(); + TextRun textRun(string, length, 0, 0, 0, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override, false, false); + + // Draw the item text + if (itemStyle->visibility() != HIDDEN) + paintInfo.context->drawBidiText(itemFont, textRun, r.location()); +} + +void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex) +{ + SelectElement* select = toSelectElement(static_cast(node())); + const Vector& listItems = select->listItems(); + Element* element = listItems[listIndex]; + OptionElement* optionElement = toOptionElement(element); + + Color backColor; + if (optionElement && optionElement->selected()) { + if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + backColor = theme()->activeListBoxSelectionBackgroundColor(); + else + backColor = theme()->inactiveListBoxSelectionBackgroundColor(); + } else + backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor); + + // Draw the background for this list box item + if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { + ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace(); + IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); + itemRect.intersect(controlClipRect(tx, ty)); + paintInfo.context->fillRect(itemRect, backColor, colorSpace); + } +} + +bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) +{ + if (!m_vBar) + return false; + + IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(), + _ty + borderTop(), + m_vBar->width(), + height() - borderTop() - borderBottom()); + + if (vertRect.contains(_x, _y)) { + result.setScrollbar(m_vBar.get()); + return true; + } + return false; +} + +int RenderListBox::listIndexAtOffset(int offsetX, int offsetY) +{ + if (!numItems()) + return -1; + + if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom()) + return -1; + + int scrollbarWidth = m_vBar ? m_vBar->width() : 0; + if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth) + return -1; + + int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset; + return newOffset < numItems() ? newOffset : -1; +} + +void RenderListBox::panScroll(const IntPoint& panStartMousePosition) +{ + const int maxSpeed = 20; + const int iconRadius = 7; + const int speedReducer = 4; + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absOffset = localToAbsolute(); + + IntPoint currentMousePosition = frame()->eventHandler()->currentMousePosition(); + // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent + static IntPoint previousMousePosition; + if (currentMousePosition.y() < 0) + currentMousePosition = previousMousePosition; + else + previousMousePosition = currentMousePosition; + + int yDelta = currentMousePosition.y() - panStartMousePosition.y(); + + // If the point is too far from the center we limit the speed + yDelta = max(min(yDelta, maxSpeed), -maxSpeed); + + if (abs(yDelta) < iconRadius) // at the center we let the space for the icon + return; + + if (yDelta > 0) + //offsetY = view()->viewHeight(); + absOffset.move(0, listHeight()); + else if (yDelta < 0) + yDelta--; + + // Let's attenuate the speed + yDelta /= speedReducer; + + IntPoint scrollPoint(0, 0); + scrollPoint.setY(absOffset.y() + yDelta); + int newOffset = scrollToward(scrollPoint); + if (newOffset < 0) + return; + + m_inAutoscroll = true; + SelectElement* select = toSelectElement(static_cast(node())); + select->updateListBoxSelection(!select->multiple()); + m_inAutoscroll = false; +} + +int RenderListBox::scrollToward(const IntPoint& destination) +{ + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = localToAbsolute(); + int offsetX = destination.x() - absPos.x(); + int offsetY = destination.y() - absPos.y(); + + int rows = numVisibleItems(); + int offset = m_indexOffset; + + if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1)) + return offset - 1; + + if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows)) + return offset + rows - 1; + + return listIndexAtOffset(offsetX, offsetY); +} + +void RenderListBox::autoscroll() +{ + IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition()); + + int endIndex = scrollToward(pos); + if (endIndex >= 0) { + SelectElement* select = toSelectElement(static_cast(node())); + m_inAutoscroll = true; + + if (!select->multiple()) + select->setActiveSelectionAnchorIndex(endIndex); + + select->setActiveSelectionEndIndex(endIndex); + select->updateListBoxSelection(!select->multiple()); + m_inAutoscroll = false; + } +} + +void RenderListBox::stopAutoscroll() +{ + toSelectElement(static_cast(node()))->listBoxOnChange(); +} + +bool RenderListBox::scrollToRevealElementAtListIndex(int index) +{ + if (index < 0 || index >= numItems() || listIndexIsVisible(index)) + return false; + + int newOffset; + if (index < m_indexOffset) + newOffset = index; + else + newOffset = index - numVisibleItems() + 1; + + m_indexOffset = newOffset; + if (m_vBar) + m_vBar->setValue(m_indexOffset, Scrollbar::NotFromScrollAnimator); + + return true; +} + +bool RenderListBox::listIndexIsVisible(int index) +{ + return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); +} + +bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**) +{ + return m_vBar && m_vBar->scroll(direction, granularity, multiplier); +} + +bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**) +{ + return m_vBar && m_vBar->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier); +} + +void RenderListBox::valueChanged(unsigned listIndex) +{ + Element* element = static_cast(node()); + SelectElement* select = toSelectElement(element); + select->setSelectedIndex(select->listToOptionIndex(listIndex)); + element->dispatchFormControlChangeEvent(); +} + +int RenderListBox::scrollSize(ScrollbarOrientation orientation) const +{ + return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0; +} + +void RenderListBox::setScrollOffsetFromAnimation(const IntPoint& offset) +{ + if (m_vBar) + m_vBar->setValue(offset.y(), Scrollbar::FromScrollAnimator); +} + +void RenderListBox::valueChanged(Scrollbar*) +{ + int newOffset = m_vBar->value(); + if (newOffset != m_indexOffset) { + m_indexOffset = newOffset; + repaint(); + node()->dispatchEvent(Event::create(eventNames().scrollEvent, false, false)); + } +} + +int RenderListBox::itemHeight() const +{ + return style()->font().height() + rowSpacing; +} + +int RenderListBox::verticalScrollbarWidth() const +{ + return m_vBar ? m_vBar->width() : 0; +} + +// FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's +// how the control currently paints. +int RenderListBox::scrollWidth() const +{ + // There is no horizontal scrolling allowed. + return clientWidth(); +} + +int RenderListBox::scrollHeight() const +{ + return max(clientHeight(), listHeight()); +} + +int RenderListBox::scrollLeft() const +{ + return 0; +} + +void RenderListBox::setScrollLeft(int) +{ +} + +int RenderListBox::scrollTop() const +{ + return m_indexOffset * itemHeight(); +} + +void RenderListBox::setScrollTop(int newTop) +{ + // Determine an index and scroll to it. + int index = newTop / itemHeight(); + if (index < 0 || index >= numItems() || index == m_indexOffset) + return; + m_indexOffset = index; + if (m_vBar) + m_vBar->setValue(index, Scrollbar::NotFromScrollAnimator); +} + +bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) + return false; + const Vector& listItems = toSelectElement(static_cast(node()))->listItems(); + int size = numItems(); + tx += this->x(); + ty += this->y(); + for (int i = 0; i < size; ++i) { + if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) { + if (Element* node = listItems[i]) { + result.setInnerNode(node); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(node); + result.setLocalPoint(IntPoint(x - tx, y - ty)); + break; + } + } + } + + return true; +} + +IntRect RenderListBox::controlClipRect(int tx, int ty) const +{ + IntRect clipRect = contentBoxRect(); + clipRect.move(tx, ty); + return clipRect; +} + +bool RenderListBox::isActive() const +{ + Page* page = frame()->page(); + return page && page->focusController()->isActive(); +} + +void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect scrollRect = rect; + scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop()); + repaintRectangle(scrollRect); +} + +IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const +{ + RenderView* view = this->view(); + if (!view) + return scrollbarRect; + + IntRect rect = scrollbarRect; + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + rect.move(scrollbarLeft, scrollbarTop); + + return view->frameView()->convertFromRenderer(this, rect); +} + +IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const +{ + RenderView* view = this->view(); + if (!view) + return parentRect; + + IntRect rect = view->frameView()->convertToRenderer(this, parentRect); + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + rect.move(-scrollbarLeft, -scrollbarTop); + return rect; +} + +IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const +{ + RenderView* view = this->view(); + if (!view) + return scrollbarPoint; + + IntPoint point = scrollbarPoint; + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + point.move(scrollbarLeft, scrollbarTop); + + return view->frameView()->convertFromRenderer(this, point); +} + +IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const +{ + RenderView* view = this->view(); + if (!view) + return parentPoint; + + IntPoint point = view->frameView()->convertToRenderer(this, parentPoint); + + int scrollbarLeft = width() - borderRight() - scrollbar->width(); + int scrollbarTop = borderTop(); + point.move(-scrollbarLeft, -scrollbarTop); + return point; +} + +PassRefPtr RenderListBox::createScrollbar() +{ + RefPtr widget; + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this); + else + widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart)); + document()->view()->addChild(widget.get()); + return widget.release(); +} + +void RenderListBox::destroyScrollbar() +{ + if (!m_vBar) + return; + + m_vBar->removeFromParent(); + m_vBar->setClient(0); + m_vBar = 0; +} + +void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_vBar != 0)) + return; + + if (hasScrollbar) + m_vBar = createScrollbar(); + else + destroyScrollbar(); + + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (document()->hasDashboardRegions()) + document()->setDashboardRegionsDirty(true); +#endif +} + +} // namespace WebCore diff --git a/Source/WebCore/rendering/RenderListBox.h b/Source/WebCore/rendering/RenderListBox.h new file mode 100644 index 0000000..1fbff0d --- /dev/null +++ b/Source/WebCore/rendering/RenderListBox.h @@ -0,0 +1,146 @@ +/* + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderListBox_h +#define RenderListBox_h + +#include "RenderBlock.h" +#include "ScrollbarClient.h" + +namespace WebCore { + +class RenderListBox : public RenderBlock, private ScrollbarClient { +public: + RenderListBox(Element*); + virtual ~RenderListBox(); + + void selectionChanged(); + + void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + + int listIndexAtOffset(int x, int y); + IntRect itemBoundingBoxRect(int tx, int ty, int index); + + bool scrollToRevealElementAtListIndex(int index); + bool listIndexIsVisible(int index); + + int scrollToward(const IntPoint&); // Returns the new index or -1 if no scroll occurred + +private: + virtual const char* renderName() const { return "RenderListBox"; } + + virtual bool isListBox() const { return true; } + + virtual void updateFromElement(); + + virtual bool canHaveChildren() const { return false; } + + virtual bool hasControlClip() const { return true; } + virtual void paintObject(PaintInfo&, int tx, int ty); + virtual IntRect controlClipRect(int tx, int ty) const; + + virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); + + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); + virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0); + + virtual void computePreferredLogicalWidths(); + virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const; + virtual void computeLogicalHeight(); + + virtual void layout(); + + virtual bool canBeProgramaticallyScrolled(bool) const { return true; } + virtual void autoscroll(); + virtual void stopAutoscroll(); + + virtual bool shouldPanScroll() const { return true; } + virtual void panScroll(const IntPoint&); + + virtual int verticalScrollbarWidth() const; + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual int scrollWidth() const; + virtual int scrollHeight() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle); + + // ScrollbarClient interface. + virtual int scrollSize(ScrollbarOrientation orientation) const; + virtual void setScrollOffsetFromAnimation(const IntPoint&); + virtual void valueChanged(Scrollbar*); + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual bool isActive() const; + virtual bool scrollbarCornerPresent() const { return false; } // We don't support resize on list boxes yet. If we did this would have to change. + virtual IntRect convertFromScrollbarToContainingView(const Scrollbar*, const IntRect&) const; + virtual IntRect convertFromContainingViewToScrollbar(const Scrollbar*, const IntRect&) const; + virtual IntPoint convertFromScrollbarToContainingView(const Scrollbar*, const IntPoint&) const; + virtual IntPoint convertFromContainingViewToScrollbar(const Scrollbar*, const IntPoint&) const; + + void setHasVerticalScrollbar(bool hasScrollbar); + PassRefPtr createScrollbar(); + void destroyScrollbar(); + + int itemHeight() const; + void valueChanged(unsigned listIndex); + int size() const; + int numVisibleItems() const; + int numItems() const; + int listHeight() const; + void paintScrollbar(PaintInfo&, int tx, int ty); + void paintItemForeground(PaintInfo&, int tx, int ty, int listIndex); + void paintItemBackground(PaintInfo&, int tx, int ty, int listIndex); + void scrollToRevealSelection(); + + bool m_optionsChanged; + bool m_scrollToRevealSelectionAfterLayout; + bool m_inAutoscroll; + int m_optionsWidth; + int m_indexOffset; + + RefPtr m_vBar; +}; + +inline RenderListBox* toRenderListBox(RenderObject* object) +{ + ASSERT(!object || object->isListBox()); + return static_cast(object); +} + +// This will catch anyone doing an unnecessary cast. +void toRenderListBox(const RenderListBox*); + +} // namepace WebCore + +#endif // RenderListBox_h diff --git a/Source/WebCore/rendering/RenderListItem.cpp b/Source/WebCore/rendering/RenderListItem.cpp new file mode 100644 index 0000000..65606f3 --- /dev/null +++ b/Source/WebCore/rendering/RenderListItem.cpp @@ -0,0 +1,459 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderListItem.h" + +#include "CachedImage.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "RenderListMarker.h" +#include "RenderView.h" +#include + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderListItem::RenderListItem(Node* node) + : RenderBlock(node) + , m_marker(0) + , m_hasExplicitValue(false) + , m_isValueUpToDate(false) + , m_notInList(false) +{ + setInline(false); +} + +void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (style()->listStyleType() != NoneListStyle + || (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { + RefPtr newStyle = RenderStyle::create(); + // The marker always inherits from the list item, regardless of where it might end + // up (e.g., in some deeply nested line box). See CSS3 spec. + newStyle->inheritFrom(style()); + if (!m_marker) + m_marker = new (renderArena()) RenderListMarker(this); + m_marker->setStyle(newStyle.release()); + } else if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } +} + +void RenderListItem::destroy() +{ + if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } + RenderBlock::destroy(); +} + +static bool isList(Node* node) +{ + return (node->hasTagName(ulTag) || node->hasTagName(olTag)); +} + +static Node* enclosingList(const RenderListItem* listItem) +{ + Node* firstNode = 0; + + for (const RenderObject* renderer = listItem->parent(); renderer; renderer = renderer->parent()) { + Node* node = renderer->node(); + if (node) { + if (isList(node)) + return node; + if (!firstNode) + firstNode = node; + } + } + + // If there's no actual