summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering')
-rw-r--r--Source/WebCore/rendering/AutoTableLayout.cpp750
-rw-r--r--Source/WebCore/rendering/AutoTableLayout.h78
-rw-r--r--Source/WebCore/rendering/BidiRun.cpp74
-rw-r--r--Source/WebCore/rendering/BidiRun.h67
-rw-r--r--Source/WebCore/rendering/ColumnInfo.h102
-rw-r--r--Source/WebCore/rendering/CounterNode.cpp272
-rw-r--r--Source/WebCore/rendering/CounterNode.h100
-rw-r--r--Source/WebCore/rendering/EllipsisBox.cpp126
-rw-r--r--Source/WebCore/rendering/EllipsisBox.h60
-rw-r--r--Source/WebCore/rendering/FixedTableLayout.cpp330
-rw-r--r--Source/WebCore/rendering/FixedTableLayout.h47
-rw-r--r--Source/WebCore/rendering/GapRects.h62
-rw-r--r--Source/WebCore/rendering/HitTestRequest.h58
-rw-r--r--Source/WebCore/rendering/HitTestResult.cpp579
-rw-r--r--Source/WebCore/rendering/HitTestResult.h157
-rw-r--r--Source/WebCore/rendering/InlineBox.cpp321
-rw-r--r--Source/WebCore/rendering/InlineBox.h379
-rw-r--r--Source/WebCore/rendering/InlineFlowBox.cpp1358
-rw-r--r--Source/WebCore/rendering/InlineFlowBox.h272
-rw-r--r--Source/WebCore/rendering/InlineIterator.h266
-rw-r--r--Source/WebCore/rendering/InlineTextBox.cpp1292
-rw-r--r--Source/WebCore/rendering/InlineTextBox.h178
-rw-r--r--Source/WebCore/rendering/LayoutState.cpp177
-rw-r--r--Source/WebCore/rendering/LayoutState.h104
-rw-r--r--Source/WebCore/rendering/MediaControlElements.cpp876
-rw-r--r--Source/WebCore/rendering/MediaControlElements.h357
-rw-r--r--Source/WebCore/rendering/OverlapTestRequestClient.h39
-rw-r--r--Source/WebCore/rendering/PaintInfo.h111
-rw-r--r--Source/WebCore/rendering/PaintPhase.h66
-rw-r--r--Source/WebCore/rendering/PointerEventsHitRules.cpp111
-rw-r--r--Source/WebCore/rendering/PointerEventsHitRules.h49
-rw-r--r--Source/WebCore/rendering/RenderApplet.cpp110
-rw-r--r--Source/WebCore/rendering/RenderApplet.h69
-rw-r--r--Source/WebCore/rendering/RenderArena.cpp146
-rw-r--r--Source/WebCore/rendering/RenderArena.h69
-rw-r--r--Source/WebCore/rendering/RenderBR.cpp81
-rw-r--r--Source/WebCore/rendering/RenderBR.h82
-rw-r--r--Source/WebCore/rendering/RenderBlock.cpp6111
-rw-r--r--Source/WebCore/rendering/RenderBlock.h739
-rw-r--r--Source/WebCore/rendering/RenderBlockLineLayout.cpp2244
-rw-r--r--Source/WebCore/rendering/RenderBox.cpp3428
-rw-r--r--Source/WebCore/rendering/RenderBox.h523
-rw-r--r--Source/WebCore/rendering/RenderBoxModelObject.cpp1858
-rw-r--r--Source/WebCore/rendering/RenderBoxModelObject.h167
-rw-r--r--Source/WebCore/rendering/RenderButton.cpp197
-rw-r--r--Source/WebCore/rendering/RenderButton.h95
-rw-r--r--Source/WebCore/rendering/RenderCounter.cpp496
-rw-r--r--Source/WebCore/rendering/RenderCounter.h70
-rw-r--r--Source/WebCore/rendering/RenderDataGrid.cpp261
-rw-r--r--Source/WebCore/rendering/RenderDataGrid.h88
-rw-r--r--Source/WebCore/rendering/RenderDetails.cpp40
-rw-r--r--Source/WebCore/rendering/RenderDetails.h49
-rw-r--r--Source/WebCore/rendering/RenderDetailsMarker.cpp31
-rw-r--r--Source/WebCore/rendering/RenderDetailsMarker.h49
-rw-r--r--Source/WebCore/rendering/RenderEmbeddedObject.cpp309
-rw-r--r--Source/WebCore/rendering/RenderEmbeddedObject.h91
-rw-r--r--Source/WebCore/rendering/RenderFieldset.cpp223
-rw-r--r--Source/WebCore/rendering/RenderFieldset.h64
-rw-r--r--Source/WebCore/rendering/RenderFileUploadControl.cpp321
-rw-r--r--Source/WebCore/rendering/RenderFileUploadControl.h96
-rw-r--r--Source/WebCore/rendering/RenderFlexibleBox.cpp1108
-rw-r--r--Source/WebCore/rendering/RenderFlexibleBox.h71
-rw-r--r--Source/WebCore/rendering/RenderForeignObject.cpp174
-rw-r--r--Source/WebCore/rendering/RenderForeignObject.h76
-rw-r--r--Source/WebCore/rendering/RenderFrame.cpp91
-rw-r--r--Source/WebCore/rendering/RenderFrame.h60
-rw-r--r--Source/WebCore/rendering/RenderFrameBase.cpp93
-rw-r--r--Source/WebCore/rendering/RenderFrameBase.h44
-rw-r--r--Source/WebCore/rendering/RenderFrameSet.cpp890
-rw-r--r--Source/WebCore/rendering/RenderFrameSet.h148
-rw-r--r--Source/WebCore/rendering/RenderFullScreen.cpp70
-rw-r--r--Source/WebCore/rendering/RenderFullScreen.h61
-rw-r--r--Source/WebCore/rendering/RenderHTMLCanvas.cpp89
-rw-r--r--Source/WebCore/rendering/RenderHTMLCanvas.h61
-rw-r--r--Source/WebCore/rendering/RenderIFrame.cpp159
-rw-r--r--Source/WebCore/rendering/RenderIFrame.h76
-rw-r--r--Source/WebCore/rendering/RenderImage.cpp572
-rw-r--r--Source/WebCore/rendering/RenderImage.h119
-rw-r--r--Source/WebCore/rendering/RenderImageResource.cpp106
-rw-r--r--Source/WebCore/rendering/RenderImageResource.h79
-rw-r--r--Source/WebCore/rendering/RenderImageResourceStyleImage.cpp63
-rw-r--r--Source/WebCore/rendering/RenderImageResourceStyleImage.h68
-rw-r--r--Source/WebCore/rendering/RenderIndicator.cpp63
-rw-r--r--Source/WebCore/rendering/RenderIndicator.h48
-rw-r--r--Source/WebCore/rendering/RenderInline.cpp1155
-rw-r--r--Source/WebCore/rendering/RenderInline.h177
-rw-r--r--Source/WebCore/rendering/RenderInputSpeech.cpp99
-rw-r--r--Source/WebCore/rendering/RenderInputSpeech.h49
-rw-r--r--Source/WebCore/rendering/RenderLayer.cpp4058
-rw-r--r--Source/WebCore/rendering/RenderLayer.h771
-rw-r--r--Source/WebCore/rendering/RenderLayerBacking.cpp1366
-rw-r--r--Source/WebCore/rendering/RenderLayerBacking.h208
-rw-r--r--Source/WebCore/rendering/RenderLayerCompositor.cpp1668
-rw-r--r--Source/WebCore/rendering/RenderLayerCompositor.h284
-rw-r--r--Source/WebCore/rendering/RenderLineBoxList.cpp389
-rw-r--r--Source/WebCore/rendering/RenderLineBoxList.h90
-rw-r--r--Source/WebCore/rendering/RenderListBox.cpp743
-rw-r--r--Source/WebCore/rendering/RenderListBox.h146
-rw-r--r--Source/WebCore/rendering/RenderListItem.cpp459
-rw-r--r--Source/WebCore/rendering/RenderListItem.h98
-rw-r--r--Source/WebCore/rendering/RenderListMarker.cpp1720
-rw-r--r--Source/WebCore/rendering/RenderListMarker.h101
-rw-r--r--Source/WebCore/rendering/RenderMarquee.cpp319
-rw-r--r--Source/WebCore/rendering/RenderMarquee.h99
-rw-r--r--Source/WebCore/rendering/RenderMedia.cpp650
-rw-r--r--Source/WebCore/rendering/RenderMedia.h169
-rw-r--r--Source/WebCore/rendering/RenderMediaControls.cpp187
-rw-r--r--Source/WebCore/rendering/RenderMediaControls.h48
-rw-r--r--Source/WebCore/rendering/RenderMediaControlsChromium.cpp324
-rw-r--r--Source/WebCore/rendering/RenderMediaControlsChromium.h46
-rw-r--r--Source/WebCore/rendering/RenderMenuList.cpp562
-rw-r--r--Source/WebCore/rendering/RenderMenuList.h150
-rw-r--r--Source/WebCore/rendering/RenderMeter.cpp261
-rw-r--r--Source/WebCore/rendering/RenderMeter.h80
-rw-r--r--Source/WebCore/rendering/RenderObject.cpp2776
-rw-r--r--Source/WebCore/rendering/RenderObject.h1050
-rw-r--r--Source/WebCore/rendering/RenderObjectChildList.cpp472
-rw-r--r--Source/WebCore/rendering/RenderObjectChildList.h68
-rw-r--r--Source/WebCore/rendering/RenderOverflow.h163
-rw-r--r--Source/WebCore/rendering/RenderPart.cpp61
-rw-r--r--Source/WebCore/rendering/RenderPart.h55
-rw-r--r--Source/WebCore/rendering/RenderProgress.cpp149
-rw-r--r--Source/WebCore/rendering/RenderProgress.h83
-rw-r--r--Source/WebCore/rendering/RenderReplaced.cpp412
-rw-r--r--Source/WebCore/rendering/RenderReplaced.h85
-rw-r--r--Source/WebCore/rendering/RenderReplica.cpp82
-rw-r--r--Source/WebCore/rendering/RenderReplica.h57
-rw-r--r--Source/WebCore/rendering/RenderRuby.cpp204
-rw-r--r--Source/WebCore/rendering/RenderRuby.h87
-rw-r--r--Source/WebCore/rendering/RenderRubyBase.cpp185
-rw-r--r--Source/WebCore/rendering/RenderRubyBase.h63
-rw-r--r--Source/WebCore/rendering/RenderRubyRun.cpp284
-rw-r--r--Source/WebCore/rendering/RenderRubyRun.h84
-rw-r--r--Source/WebCore/rendering/RenderRubyText.cpp51
-rw-r--r--Source/WebCore/rendering/RenderRubyText.h52
-rw-r--r--Source/WebCore/rendering/RenderSVGAllInOne.cpp76
-rw-r--r--Source/WebCore/rendering/RenderSVGBlock.cpp105
-rw-r--r--Source/WebCore/rendering/RenderSVGBlock.h50
-rw-r--r--Source/WebCore/rendering/RenderSVGContainer.cpp184
-rw-r--r--Source/WebCore/rendering/RenderSVGContainer.h101
-rw-r--r--Source/WebCore/rendering/RenderSVGGradientStop.cpp83
-rw-r--r--Source/WebCore/rendering/RenderSVGGradientStop.h68
-rw-r--r--Source/WebCore/rendering/RenderSVGHiddenContainer.cpp62
-rw-r--r--Source/WebCore/rendering/RenderSVGHiddenContainer.h58
-rw-r--r--Source/WebCore/rendering/RenderSVGImage.cpp195
-rw-r--r--Source/WebCore/rendering/RenderSVGImage.h99
-rw-r--r--Source/WebCore/rendering/RenderSVGModelObject.cpp117
-rw-r--r--Source/WebCore/rendering/RenderSVGModelObject.h76
-rw-r--r--Source/WebCore/rendering/RenderSVGResource.cpp162
-rw-r--r--Source/WebCore/rendering/RenderSVGResource.h90
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceClipper.cpp350
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceClipper.h79
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceContainer.cpp193
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceContainer.h95
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceFilter.cpp327
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceFilter.h94
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.cpp105
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.h58
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceGradient.cpp282
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceGradient.h78
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceLinearGradient.cpp67
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceLinearGradient.h55
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceMarker.cpp177
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceMarker.h83
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceMasker.cpp224
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceMasker.h71
-rw-r--r--Source/WebCore/rendering/RenderSVGResourcePattern.cpp293
-rw-r--r--Source/WebCore/rendering/RenderSVGResourcePattern.h76
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceRadialGradient.cpp72
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceRadialGradient.h55
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceSolidColor.cpp93
-rw-r--r--Source/WebCore/rendering/RenderSVGResourceSolidColor.h56
-rw-r--r--Source/WebCore/rendering/RenderSVGRoot.cpp361
-rw-r--r--Source/WebCore/rendering/RenderSVGRoot.h120
-rw-r--r--Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp108
-rw-r--r--Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.h54
-rw-r--r--Source/WebCore/rendering/RenderSVGTransformableContainer.cpp66
-rw-r--r--Source/WebCore/rendering/RenderSVGTransformableContainer.h47
-rw-r--r--Source/WebCore/rendering/RenderSVGViewportContainer.cpp94
-rw-r--r--Source/WebCore/rendering/RenderSVGViewportContainer.h66
-rw-r--r--Source/WebCore/rendering/RenderScrollbar.cpp366
-rw-r--r--Source/WebCore/rendering/RenderScrollbar.h97
-rw-r--r--Source/WebCore/rendering/RenderScrollbarPart.cpp183
-rw-r--r--Source/WebCore/rendering/RenderScrollbarPart.h68
-rw-r--r--Source/WebCore/rendering/RenderScrollbarTheme.cpp140
-rw-r--r--Source/WebCore/rendering/RenderScrollbarTheme.h82
-rw-r--r--Source/WebCore/rendering/RenderSelectionInfo.h104
-rw-r--r--Source/WebCore/rendering/RenderSlider.cpp337
-rw-r--r--Source/WebCore/rendering/RenderSlider.h83
-rw-r--r--Source/WebCore/rendering/RenderSummary.cpp31
-rw-r--r--Source/WebCore/rendering/RenderSummary.h49
-rw-r--r--Source/WebCore/rendering/RenderTable.cpp1249
-rw-r--r--Source/WebCore/rendering/RenderTable.h287
-rw-r--r--Source/WebCore/rendering/RenderTableCell.cpp1068
-rw-r--r--Source/WebCore/rendering/RenderTableCell.h177
-rw-r--r--Source/WebCore/rendering/RenderTableCol.cpp107
-rw-r--r--Source/WebCore/rendering/RenderTableCol.h85
-rw-r--r--Source/WebCore/rendering/RenderTableRow.cpp217
-rw-r--r--Source/WebCore/rendering/RenderTableRow.h86
-rw-r--r--Source/WebCore/rendering/RenderTableSection.cpp1320
-rw-r--r--Source/WebCore/rendering/RenderTableSection.h189
-rw-r--r--Source/WebCore/rendering/RenderText.cpp1539
-rw-r--r--Source/WebCore/rendering/RenderText.h206
-rw-r--r--Source/WebCore/rendering/RenderTextControl.cpp629
-rw-r--r--Source/WebCore/rendering/RenderTextControl.h143
-rw-r--r--Source/WebCore/rendering/RenderTextControlMultiLine.cpp163
-rw-r--r--Source/WebCore/rendering/RenderTextControlMultiLine.h69
-rw-r--r--Source/WebCore/rendering/RenderTextControlSingleLine.cpp1151
-rw-r--r--Source/WebCore/rendering/RenderTextControlSingleLine.h183
-rw-r--r--Source/WebCore/rendering/RenderTextFragment.cpp117
-rw-r--r--Source/WebCore/rendering/RenderTextFragment.h84
-rw-r--r--Source/WebCore/rendering/RenderTheme.cpp1134
-rw-r--r--Source/WebCore/rendering/RenderTheme.h334
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumLinux.cpp195
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumLinux.h87
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumMac.h60
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumMac.mm163
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumSkia.cpp888
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumSkia.h170
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumWin.cpp770
-rw-r--r--Source/WebCore/rendering/RenderThemeChromiumWin.h123
-rw-r--r--Source/WebCore/rendering/RenderThemeMac.h237
-rw-r--r--Source/WebCore/rendering/RenderThemeMac.mm2064
-rw-r--r--Source/WebCore/rendering/RenderThemeSafari.cpp1215
-rw-r--r--Source/WebCore/rendering/RenderThemeSafari.h186
-rw-r--r--Source/WebCore/rendering/RenderThemeWin.cpp1118
-rw-r--r--Source/WebCore/rendering/RenderThemeWin.h183
-rw-r--r--Source/WebCore/rendering/RenderThemeWinCE.cpp647
-rw-r--r--Source/WebCore/rendering/RenderThemeWinCE.h142
-rw-r--r--Source/WebCore/rendering/RenderTreeAsText.cpp796
-rw-r--r--Source/WebCore/rendering/RenderTreeAsText.h102
-rw-r--r--Source/WebCore/rendering/RenderVideo.cpp284
-rw-r--r--Source/WebCore/rendering/RenderVideo.h94
-rw-r--r--Source/WebCore/rendering/RenderView.cpp802
-rw-r--r--Source/WebCore/rendering/RenderView.h334
-rw-r--r--Source/WebCore/rendering/RenderWidget.cpp409
-rw-r--r--Source/WebCore/rendering/RenderWidget.h99
-rw-r--r--Source/WebCore/rendering/RenderWidgetProtector.h53
-rw-r--r--Source/WebCore/rendering/RenderWordBreak.cpp49
-rw-r--r--Source/WebCore/rendering/RenderWordBreak.h46
-rw-r--r--Source/WebCore/rendering/RenderingAllInOne.cpp115
-rw-r--r--Source/WebCore/rendering/RootInlineBox.cpp557
-rw-r--r--Source/WebCore/rendering/RootInlineBox.h180
-rw-r--r--Source/WebCore/rendering/SVGImageBufferTools.cpp128
-rw-r--r--Source/WebCore/rendering/SVGImageBufferTools.h53
-rw-r--r--Source/WebCore/rendering/SVGMarkerData.h134
-rw-r--r--Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp123
-rw-r--r--Source/WebCore/rendering/SVGMarkerLayoutInfo.h74
-rw-r--r--Source/WebCore/rendering/SVGRenderSupport.cpp352
-rw-r--r--Source/WebCore/rendering/SVGRenderSupport.h84
-rw-r--r--Source/WebCore/rendering/SVGRenderTreeAsText.cpp748
-rw-r--r--Source/WebCore/rendering/SVGRenderTreeAsText.h95
-rw-r--r--Source/WebCore/rendering/SVGResources.cpp684
-rw-r--r--Source/WebCore/rendering/SVGResources.h179
-rw-r--r--Source/WebCore/rendering/SVGResourcesCache.cpp165
-rw-r--r--Source/WebCore/rendering/SVGResourcesCache.h65
-rw-r--r--Source/WebCore/rendering/SVGResourcesCycleSolver.cpp213
-rw-r--r--Source/WebCore/rendering/SVGResourcesCycleSolver.h51
-rw-r--r--Source/WebCore/rendering/SVGShadowTreeElements.cpp90
-rw-r--r--Source/WebCore/rendering/SVGShadowTreeElements.h67
-rw-r--r--Source/WebCore/rendering/ScrollBehavior.cpp55
-rw-r--r--Source/WebCore/rendering/ScrollBehavior.h78
-rw-r--r--Source/WebCore/rendering/ShadowElement.cpp121
-rw-r--r--Source/WebCore/rendering/ShadowElement.h86
-rw-r--r--Source/WebCore/rendering/TableLayout.h48
-rw-r--r--Source/WebCore/rendering/TextControlInnerElements.cpp505
-rw-r--r--Source/WebCore/rendering/TextControlInnerElements.h159
-rw-r--r--Source/WebCore/rendering/TrailingFloatsRootInlineBox.h47
-rw-r--r--Source/WebCore/rendering/TransformState.cpp175
-rw-r--r--Source/WebCore/rendering/TransformState.h135
-rw-r--r--Source/WebCore/rendering/VerticalPositionCache.h70
-rw-r--r--Source/WebCore/rendering/break_lines.cpp201
-rw-r--r--Source/WebCore/rendering/break_lines.h39
-rw-r--r--Source/WebCore/rendering/style/BorderData.h129
-rw-r--r--Source/WebCore/rendering/style/BorderValue.h79
-rw-r--r--Source/WebCore/rendering/style/CollapsedBorderValue.h72
-rw-r--r--Source/WebCore/rendering/style/ContentData.cpp79
-rw-r--r--Source/WebCore/rendering/style/ContentData.h112
-rw-r--r--Source/WebCore/rendering/style/CounterContent.h62
-rw-r--r--Source/WebCore/rendering/style/CounterDirectives.cpp38
-rw-r--r--Source/WebCore/rendering/style/CounterDirectives.h54
-rw-r--r--Source/WebCore/rendering/style/CursorData.h63
-rw-r--r--Source/WebCore/rendering/style/CursorList.h62
-rw-r--r--Source/WebCore/rendering/style/DataRef.h71
-rw-r--r--Source/WebCore/rendering/style/FillLayer.cpp284
-rw-r--r--Source/WebCore/rendering/style/FillLayer.h201
-rw-r--r--Source/WebCore/rendering/style/KeyframeList.cpp97
-rw-r--r--Source/WebCore/rendering/style/KeyframeList.h99
-rw-r--r--Source/WebCore/rendering/style/LineClampValue.h69
-rw-r--r--Source/WebCore/rendering/style/NinePieceImage.cpp35
-rw-r--r--Source/WebCore/rendering/style/NinePieceImage.h78
-rw-r--r--Source/WebCore/rendering/style/OutlineValue.h61
-rw-r--r--Source/WebCore/rendering/style/RenderStyle.cpp1473
-rw-r--r--Source/WebCore/rendering/style/RenderStyle.h1409
-rw-r--r--Source/WebCore/rendering/style/RenderStyleConstants.h434
-rw-r--r--Source/WebCore/rendering/style/SVGRenderStyle.cpp220
-rw-r--r--Source/WebCore/rendering/style/SVGRenderStyle.h431
-rw-r--r--Source/WebCore/rendering/style/SVGRenderStyleDefs.cpp227
-rw-r--r--Source/WebCore/rendering/style/SVGRenderStyleDefs.h266
-rw-r--r--Source/WebCore/rendering/style/ShadowData.cpp100
-rw-r--r--Source/WebCore/rendering/style/ShadowData.h101
-rw-r--r--Source/WebCore/rendering/style/StyleAllInOne.cpp49
-rw-r--r--Source/WebCore/rendering/style/StyleBackgroundData.cpp49
-rw-r--r--Source/WebCore/rendering/style/StyleBackgroundData.h65
-rw-r--r--Source/WebCore/rendering/style/StyleBoxData.cpp68
-rw-r--r--Source/WebCore/rendering/style/StyleBoxData.h86
-rw-r--r--Source/WebCore/rendering/style/StyleCachedImage.cpp92
-rw-r--r--Source/WebCore/rendering/style/StyleCachedImage.h67
-rw-r--r--Source/WebCore/rendering/style/StyleDashboardRegion.h61
-rw-r--r--Source/WebCore/rendering/style/StyleFlexibleBoxData.cpp59
-rw-r--r--Source/WebCore/rendering/style/StyleFlexibleBoxData.h60
-rw-r--r--Source/WebCore/rendering/style/StyleGeneratedImage.cpp80
-rw-r--r--Source/WebCore/rendering/style/StyleGeneratedImage.h69
-rw-r--r--Source/WebCore/rendering/style/StyleImage.h84
-rw-r--r--Source/WebCore/rendering/style/StyleInheritedData.cpp65
-rw-r--r--Source/WebCore/rendering/style/StyleInheritedData.h69
-rw-r--r--Source/WebCore/rendering/style/StyleMarqueeData.cpp54
-rw-r--r--Source/WebCore/rendering/style/StyleMarqueeData.h61
-rw-r--r--Source/WebCore/rendering/style/StyleMultiColData.cpp67
-rw-r--r--Source/WebCore/rendering/style/StyleMultiColData.h76
-rw-r--r--Source/WebCore/rendering/style/StylePendingImage.h71
-rw-r--r--Source/WebCore/rendering/style/StyleRareInheritedData.cpp187
-rw-r--r--Source/WebCore/rendering/style/StyleRareInheritedData.h114
-rw-r--r--Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp203
-rw-r--r--Source/WebCore/rendering/style/StyleRareNonInheritedData.h140
-rw-r--r--Source/WebCore/rendering/style/StyleReflection.h70
-rw-r--r--Source/WebCore/rendering/style/StyleSurroundData.cpp47
-rw-r--r--Source/WebCore/rendering/style/StyleSurroundData.h58
-rw-r--r--Source/WebCore/rendering/style/StyleTransformData.cpp51
-rw-r--r--Source/WebCore/rendering/style/StyleTransformData.h58
-rw-r--r--Source/WebCore/rendering/style/StyleVisualData.cpp49
-rw-r--r--Source/WebCore/rendering/style/StyleVisualData.h62
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGInline.cpp124
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGInline.h68
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGInlineText.cpp212
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGInlineText.h84
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGPath.cpp338
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGPath.h105
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGTSpan.cpp38
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGTSpan.h38
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGText.cpp260
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGText.h98
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGTextPath.cpp85
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGTextPath.h66
-rw-r--r--Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp144
-rw-r--r--Source/WebCore/rendering/svg/SVGInlineFlowBox.h61
-rw-r--r--Source/WebCore/rendering/svg/SVGInlineTextBox.cpp608
-rw-r--r--Source/WebCore/rendering/svg/SVGInlineTextBox.h92
-rw-r--r--Source/WebCore/rendering/svg/SVGRootInlineBox.cpp222
-rw-r--r--Source/WebCore/rendering/svg/SVGRootInlineBox.h71
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunk.cpp93
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunk.h68
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp232
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunkBuilder.h64
-rw-r--r--Source/WebCore/rendering/svg/SVGTextFragment.h57
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp100
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h69
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp308
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h83
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp579
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngine.h94
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp234
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h54
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp96
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h52
-rw-r--r--Source/WebCore/rendering/svg/SVGTextMetrics.cpp115
-rw-r--r--Source/WebCore/rendering/svg/SVGTextMetrics.h78
-rw-r--r--Source/WebCore/rendering/svg/SVGTextQuery.cpp562
-rw-r--r--Source/WebCore/rendering/svg/SVGTextQuery.h75
369 files changed, 103531 insertions, 0 deletions
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<float>(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<int>(min(maxNonPercent, INT_MAX / 2.0f)));
+ maxWidth = max(maxWidth, static_cast<int>(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:
+ // <table border=2 width=100%><
+ // <tr><td>1</td><td colspan=2>2-3</tr>
+ // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
+ // </table>
+ 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<int>(percentMissing * static_cast<float>(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<int>(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast<float>(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<int>(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast<float>(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<int>(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<int>(available * static_cast<float>(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<int>(available * static_cast<float>(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 <wtf/Vector.h>
+
+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<Layout, 4> m_layoutStruct;
+ Vector<RenderTableCell*, 4> 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 <wtf/RefCountedLeakCounter.h>
+
+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<size_t*>(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 <wtf/StdLibExtras.h>
+#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<BidiRun*>(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 <wtf/Vector.h>
+#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 <stdio.h>
+
+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> 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 <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
+
+// 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<CounterNode> {
+public:
+ static PassRefPtr<CounterNode> 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 <col> 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<int>::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:
+ /*
+ <table style="width:100%; background-color:red"><tr><td>
+ <table style="background-color:blue"><tr><td>
+ <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
+ Content
+ </td></tr></table>
+ </td></tr></table>
+ </td></tr></table>
+ */
+ // 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<int> 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 <wtf/Vector.h>
+
+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<Length> 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 <area> tags in image maps, walk the tree for the <area>, not the <img> using it.
+ for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) {
+ if (titleNode->isElementNode()) {
+ String title = static_cast<Element*>(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<HTMLImageElement*>(m_innerNonSharedNode.get());
+ return displayString(image->getAttribute(altAttr), m_innerNonSharedNode.get());
+ }
+
+ if (m_innerNonSharedNode->hasTagName(inputTag)) {
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get());
+ return displayString(input->alt(), m_innerNonSharedNode.get());
+ }
+
+#if ENABLE(WML)
+ if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) {
+ WMLImageElement* image = static_cast<WMLImageElement*>(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<WebCore::RenderImage*>(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<Element*>(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<HTMLMediaElement*>(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<HTMLVideoElement*>(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<HTMLAnchorElement*>(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<HTMLInputElement*>(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<RefPtr<Node> >& list = other.rectBasedTestResult();
+ ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
+ for (ListHashSet<RefPtr<Node> >::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 <wtf/Forward.h>
+#include <wtf/ListHashSet.h>
+#include <wtf/RefPtr.h>
+
+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<RefPtr<Node> >& rectBasedTestResult() const { return m_rectBasedTestResult; }
+ void append(const HitTestResult&);
+
+private:
+
+#if ENABLE(VIDEO)
+ HTMLMediaElement* mediaElement() const;
+#endif
+
+ RefPtr<Node> m_innerNode;
+ RefPtr<Node> 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<Element> m_innerURLElement;
+ RefPtr<Scrollbar> 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<RefPtr<Node> > 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<const RootInlineBox*>(this);
+}
+
+RootInlineBox* InlineBox::root()
+{
+ if (m_parent)
+ return m_parent->root();
+ ASSERT(isRootInlineBox());
+ return static_cast<RootInlineBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(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 <br>.
+ bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> 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 <math.h>
+
+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<InlineFlowBox*>(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<InlineFlowBox*>(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<InlineTextBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(curr)->requiresIdeographicBaseline(textBoxDataMap))
+ return true;
+ } else {
+ if (curr->renderer()->style(m_firstLine)->font().primaryFont()->orientation() == Vertical)
+ return true;
+
+ const Vector<const SimpleFontData*>* usedFonts = 0;
+ if (curr->isInlineTextBox()) {
+ GlyphOverflowAndFallbackFontsMap::const_iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(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<InlineFlowBox*>(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<int>(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<const SimpleFontData*>* usedFonts = 0;
+ if (curr->isInlineTextBox()) {
+ GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(static_cast<InlineTextBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(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<RenderRubyRun*>(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<InlineTextBox*>(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<InlineFlowBox*>(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<int>(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<InlineTextBox*>(curr);
+ RenderText* rt = toRenderText(text->renderer());
+ if (rt->isBR())
+ continue;
+ addTextBoxVisualOverflow(text, textBoxDataMap, logicalVisualOverflow);
+ } else if (curr->renderer()->isRenderInline()) {
+ InlineFlowBox* flow = static_cast<InlineFlowBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(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<InlineFlowBox*>(curr)->computeOverAnnotationAdjustment(allowedPosition));
+
+ if (curr->renderer()->isReplaced() && curr->renderer()->isRubyRun()) {
+ RenderRubyRun* rubyRun = static_cast<RenderRubyRun*>(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<InlineTextBox*>(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<InlineFlowBox*>(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<const InlineTextBox*, pair<Vector<const SimpleFontData*>, 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<RenderOverflow> 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 <wtf/AlwaysInline.h>
+#include <wtf/StdLibExtras.h>
+
+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 <wtf/AlwaysInline.h>
+
+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<UChar, 256> 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<RenderRubyRun*>(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<float>(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<int>(sPos, m_truncation);
+ ePos = min<int>(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<CompositionUnderline>& 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<int>(marker.startOffset - m_start, 0);
+ int endPosition = min<int>(marker.endOffset - m_start, m_len);
+
+ if (m_truncation != cNoTruncation)
+ endPosition = min<int>(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<DocumentMarker> markers = renderer()->document()->markers()->markersForNode(renderer()->node());
+ Vector<DocumentMarker>::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: <http://bugs.webkit.org/show_bug.cgi?id=13443> 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 <wtf/Noncopyable.h>
+
+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<HTMLMediaElement*>(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> MediaControlShadowRootElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlShadowRootElement> element = adoptRef(new MediaControlShadowRootElement(mediaElement));
+
+ RefPtr<RenderStyle> 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> 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<RenderStyle> 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<RenderStyle> 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<RenderStyle> 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> 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> MediaControlVolumeSliderContainerElement::create(HTMLMediaElement* mediaElement)
+{
+ return adoptRef(new MediaControlVolumeSliderContainerElement(mediaElement));
+}
+
+PassRefPtr<RenderStyle> MediaControlVolumeSliderContainerElement::styleForElement()
+{
+ RefPtr<RenderStyle> 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> 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<RenderStyle> 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<RenderStyle> 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<RenderStyle> 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> MediaControlMuteButtonElement::create(HTMLMediaElement* mediaElement, ButtonLocation location)
+{
+ RefPtr<MediaControlMuteButtonElement> 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> MediaControlPlayButtonElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlPlayButtonElement> 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> MediaControlSeekButtonElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
+{
+ RefPtr<MediaControlSeekButtonElement> 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<MediaControlSeekButtonElement>*)
+{
+ 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> MediaControlRewindButtonElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlRewindButtonElement> 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> MediaControlReturnToRealtimeButtonElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlReturnToRealtimeButtonElement> 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> MediaControlToggleClosedCaptionsButtonElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlToggleClosedCaptionsButtonElement> 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> MediaControlTimelineElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlTimelineElement> 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<MouseEvent*>(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> MediaControlVolumeSliderElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlVolumeSliderElement> 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<MouseEvent*>(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> MediaControlFullscreenButtonElement::create(HTMLMediaElement* mediaElement)
+{
+ RefPtr<MediaControlFullscreenButtonElement> 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> MediaControlTimeDisplayElement::create(HTMLMediaElement* mediaElement, PseudoId pseudoStyleId)
+{
+ return adoptRef(new MediaControlTimeDisplayElement(mediaElement, pseudoStyleId));
+}
+
+PassRefPtr<RenderStyle> MediaControlTimeDisplayElement::styleForElement()
+{
+ RefPtr<RenderStyle> 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<RenderStyle> 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<MediaControlShadowRootElement> create(HTMLMediaElement*);
+
+ void updateStyle();
+ virtual void detach();
+
+private:
+ MediaControlShadowRootElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlElement : public HTMLDivElement {
+public:
+ static PassRefPtr<MediaControlElement> 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<RenderStyle> 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<MediaControlTimelineContainerElement> create(HTMLMediaElement*);
+
+private:
+ MediaControlTimelineContainerElement(HTMLMediaElement*);
+ virtual bool rendererIsNeeded(RenderStyle*);
+};
+
+// ----------------------------
+
+class MediaControlVolumeSliderContainerElement : public MediaControlElement {
+public:
+ static PassRefPtr<MediaControlVolumeSliderContainerElement> create(HTMLMediaElement*);
+
+ virtual PassRefPtr<RenderStyle> 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<MediaControlStatusDisplayElement> 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<RenderStyle> 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<MediaControlMuteButtonElement> create(HTMLMediaElement*, ButtonLocation);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlMuteButtonElement(HTMLMediaElement*, ButtonLocation);
+
+ virtual void updateDisplayType();
+};
+
+// ----------------------------
+
+class MediaControlPlayButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlPlayButtonElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlPlayButtonElement(HTMLMediaElement*);
+
+ virtual void updateDisplayType();
+};
+
+// ----------------------------
+
+class MediaControlSeekButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlSeekButtonElement> create(HTMLMediaElement*, PseudoId);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlSeekButtonElement(HTMLMediaElement*, PseudoId);
+
+ bool isForwardButton() const;
+
+ virtual void detach();
+ void seekTimerFired(Timer<MediaControlSeekButtonElement>*);
+
+ bool m_seeking;
+ bool m_capturing;
+ Timer<MediaControlSeekButtonElement> m_seekTimer;
+};
+
+// ----------------------------
+
+class MediaControlRewindButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlRewindButtonElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlRewindButtonElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlReturnToRealtimeButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlReturnToRealtimeButtonElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlReturnToRealtimeButtonElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlToggleClosedCaptionsButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlToggleClosedCaptionsButtonElement(HTMLMediaElement*);
+
+ virtual void updateDisplayType();
+};
+
+// ----------------------------
+
+class MediaControlTimelineElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlTimelineElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+ void update(bool updateDuration = true);
+
+private:
+ MediaControlTimelineElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlVolumeSliderElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlVolumeSliderElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+ void update();
+
+private:
+ MediaControlVolumeSliderElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlFullscreenButtonElement : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlFullscreenButtonElement> create(HTMLMediaElement*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ MediaControlFullscreenButtonElement(HTMLMediaElement*);
+};
+
+// ----------------------------
+
+class MediaControlTimeDisplayElement : public MediaControlElement {
+public:
+ static PassRefPtr<MediaControlTimeDisplayElement> create(HTMLMediaElement*, PseudoId);
+
+ void setVisible(bool);
+
+ void setCurrentValue(float);
+ float currentValue() const { return m_currentValue; }
+
+private:
+ MediaControlTimeDisplayElement(HTMLMediaElement*, PseudoId);
+
+ virtual PassRefPtr<RenderStyle> 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 <wtf/HashMap.h>
+#include <wtf/ListHashSet.h>
+
+namespace WebCore {
+
+class OverlapTestRequestClient;
+class RenderInline;
+class RenderObject;
+
+typedef HashMap<OverlapTestRequestClient*, IntRect> 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<RenderInline*>* 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<RenderInline*>* 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 <buis@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
+ 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 <buis@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
+ 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<String, String>& 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<HTMLAppletElement*>(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<HTMLParamElement*>(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<PluginViewBase*>(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 <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+class HTMLAppletElement;
+
+class RenderApplet : public RenderWidget {
+public:
+ RenderApplet(HTMLAppletElement*, const HashMap<String, String>& 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<String, String> m_args;
+};
+
+inline RenderApplet* toRenderApplet(RenderObject* object)
+{
+ ASSERT(!object || object->isApplet());
+ return static_cast<RenderApplet*>(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 <stdlib.h>
+#include <string.h>
+#include <wtf/Assertions.h>
+
+#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<RenderArenaDebugHeader*>(block);
+ header->arena = this;
+ header->size = size;
+ header->signature = signature;
+ return static_cast<char*>(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<char*>(ptr) - debugHeaderSize;
+ RenderArenaDebugHeader* header = static_cast<RenderArenaDebugHeader*>(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 <wtf/Noncopyable.h>
+
+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 <br> 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<RenderBR*>(object);
+}
+
+inline const RenderBR* toRenderBR(const RenderObject* object)
+{
+ ASSERT(!object || object->isBR());
+ return static_cast<const RenderBR*>(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 <wtf/StdLibExtras.h>
+
+#ifdef ANDROID_LAYOUT
+#include "Settings.h"
+#endif
+
+using namespace std;
+using namespace WTF;
+using namespace Unicode;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap;
+static ColumnInfoMap* gColumnInfoMap = 0;
+
+typedef WTF::HashMap<const RenderBlock*, HashSet<RenderBox*>*> PercentHeightDescendantsMap;
+static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0;
+
+typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap;
+static PercentHeightContainerMap* gPercentHeightContainerMap = 0;
+
+typedef WTF::HashMap<RenderBlock*, ListHashSet<RenderInline*>*> ContinuationOutlineTableMap;
+
+typedef WTF::HashSet<RenderBlock*> 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<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->take(this)) {
+ HashSet<RenderBox*>::iterator end = descendantSet->end();
+ for (HashSet<RenderBox*>::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) {
+ HashSet<RenderBlock*>* 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<RenderStyle> 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 <q> 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 <p> inside a <q>, 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<RenderStyle> 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<DelayedUpdateScrollInfoSet> 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 <form>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<HTMLFormElement*>(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<FloatingObject> 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 <dt> with 0.8em author-specified inside
+ // a <dl> inside a <td>.
+ 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 <td><div><p> 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 <td><div><p> 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<RenderBox*>* descendants = gPercentHeightDescendantsMap->get(this)) {
+ HashSet<RenderBox*>::iterator end = descendants->end();
+ for (HashSet<RenderBox*>::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<FloatingObject> 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<FloatingObject> 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<RenderInline*>* continuations = table->get(this);
+ if (!continuations) {
+ continuations = new ListHashSet<RenderInline*>;
+ table->set(this, continuations);
+ }
+
+ continuations->add(flow);
+}
+
+void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty)
+{
+ ContinuationOutlineTableMap* table = continuationOutlineTable();
+ if (table->isEmpty())
+ return;
+
+ ListHashSet<RenderInline*>* continuations = table->get(this);
+ if (!continuations)
+ return;
+
+ // Paint each continuation outline.
+ ListHashSet<RenderInline*>::iterator end = continuations->end();
+ for (ListHashSet<RenderInline*>::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 <body> 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<FloatingObject> 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. <hr>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<RenderBox*, 16> 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<FloatingObject>;
+ m_floatingObjects->setAutoDelete(true);
+ } else {
+ // Don't insert the object again if it's already in the list
+ DeprecatedPtrListIterator<FloatingObject> 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<FloatingObject> 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<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->get(this);
+ if (!descendantSet) {
+ descendantSet = new HashSet<RenderBox*>;
+ gPercentHeightDescendantsMap->set(this, descendantSet);
+ }
+ bool added = descendantSet->add(descendant).second;
+ if (!added) {
+ ASSERT(gPercentHeightContainerMap->get(descendant));
+ ASSERT(gPercentHeightContainerMap->get(descendant)->contains(this));
+ return;
+ }
+
+ HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(descendant);
+ if (!containerSet) {
+ containerSet = new HashSet<RenderBlock*>;
+ gPercentHeightContainerMap->set(descendant, containerSet);
+ }
+ ASSERT(!containerSet->contains(this));
+ containerSet->add(this);
+}
+
+void RenderBlock::removePercentHeightDescendant(RenderBox* descendant)
+{
+ if (!gPercentHeightContainerMap)
+ return;
+
+ HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->take(descendant);
+ if (!containerSet)
+ return;
+
+ HashSet<RenderBlock*>::iterator end = containerSet->end();
+ for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) {
+ RenderBlock* container = *it;
+ HashSet<RenderBox*>* 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<RenderBox*>* 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<FloatingObject> 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<FloatingObject> 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<FloatingObject> 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<FloatingObject> 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<RenderObject*, FloatingObject*> 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 <rdar://problem/8049753>, 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<int>::max();
+ int changeLogicalBottom = numeric_limits<int>::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., <html>.
+ 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<FloatingObject> 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<FloatingObject>;
+ 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<FloatingObject> 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<FloatingObject> 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<FloatingObject>;
+ 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<FloatingObject> 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<FloatingObject> 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<InlineTextBox *>(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 <p> margins.
+ return static_cast<int>(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<int>(style()->columnWidth()));
+ int colCount = max(1, static_cast<int>(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., <a>, <span>, <i>) 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., <span></span>.)
+ 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:
+ /*
+ <div style="position:absolute; width:100px; top:50px;">
+ <div style="position:absolute;left:0px;top:50px;height:50px;background-color:green">
+ <table style="width:100%"><tr><td></table>
+ </div>
+ </div>
+ */
+ // 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<RenderBlock*>(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<StringImpl> 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<FloatingObject> 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<IntRect>& 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<FloatQuad>& 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<IntRect>& 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<RenderStyle> 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<RenderStyle> 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<RenderStyle> 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 <wtf/ListHashSet.h>
+
+namespace WebCore {
+
+class ColumnInfo;
+class InlineIterator;
+class LayoutStateMaintainer;
+class RenderInline;
+
+struct BidiRun;
+
+template <class Iterator, class Run> class BidiResolver;
+template <class Iterator> struct MidpointState;
+typedef BidiResolver<InlineIterator, BidiRun> InlineBidiResolver;
+typedef MidpointState<InlineIterator> 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<RenderBox*, 4> PositionedObjectsListHashSet;
+ PositionedObjectsListHashSet* positionedObjects() const { return m_positionedObjects; }
+
+ void addPercentHeightDescendant(RenderBox*);
+ static void removePercentHeightDescendant(RenderBox*);
+ HashSet<RenderBox*>* 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<RootInlineBox*>(firstLineBox()); }
+ RootInlineBox* lastRootBox() const { return static_cast<RootInlineBox*>(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<Type>(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<FloatWithRect>& 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<IntRect>&, int tx, int ty);
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ 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<IntRect>&, 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<FloatingObject>* 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<RenderBlockRareData> m_rareData;
+
+ RenderObjectChildList m_children;
+ RenderLineBoxList m_lineBoxes; // All of the root line boxes created for this block flow. For example, <div>Hello<br>world.</div> will have two total lines for the <div>.
+
+ 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<RenderBlock*>(object);
+}
+
+inline const RenderBlock* toRenderBlock(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderBlock());
+ return static_cast<const RenderBlock*>(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 <wtf/AlwaysInline.h>
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+#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<int>(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<int>(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 <br> 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 <br> 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<InlineFlowBox*>(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<InlineTextBox*>(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<const SimpleFontData*> fallbackFonts;
+ GlyphOverflow glyphOverflow;
+ int hyphenWidth = 0;
+ if (static_cast<InlineTextBox*>(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<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), 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<InlineTextBox*>(r->m_box), make_pair(Vector<const SimpleFontData*>(), 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<InlineTextBox*>(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<FloatWithRect> 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 <a> or
+ // <li> as tabs, we don't force text to wrap if all the text
+ // are short and within an <a> or <li> tag, and only separated
+ // by short word like "|" or ";".
+ if (node && node->isTextNode() &&
+ !static_cast<Text*>(node)->containsOnlyWhitespace()) {
+ int length = static_cast<Text*>(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<SVGRootInlineBox*>(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<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
+ Vector<RenderBox*>::iterator end = cleanLineFloats->end();
+ for (Vector<RenderBox*>::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<FloatWithRect>& 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<RenderBox*>* cleanLineFloats = curr->floatsPtr()) {
+ Vector<RenderBox*>::iterator end = cleanLineFloats->end();
+ for (Vector<RenderBox*>::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<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
+ Vector<RenderBox*>::iterator end = cleanLineFloats->end();
+ for (Vector<RenderBox*>::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 <br> always breaks a line, so don't let the line be collapsed
+ // away. Also, the space at the end of a line with a <br> does not
+ // get collapsed away. It only does this if the previous line broke
+ // cleanly. Otherwise the <br> 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<RenderSVGInlineText*>(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 <pre>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 <algorithm>
+#include <math.h>
+
+#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<const RenderBox*, int> 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 <body> and <html>, 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 <html>.
+ // (2) We are the primary <body> (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<IntRect>& rects, int tx, int ty)
+{
+ rects.append(IntRect(tx, ty, width(), height()));
+}
+
+void RenderBox::absoluteQuads(Vector<FloatQuad>& 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<IntRect>& 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<RenderBox*>(this)->computePreferredLogicalWidths();
+
+ return m_minPreferredLogicalWidth;
+}
+
+int RenderBox::maxPreferredLogicalWidth() const
+{
+ if (preferredLogicalWidthsDirty())
+ const_cast<RenderBox*>(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 <body> 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 <body> tags etc. We can locate the <body>
+ // 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 <body> 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 <html> block always fills the entire canvas in quirks mode. The <body> always fills the
+ // <html> 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<int>(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<RenderBox*>(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
+ // <http://bugs.webkit.org/show_bug.cgi?id=15359>
+ 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<RenderBlock*>(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"
+ // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
+ // (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"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
+ // (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"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
+ // (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"
+ // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
+ // (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:
+ // <rdar://problem/3777804> 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<IntRect>&, 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<IntRect>&, int tx, int ty);
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ 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, <select>s, <input>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<RenderOverflow> 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<RenderBox*>(object);
+}
+
+inline const RenderBox* toRenderBox(const RenderObject* object)
+{
+ ASSERT(!object || object->isBox());
+ return static_cast<const RenderBox*>(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 <wtf/CurrentTime.h>
+
+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<RenderBoxModelObject*, const void*> LastPaintSizeMapKey;
+typedef HashMap<LastPaintSizeMapKey, IntSize> 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.
+// <b><i><p>Hello</p></i></b>. In this example the <i> will have a block as
+// its continuation but the <b> will just have an inline as its continuation.
+typedef HashMap<const RenderBoxModelObject*, RenderBoxModelObject*> 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<ImageQualityController>*);
+ void restartTimer();
+
+ LastPaintSizeMap m_lastPaintSizeMap;
+ Timer<ImageQualityController> 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<LastPaintSizeMapKey> keysToDie;
+ for (LastPaintSizeMap::iterator it = m_lastPaintSizeMap.begin(); it != m_lastPaintSizeMap.end(); ++it)
+ if (it->first.first == object)
+ keysToDie.append(it->first);
+ for (Vector<LastPaintSizeMapKey>::iterator it = keysToDie.begin(); it != keysToDie.end(); ++it)
+ keyDestroyed(*it);
+}
+
+void ImageQualityController::highQualityRepaintTimerFired(Timer<ImageQualityController>*)
+{
+ 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<double>(image->width()) * static_cast<double>(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 <html> and <body> assume the size of the viewport. In this case,
+ // calculate the percent offset based on this height.
+ // See <https://bugs.webkit.org/show_bug.cgi?id=26396>.
+ 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<ImageBuffer> 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 <body> 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 <body> tags etc. We can locate the <body>
+ // 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<float>(positioningAreaSize.width()) / imageIntrinsicSize.width() : 1;
+ float verticalScaleFactor = imageIntrinsicSize.height()
+ ? static_cast<float>(positioningAreaSize.height()) / imageIntrinsicSize.height() : 1;
+ float scaleFactor = type == Contain ? min(horizontalScaleFactor, verticalScaleFactor) : max(horizontalScaleFactor, verticalScaleFactor);
+ return IntSize(max<int>(1, imageIntrinsicSize.width() * scaleFactor), max<int>(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<RenderBoxModelObject>*);
+
+ 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<RenderBoxModelObject*>(object);
+}
+
+inline const RenderBoxModelObject* toRenderBoxModelObject(const RenderObject* object)
+{
+ ASSERT(!object || object->isBoxModelObject());
+ return static_cast<const RenderBoxModelObject*>(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<RenderButton>(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<HTMLInputElement*>(node());
+ String value = input->valueWithDefault();
+ setText(value);
+ }
+
+
+#if ENABLE(WML)
+ else if (node()->hasTagName(WMLNames::doTag)) {
+ WMLDoElement* doElement = static_cast<WMLDoElement*>(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<RenderButton>*)
+{
+ // 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 <wtf/OwnPtr.h>
+
+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<RenderButton>*);
+
+ RenderTextFragment* m_buttonText;
+ RenderBlock* m_inner;
+
+ OwnPtr<Timer<RenderButton> > m_timer;
+ bool m_default;
+};
+
+inline RenderButton* toRenderButton(RenderObject* object)
+{
+ ASSERT(!object || object->isRenderButton());
+ return static_cast<RenderButton*>(object);
+}
+
+inline const RenderButton* toRenderButton(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderButton());
+ return static_cast<const RenderButton*>(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 <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<CounterNode> > CounterMap;
+typedef HashMap<const RenderObject*, CounterMap*> 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<HTMLOListElement*>(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<CounterNode> 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<StringImpl> 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<CounterNode> 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<CounterNode> 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<StringImpl> 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<RenderCounter*>(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 <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+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<HTMLDataGridElement*>(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<Scrollbar> 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<RenderDetails*>(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<RenderDetailsMarker*>(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 <hausmann@kde.org>
+ * (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<PluginViewBase*>(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 <object> elements whose contents are rendered by WebCore (e.g. src="foo.html").
+ if (node() && widget() && widget()->isFrameView()) {
+ FrameView* view = static_cast<FrameView*>(widget());
+ int marginWidth = -1;
+ int marginHeight = -1;
+ if (node()->hasTagName(iframeTag)) {
+ HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(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<MouseEvent*>(event);
+ HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(node());
+ if (event->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(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<MouseEvent*>(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 <hausmann@kde.org>
+ * 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, <embed src="foo.html"> 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<RenderEmbeddedObject*>(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<int>(style()->borderTopWidth()), legend->height());
+ graphicsContext->clipOut(IntRect(tx + legend->x(), clipTop, legend->width(), clipHeight));
+ } else {
+ int clipLeft = tx;
+ int clipWidth = max(static_cast<int>(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<RenderFieldset*>(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 <math.h>
+
+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<String> 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> fileChooser = m_fileChooser;
+
+ HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(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<HTMLInputElement*>(node());
+ return input->fastHasAttribute(multipleAttr);
+}
+
+#if ENABLE(DIRECTORY_UPLOAD)
+bool RenderFileUploadControl::allowsDirectoryUpload()
+{
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ return input->fastHasAttribute(webkitdirectoryAttr);
+}
+#endif
+
+String RenderFileUploadControl::acceptTypes()
+{
+ return static_cast<HTMLInputElement*>(node())->accept();
+}
+
+void RenderFileUploadControl::chooseIconForFiles(FileChooser* chooser, const Vector<String>& 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<HTMLInputElement*>(node());
+ ASSERT(inputElement->isFileUpload());
+
+ if (!m_button) {
+ m_button = ShadowInputElement::create(inputElement);
+ m_button->setType("button");
+ m_button->setValue(fileButtonChooseFileLabel());
+ RefPtr<RenderStyle> 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<RenderStyle> RenderFileUploadControl::createButtonStyle(const RenderStyle* parentStyle) const
+{
+ RefPtr<RenderStyle> 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<String>& 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>&);
+
+ 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<String>&);
+
+ Chrome* chrome() const;
+ int maxFilenameWidth() const;
+ PassRefPtr<RenderStyle> createButtonStyle(const RenderStyle* parentStyle) const;
+
+ RefPtr<HTMLInputElement> m_button;
+ RefPtr<FileChooser> m_fileChooser;
+};
+
+inline RenderFileUploadControl* toRenderFileUploadControl(RenderObject* object)
+{
+ ASSERT(!object || object->isFileUploadControl());
+ return static_cast<RenderFileUploadControl*>(object);
+}
+
+inline const RenderFileUploadControl* toRenderFileUploadControl(const RenderObject* object)
+{
+ ASSERT(!object || object->isFileUploadControl());
+ return static_cast<const RenderFileUploadControl*>(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 <wtf/StdLibExtras.h>
+
+#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<int>(roundf(m_viewport.width())));
+}
+
+void RenderForeignObject::computeLogicalHeight()
+{
+ // FIXME: Investigate in size rounding issues
+ setHeight(static_cast<int>(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<SVGForeignObjectElement*>(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 <svg> 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 <hausmann@kde.org>
+ * (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<HTMLFrameElement*>(node());
+ return FrameEdgeInfo(element->noResize(), element->hasFrameBorder());
+}
+
+void RenderFrame::viewCleared()
+{
+ HTMLFrameElement* element = static_cast<HTMLFrameElement*>(node());
+ if (!element || !widget() || !widget()->isFrameView())
+ return;
+
+ FrameView* view = static_cast<FrameView*>(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<FrameView*>(widget());
+ RenderView* root = view ? view->frame()->contentRenderer() : 0;
+ if (!width() || !height() || !root) {
+ setNeedsLayout(false);
+ return;
+ }
+
+ HTMLFrameElementBase* element = static_cast<HTMLFrameElementBase*>(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 <hausmann@kde.org>
+ * 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<RenderFrame*>(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<FrameView*>(widget());
+ RenderView* childRoot = childFrameView ? static_cast<RenderView*>(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<HTMLFrameElementBase*>(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 <hausmann@kde.org>
+ * (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<HTMLFrameSetElement*>(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<RenderFrameSet*>(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 <hausmann@kde.org>
+ * 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<bool> m_preventResize;
+ Vector<bool> 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<int> m_sizes;
+ Vector<int> m_deltas;
+ Vector<bool> m_preventResize;
+ Vector<bool> 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<RenderFrameSet*>(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<RenderStyle> RenderFullScreen::createFullScreenStyle()
+{
+ RefPtr<RenderStyle> 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<RenderStyle> createFullScreenStyle();
+
+protected:
+ bool m_isAnimating;
+};
+
+inline RenderFullScreen* toRenderFullScreen(RenderObject* object)
+{
+ ASSERT(object->isRenderFullScreen());
+ return static_cast<RenderFullScreen*>(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<HTMLCanvasElement*>(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<HTMLCanvasElement*>(node())->paint(paintInfo.context, rect);
+}
+
+void RenderHTMLCanvas::canvasSizeChanged()
+{
+ IntSize canvasSize = static_cast<HTMLCanvasElement*>(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<RenderHTMLCanvas*>(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<HTMLIFrameElement*>(node());
+ bool isScrollable = frame->scrollingMode() != ScrollbarAlwaysOff;
+
+ if (isScrollable || !style()->height().isFixed()) {
+ FrameView* view = static_cast<FrameView*>(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<HTMLIFrameElement*>(node());
+ bool isScrollable = frame->scrollingMode() != ScrollbarAlwaysOff;
+
+ if (isScrollable || !style()->width().isFixed()) {
+ FrameView* view = static_cast<FrameView*>(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<HTMLIFrameElement*>(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<HTMLIFrameElement*>(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<RenderIFrame*>(object);
+}
+
+inline const RenderIFrame* toRenderIFrame(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderIFrame());
+ return static_cast<const RenderIFrame*>(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 <wtf/CurrentTime.h>
+#include <wtf/UnusedParam.h>
+
+#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<RenderImageResource> 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<HTMLCollection> 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<HTMLAreaElement*>(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<HTMLImageElement*>(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<HTMLImageElement*>(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<HTMLInputElement*>(node())->altText();
+ else if (node()->hasTagName(imgTag))
+ m_altText = static_cast<HTMLImageElement*>(node())->altText();
+#if ENABLE(WML)
+ else if (node()->hasTagName(WMLNames::imgTag))
+ m_altText = static_cast<WMLImageElement*>(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>);
+
+ 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<RenderImage>*);
+
+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<RenderImageResource> m_imageResource;
+ bool m_needsToSetSizeForAltText;
+
+ friend class RenderImageScaleObserver;
+};
+
+inline RenderImage* toRenderImage(RenderObject* object)
+{
+ ASSERT(!object || object->isRenderImage());
+ return static_cast<RenderImage*>(object);
+}
+
+inline const RenderImage* toRenderImage(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderImage());
+ return static_cast<const RenderImage*>(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 <knoll@kde.org>
+ * Copyright (C) 1999 Antti Koivisto <koivisto@kde.org>
+ * Copyright (C) 2000 Dirk Mueller <mueller@kde.org>
+ * Copyright (C) 2006 Allan Sandfeld Jensen <kde@carewolf.com>
+ * Copyright (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.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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.
+ *
+ */
+
+#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 <knoll@kde.org>
+ * Copyright (C) 1999 Antti Koivisto <koivisto@kde.org>
+ * Copyright (C) 2006 Allan Sandfeld Jensen <kde@carewolf.com>
+ * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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 RenderImageResource_h
+#define RenderImageResource_h
+
+#include "CachedImage.h"
+#include "CachedResourceHandle.h"
+#include "StyleImage.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class RenderObject;
+
+class RenderImageResource : public Noncopyable {
+public:
+ virtual ~RenderImageResource();
+
+ static PassOwnPtr<RenderImageResource> 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<CachedImage> 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 <knoll@kde.org>
+ * Copyright (C) 1999 Antti Koivisto <koivisto@kde.org>
+ * Copyright (C) 2000 Dirk Mueller <mueller@kde.org>
+ * Copyright (C) 2006 Allan Sandfeld Jensen <kde@carewolf.com>
+ * Copyright (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.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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.
+ *
+ */
+
+#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<StyleCachedImage*>(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 <knoll@kde.org>
+ * Copyright (C) 1999 Antti Koivisto <koivisto@kde.org>
+ * Copyright (C) 2006 Allan Sandfeld Jensen <kde@carewolf.com>
+ * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com>
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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 RenderImageResourceStyleImage_h
+#define RenderImageResourceStyleImage_h
+
+#include "RenderImageResource.h"
+#include "StyleImage.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class RenderObject;
+
+class RenderImageResourceStyleImage : public RenderImageResource {
+public:
+ virtual ~RenderImageResourceStyleImage();
+
+ static PassOwnPtr<RenderImageResource> 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<StyleImage> 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., <font>foo <h4>goo</h4> moo</font>. The <font> 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<RenderStyle> 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 <p> inside a <q>, 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 <q> 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<IntRect>& 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<FloatQuad>& 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 <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been
+ // 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<int>::max();
+ int logicalRightSide = numeric_limits<int>::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<IntRect>& 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<IntRect> 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<DashboardRegionValue>& regions)
+{
+ // Convert the style regions to absolute coordinates.
+ if (style()->visibility() != VISIBLE)
+ return;
+
+ const Vector<StyleDashboardRegion>& 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<IntRect>&, int tx, int ty);
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ 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<IntRect>&, 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<DashboardRegionValue>&);
+#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, <i>Hello<br>world.</i> will have two <i> line boxes.
+
+ mutable int m_lineHeight;
+};
+
+inline RenderInline* toRenderInline(RenderObject* object)
+{
+ ASSERT(!object || object->isRenderInline());
+ return static_cast<RenderInline*>(object);
+}
+
+inline const RenderInline* toRenderInline(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderInline());
+ return static_cast<const RenderInline*>(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 <input> 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<Image>, imageStateNormal, (Image::loadPlatformResource("inputSpeech")));
+ DEFINE_STATIC_LOCAL(RefPtr<Image>, imageStateRecording, (Image::loadPlatformResource("inputSpeechRecording")));
+ DEFINE_STATIC_LOCAL(RefPtr<Image>, imageStateWaiting, (Image::loadPlatformResource("inputSpeechWaiting")));
+
+ InputFieldSpeechButtonElement* speechButton = reinterpret_cast<InputFieldSpeechButtonElement*>(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 <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 <wtf/StdLibExtras.h>
+#include <wtf/UnusedParam.h>
+#include <wtf/text/CString.h>
+
+#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<RenderStyle> 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<RenderLayer*>(this);
+
+ for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) {
+ if (curr->isComposited())
+ return const_cast<RenderLayer*>(curr);
+ }
+
+ return 0;
+}
+#endif
+
+RenderLayer* RenderLayer::clippingRoot() const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (isComposited())
+ return const_cast<RenderLayer*>(this);
+#endif
+
+ const RenderLayer* current = this;
+ while (current) {
+ if (current->renderer()->isRenderView())
+ return const_cast<RenderLayer*>(current);
+
+ current = compositingContainer(current);
+ ASSERT(current);
+ if (current->transform()
+#if USE(ACCELERATED_COMPOSITING)
+ || current->isComposited()
+#endif
+ )
+ return const_cast<RenderLayer*>(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<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
+ else if (adjustedDelta < -1)
+ adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-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<Element*>(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 <http://bugs.webkit.org/show_bug.cgi?id=9547>).
+ 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 <http://bugs.webkit.org/show_bug.cgi?id=9547>).
+ 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<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation)
+{
+ RefPtr<Scrollbar> 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>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
+ if (scrollbar) {
+ if (scrollbar->isCustomScrollbar())
+ static_cast<RenderScrollbar*>(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<int>(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<int>(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<HTMLTextAreaElement*>(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<Image>, 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<OverlapTestRequestClient*> 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<RenderLayer*>* 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<RenderLayer*> 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<RenderLayer*>& 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<Element*>(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<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer,
+ const IntRect& hitTestRect, const IntPoint& hitTestPoint,
+ const HitTestingTransformState* containerTransformState) const
+{
+ RefPtr<HitTestingTransformState> 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<HitTestingTransformState> 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<HitTestingTransformState> localTransformState;
+ if (appliedTransform) {
+ // We computed the correct state in the caller (above code), so just reference it.
+ ASSERT(transformState);
+ localTransformState = const_cast<HitTestingTransformState*>(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<HitTestingTransformState> 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<double>::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<RenderLayer*>* 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<RenderLayer*> 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<RenderLayer*>& 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<HitTestingTransformState> 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 <span> 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<Node> 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<RefPtr<Node>, 32> nodesToRemoveFromChain;
+ Vector<RefPtr<Node>, 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<RenderLayer*>;
+ m_normalFlowList->append(child);
+ }
+ }
+
+ m_normalFlowListDirty = false;
+}
+
+void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& 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<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
+
+ // Create the buffer if it doesn't exist yet.
+ if (!buffer)
+ buffer = new Vector<RenderLayer*>;
+
+ // 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<RenderStyle> 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<RenderStyle> 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<RenderStyle> 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 <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 <wtf/OwnPtr.h>
+
+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<Scrollbar> 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<RenderLayer*>* posZOrderList() const { return m_posZOrderList; }
+ Vector<RenderLayer*>* negZOrderList() const { return m_negZOrderList; }
+
+ void dirtyNormalFlowList();
+ void updateNormalFlowList();
+ Vector<RenderLayer*>* 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 <html> 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<RenderLayer*>*&, Vector<RenderLayer*>*&);
+
+ 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*>*, 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<RenderLayer*>& 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*>*, 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<RenderLayer*>& columnLayers, size_t columnIndex);
+
+ PassRefPtr<HitTestingTransformState> 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<Scrollbar> m_hBar;
+ RefPtr<Scrollbar> 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<RenderLayer*>* m_posZOrderList;
+ Vector<RenderLayer*>* 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<RenderLayer*>* 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<TransformationMatrix> 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<RenderLayerBacking> 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<HTMLCanvasElement*>(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<PluginViewBase*>(toRenderWidget(renderer)->widget());
+ m_graphicsLayer->setContentsToMedia(pluginViewBase->platformLayer());
+ }
+#if ENABLE(VIDEO)
+ else if (renderer->isVideo()) {
+ HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(renderer->node());
+ m_graphicsLayer->setContentsToMedia(mediaElement->platformLayer());
+ }
+#endif
+#if ENABLE(3D_CANVAS) || ENABLE(ACCELERATED_2D_CANVAS)
+ else if (isAcceleratedCanvas(renderer)) {
+ HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(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<GraphicsLayerPaintingPhase>(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:
+ // <div>
+ // <img src=...>
+ // </div>
+ // 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<TimingFunction> 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<Element*>(node)->tagName();
+ if (node->hasID())
+ name += " \'" + static_cast<Element*>(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<GraphicsLayer> m_ancestorClippingLayer; // only used if we are clipped by an ancestor which is not a stacking context
+ OwnPtr<GraphicsLayer> m_graphicsLayer;
+ OwnPtr<GraphicsLayer> m_foregroundLayer; // only used in cases where we need to draw the foreground separately
+ OwnPtr<GraphicsLayer> m_clippingLayer; // only used if we have clipping on a stacking context, with compositing children
+ OwnPtr<GraphicsLayer> 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 <wtf/CurrentTime.h>
+#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::CompositingTriggerFlags>(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<RenderLayerCompositor>*)
+{
+ 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<GraphicsLayer*> 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<HTMLCanvasElement*>(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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<GraphicsLayer*>& 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<GraphicsLayer*> layerChildren;
+ Vector<GraphicsLayer*>& 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<HTMLIFrameElement*>(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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* negZOrderList = layer->negZOrderList()) {
+ size_t listSize = negZOrderList->size();
+ for (size_t i = 0; i < listSize; ++i)
+ updateCompositingDescendantGeometry(compositingAncestor, negZOrderList->at(i), updateDepth);
+ }
+ }
+
+ if (Vector<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<FrameView*>(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<HTMLMediaElement*>(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<HTMLCanvasElement*>(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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*>* 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<RenderLayer*, IntRect> OverlapMap;
+ static void addToOverlapMap(OverlapMap&, RenderLayer*, IntRect& layerBounds, bool& boundsComputed);
+ static bool overlapsCompositedLayers(OverlapMap&, const IntRect& layerBounds);
+
+ void updateCompositingLayersTimerFired(Timer<RenderLayerCompositor>*);
+
+ // 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<GraphicsLayer*>& 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<GraphicsLayer> m_rootPlatformLayer;
+ Timer<RenderLayerCompositor> 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<GraphicsLayer> m_clipLayer;
+ OwnPtr<GraphicsLayer> 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<RenderInline*> 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<RenderInline*>::iterator end = info.outlineObjects->end();
+ for (ListHashSet<RenderInline*>::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.
+ // <rdar://problem/3849947> "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 <math.h>
+
+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<Element*>& listItems = toSelectElement(static_cast<Element*>(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<int>(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<Element*>(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<Element*>(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<Element*>(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<Element*>(node()));
+ const Vector<Element*>& 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<Element*>(node()));
+ const Vector<Element*>& 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<Element*>(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<Element*>(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<Element*>(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<Element*>(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<Element*>& listItems = toSelectElement(static_cast<Element*>(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<Scrollbar> RenderListBox::createScrollbar()
+{
+ RefPtr<Scrollbar> 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<Scrollbar> 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<Scrollbar> m_vBar;
+};
+
+inline RenderListBox* toRenderListBox(RenderObject* object)
+{
+ ASSERT(!object || object->isListBox());
+ return static_cast<RenderListBox*>(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 <wtf/StdLibExtras.h>
+
+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<RenderStyle> 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 <ul> or <ol> list element, then the first found
+ // node acts as our list for purposes of determining what other list items
+ // should be numbered as part of the same list.
+ return firstNode;
+}
+
+static RenderListItem* previousListItem(Node* list, const RenderListItem* item)
+{
+ for (RenderObject* renderer = item->previousInPreOrder(); renderer && renderer != list->renderer(); renderer = renderer->previousInPreOrder()) {
+ if (!renderer->isListItem())
+ continue;
+ Node* otherList = enclosingList(toRenderListItem(renderer));
+ // This item is part of our current list, so it's what we're looking for.
+ if (list == otherList)
+ return toRenderListItem(renderer);
+ // We found ourself inside another list; lets skip the rest of it.
+ // Use nextInPreOrder() here because the other list itself may actually
+ // be a list item itself. We need to examine it, so we do this to counteract
+ // the previousInPreOrder() that will be done by the loop.
+ if (otherList)
+ renderer = otherList->renderer()->nextInPreOrder();
+ }
+ return 0;
+}
+
+inline int RenderListItem::calcValue() const
+{
+ if (m_hasExplicitValue)
+ return m_explicitValue;
+ Node* list = enclosingList(this);
+ // FIXME: This recurses to a possible depth of the length of the list.
+ // That's not good -- we need to change this to an iterative algorithm.
+ if (RenderListItem* previousItem = previousListItem(list, this))
+ return previousItem->value() + 1;
+ if (list && list->hasTagName(olTag))
+ return static_cast<HTMLOListElement*>(list)->start();
+ return 1;
+}
+
+void RenderListItem::updateValueNow() const
+{
+ m_value = calcValue();
+ m_isValueUpToDate = true;
+}
+
+bool RenderListItem::isEmpty() const
+{
+ return lastChild() == m_marker;
+}
+
+static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
+{
+ RenderObject* firstChild = curr->firstChild();
+ if (!firstChild)
+ return 0;
+
+ bool inQuirksMode = curr->document()->inQuirksMode();
+ for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
+ if (currChild == marker)
+ continue;
+
+ if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild)))
+ return curr;
+
+ if (currChild->isFloating() || currChild->isPositioned())
+ continue;
+
+ if (currChild->isTable() || !currChild->isRenderBlock() || (currChild->isBox() && toRenderBox(currChild)->isWritingModeRoot()))
+ break;
+
+ if (curr->isListItem() && inQuirksMode && currChild->node() &&
+ (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag)))
+ break;
+
+ RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker);
+ if (lineBox)
+ return lineBox;
+ }
+
+ return 0;
+}
+
+void RenderListItem::updateValue()
+{
+ if (!m_hasExplicitValue) {
+ m_isValueUpToDate = false;
+ if (m_marker)
+ m_marker->setNeedsLayoutAndPrefWidthsRecalc();
+ }
+}
+
+static RenderObject* firstNonMarkerChild(RenderObject* parent)
+{
+ RenderObject* result = parent->firstChild();
+ while (result && result->isListMarker())
+ result = result->nextSibling();
+ return result;
+}
+
+void RenderListItem::updateMarkerLocation()
+{
+ // Sanity check the location of our marker.
+ if (m_marker) {
+ RenderObject* markerPar = m_marker->parent();
+ RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
+ if (!lineBoxParent) {
+ // If the marker is currently contained inside an anonymous box,
+ // then we are the only item in that anonymous box (since no line box
+ // parent was found). It's ok to just leave the marker where it is
+ // in this case.
+ if (markerPar && markerPar->isAnonymousBlock())
+ lineBoxParent = markerPar;
+ else
+ lineBoxParent = this;
+ }
+
+ if (markerPar != lineBoxParent || m_marker->preferredLogicalWidthsDirty()) {
+ // Removing and adding the marker can trigger repainting in
+ // containers other than ourselves, so we need to disable LayoutState.
+ view()->disableLayoutState();
+ updateFirstLetter();
+ m_marker->remove();
+ if (!lineBoxParent)
+ lineBoxParent = this;
+ lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
+ if (m_marker->preferredLogicalWidthsDirty())
+ m_marker->computePreferredLogicalWidths();
+ view()->enableLayoutState();
+ }
+ }
+}
+
+void RenderListItem::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ updateMarkerLocation();
+
+ RenderBlock::computePreferredLogicalWidths();
+}
+
+void RenderListItem::layout()
+{
+ ASSERT(needsLayout());
+
+ updateMarkerLocation();
+ RenderBlock::layout();
+}
+
+void RenderListItem::addOverflowFromChildren()
+{
+ RenderBlock::addOverflowFromChildren();
+ positionListMarker();
+}
+
+void RenderListItem::positionListMarker()
+{
+ if (m_marker && m_marker->parent()->isBox() && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
+ int markerOldLogicalLeft = m_marker->logicalLeft();
+ int blockOffset = 0;
+ int lineOffset = 0;
+ for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) {
+ blockOffset += o->logicalTop();
+ lineOffset += o->logicalLeft();
+ }
+
+ bool adjustOverflow = false;
+ int markerLogicalLeft;
+ RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
+ bool hitSelfPaintingLayer = false;
+
+ // FIXME: Need to account for relative positioning in the layout overflow.
+ if (style()->isLeftToRightDirection()) {
+ int leftLineOffset = logicalLeftOffsetForLine(blockOffset, logicalLeftOffsetForLine(blockOffset, false), false);
+ markerLogicalLeft = leftLineOffset - lineOffset - paddingStart() - borderStart() + m_marker->marginStart();
+ m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
+ for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
+ IntRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect();
+ IntRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect();
+ if (markerLogicalLeft < newLogicalVisualOverflowRect.x() && !hitSelfPaintingLayer) {
+ newLogicalVisualOverflowRect.setX(markerLogicalLeft);
+ newLogicalVisualOverflowRect.setWidth(box->logicalRightVisualOverflow() - newLogicalVisualOverflowRect.x());
+ if (box == root)
+ adjustOverflow = true;
+ }
+ if (markerLogicalLeft < newLogicalLayoutOverflowRect.x()) {
+ newLogicalLayoutOverflowRect.setX(markerLogicalLeft);
+ newLogicalLayoutOverflowRect.setWidth(box->logicalRightLayoutOverflow() - newLogicalLayoutOverflowRect.x());
+ if (box == root)
+ adjustOverflow = true;
+ }
+ box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect);
+ if (box->boxModelObject()->hasSelfPaintingLayer())
+ hitSelfPaintingLayer = true;
+ }
+ } else {
+ markerLogicalLeft = m_marker->logicalLeft() + paddingStart() + borderStart() + m_marker->marginEnd();
+ int rightLineOffset = logicalRightOffsetForLine(blockOffset, logicalRightOffsetForLine(blockOffset, false), false);
+ markerLogicalLeft = rightLineOffset - lineOffset + paddingStart() + borderStart() + m_marker->marginEnd();
+ m_marker->inlineBoxWrapper()->adjustLineDirectionPosition(markerLogicalLeft - markerOldLogicalLeft);
+ for (InlineFlowBox* box = m_marker->inlineBoxWrapper()->parent(); box; box = box->parent()) {
+ IntRect newLogicalVisualOverflowRect = box->logicalVisualOverflowRect();
+ IntRect newLogicalLayoutOverflowRect = box->logicalLayoutOverflowRect();
+ if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalVisualOverflowRect.right() && !hitSelfPaintingLayer) {
+ newLogicalVisualOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - box->logicalLeftVisualOverflow());
+ if (box == root)
+ adjustOverflow = true;
+ }
+ if (markerLogicalLeft + m_marker->logicalWidth() > newLogicalLayoutOverflowRect.right()) {
+ newLogicalLayoutOverflowRect.setWidth(markerLogicalLeft + m_marker->logicalWidth() - box->logicalLeftLayoutOverflow());
+ if (box == root)
+ adjustOverflow = true;
+ }
+ box->setOverflowFromLogicalRects(newLogicalLayoutOverflowRect, newLogicalVisualOverflowRect);
+
+ if (box->boxModelObject()->hasSelfPaintingLayer())
+ hitSelfPaintingLayer = true;
+ }
+ }
+
+ if (adjustOverflow) {
+ IntRect markerRect(markerLogicalLeft + lineOffset, blockOffset, m_marker->width(), m_marker->height());
+ if (!style()->isHorizontalWritingMode())
+ markerRect = markerRect.transposedRect();
+ RenderBox* o = m_marker;
+ bool propagateVisualOverflow = true;
+ bool propagateLayoutOverflow = true;
+ do {
+ o = o->parentBox();
+ if (o->hasOverflowClip())
+ propagateVisualOverflow = false;
+ if (o->isRenderBlock()) {
+ if (propagateVisualOverflow)
+ toRenderBlock(o)->addVisualOverflow(markerRect);
+ if (propagateLayoutOverflow)
+ toRenderBlock(o)->addLayoutOverflow(markerRect);
+ }
+ if (o->hasOverflowClip())
+ propagateLayoutOverflow = false;
+ if (o->hasSelfPaintingLayer())
+ propagateVisualOverflow = false;
+ markerRect.move(-o->x(), -o->y());
+ } while (o != this && propagateVisualOverflow && propagateLayoutOverflow);
+ }
+ }
+}
+
+void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (!logicalHeight())
+ return;
+
+ RenderBlock::paint(paintInfo, tx, ty);
+}
+
+const String& RenderListItem::markerText() const
+{
+ if (m_marker)
+ return m_marker->text();
+ DEFINE_STATIC_LOCAL(String, staticNullString, ());
+ return staticNullString;
+}
+
+String RenderListItem::markerTextWithSuffix() const
+{
+ if (!m_marker)
+ return String();
+
+ // Append the suffix for the marker in the right place depending
+ // on the direction of the text (right-to-left or left-to-right).
+
+ const String& markerText = m_marker->text();
+ const String markerSuffix = m_marker->suffix();
+ Vector<UChar> resultVector;
+
+ if (!m_marker->style()->isLeftToRightDirection())
+ resultVector.append(markerSuffix.characters(), markerSuffix.length());
+
+ resultVector.append(markerText.characters(), markerText.length());
+
+ if (m_marker->style()->isLeftToRightDirection())
+ resultVector.append(markerSuffix.characters(), markerSuffix.length());
+
+ return String::adopt(resultVector);
+}
+
+void RenderListItem::explicitValueChanged()
+{
+ if (m_marker)
+ m_marker->setNeedsLayoutAndPrefWidthsRecalc();
+ Node* listNode = enclosingList(this);
+ RenderObject* listRenderer = 0;
+ if (listNode)
+ listRenderer = listNode->renderer();
+ for (RenderObject* renderer = this; renderer; renderer = renderer->nextInPreOrder(listRenderer))
+ if (renderer->isListItem()) {
+ RenderListItem* item = toRenderListItem(renderer);
+ if (!item->m_hasExplicitValue) {
+ item->m_isValueUpToDate = false;
+ if (RenderListMarker* marker = item->m_marker)
+ marker->setNeedsLayoutAndPrefWidthsRecalc();
+ }
+ }
+}
+
+void RenderListItem::setExplicitValue(int value)
+{
+ ASSERT(node());
+
+ if (m_hasExplicitValue && m_explicitValue == value)
+ return;
+ m_explicitValue = value;
+ m_value = value;
+ m_hasExplicitValue = true;
+ explicitValueChanged();
+}
+
+void RenderListItem::clearExplicitValue()
+{
+ ASSERT(node());
+
+ if (!m_hasExplicitValue)
+ return;
+ m_hasExplicitValue = false;
+ m_isValueUpToDate = false;
+ explicitValueChanged();
+}
+
+void RenderListItem::updateListMarkerNumbers()
+{
+ Node* listNode = enclosingList(this);
+ ASSERT(listNode && listNode->renderer());
+ if (!listNode || !listNode->renderer())
+ return;
+
+ RenderObject* list = listNode->renderer();
+ RenderObject* child = nextInPreOrder(list);
+ while (child) {
+ if (child->node() && isList(child->node())) {
+ // We've found a nested, independent list: nothing to do here.
+ child = child->nextInPreOrderAfterChildren(list);
+ continue;
+ }
+
+ if (child->isListItem()) {
+ RenderListItem* item = toRenderListItem(child);
+
+ if (!item->m_isValueUpToDate) {
+ // If an item has been marked for update before, we can safely
+ // assume that all the following ones have too.
+ // This gives us the opportunity to stop here and avoid
+ // marking the same nodes again.
+ break;
+ }
+
+ item->updateValue();
+ }
+
+ child = child->nextInPreOrder(list);
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderListItem.h b/Source/WebCore/rendering/RenderListItem.h
new file mode 100644
index 0000000..fe2cb6a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderListItem.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 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 RenderListItem_h
+#define RenderListItem_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class RenderListMarker;
+
+class RenderListItem : public RenderBlock {
+public:
+ explicit RenderListItem(Node*);
+
+ int value() const { if (!m_isValueUpToDate) updateValueNow(); return m_value; }
+ void updateValue();
+
+ bool hasExplicitValue() const { return m_hasExplicitValue; }
+ int explicitValue() const { return m_explicitValue; }
+ void setExplicitValue(int value);
+ void clearExplicitValue();
+
+ void setNotInList(bool notInList) { m_notInList = notInList; }
+ bool notInList() const { return m_notInList; }
+
+ const String& markerText() const;
+ String markerTextWithSuffix() const;
+
+ void updateListMarkerNumbers();
+
+private:
+ virtual const char* renderName() const { return "RenderListItem"; }
+
+ virtual bool isListItem() const { return true; }
+
+ virtual void destroy();
+
+ virtual bool isEmpty() const;
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual void layout();
+ virtual void computePreferredLogicalWidths();
+
+ void positionListMarker();
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return true; }
+
+ virtual void addOverflowFromChildren();
+
+ void updateMarkerLocation();
+ inline int calcValue() const;
+ void updateValueNow() const;
+ void explicitValueChanged();
+
+ RenderListMarker* m_marker;
+ int m_explicitValue;
+ mutable int m_value;
+
+ bool m_hasExplicitValue : 1;
+ mutable bool m_isValueUpToDate : 1;
+ bool m_notInList : 1;
+};
+
+inline RenderListItem* toRenderListItem(RenderObject* object)
+{
+ ASSERT(!object || object->isListItem());
+ return static_cast<RenderListItem*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderListItem(const RenderListItem*);
+
+} // namespace WebCore
+
+#endif // RenderListItem_h
diff --git a/Source/WebCore/rendering/RenderListMarker.cpp b/Source/WebCore/rendering/RenderListMarker.cpp
new file mode 100644
index 0000000..71b1eae
--- /dev/null
+++ b/Source/WebCore/rendering/RenderListMarker.cpp
@@ -0,0 +1,1720 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
+ * Copyright (C) 2010 Daniel Bates (dbates@intudata.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.
+ *
+ */
+
+#include "config.h"
+#include "RenderListMarker.h"
+
+#include "CachedImage.h"
+#include "CharacterNames.h"
+#include "Document.h"
+#include "GraphicsContext.h"
+#include "RenderLayer.h"
+#include "RenderListItem.h"
+#include "RenderView.h"
+
+using namespace std;
+using namespace WTF;
+using namespace Unicode;
+
+namespace WebCore {
+
+const int cMarkerPadding = 7;
+
+enum SequenceType { NumericSequence, AlphabeticSequence };
+
+static String toRoman(int number, bool upper)
+{
+ // FIXME: CSS3 describes how to make this work for much larger numbers,
+ // using overbars and special characters. It also specifies the characters
+ // in the range U+2160 to U+217F instead of standard ASCII ones.
+ ASSERT(number >= 1 && number <= 3999);
+
+ // Big enough to store largest roman number less than 3999 which
+ // is 3888 (MMMDCCCLXXXVIII)
+ const int lettersSize = 15;
+ UChar letters[lettersSize];
+
+ int length = 0;
+ const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
+ const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
+ const UChar* digits = upper ? udigits : ldigits;
+ int d = 0;
+ do {
+ int num = number % 10;
+ if (num % 5 < 4)
+ for (int i = num % 5; i > 0; i--)
+ letters[lettersSize - ++length] = digits[d];
+ if (num >= 4 && num <= 8)
+ letters[lettersSize - ++length] = digits[d + 1];
+ if (num == 9)
+ letters[lettersSize - ++length] = digits[d + 2];
+ if (num % 5 == 4)
+ letters[lettersSize - ++length] = digits[d];
+ number /= 10;
+ d += 2;
+ } while (number);
+
+ ASSERT(length <= lettersSize);
+ return String(&letters[lettersSize - length], length);
+}
+
+static inline String toAlphabeticOrNumeric(int number, const UChar* sequence, unsigned sequenceSize, SequenceType type)
+{
+ ASSERT(sequenceSize >= 2);
+
+ const int lettersSize = sizeof(number) * 8 + 1; // Binary is the worst case; requires one character per bit plus a minus sign.
+
+ UChar letters[lettersSize];
+
+ bool isNegativeNumber = false;
+ unsigned numberShadow = number;
+ if (type == AlphabeticSequence) {
+ ASSERT(number > 0);
+ --numberShadow;
+ } else if (number < 0) {
+ numberShadow = -number;
+ isNegativeNumber = true;
+ }
+ letters[lettersSize - 1] = sequence[numberShadow % sequenceSize];
+ int length = 1;
+
+ if (type == AlphabeticSequence) {
+ while ((numberShadow /= sequenceSize) > 0) {
+ --numberShadow;
+ letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
+ }
+ } else {
+ while ((numberShadow /= sequenceSize) > 0)
+ letters[lettersSize - ++length] = sequence[numberShadow % sequenceSize];
+ }
+ if (isNegativeNumber)
+ letters[lettersSize - ++length] = hyphenMinus;
+
+ ASSERT(length <= lettersSize);
+ return String(&letters[lettersSize - length], length);
+}
+
+static String toSymbolic(int number, const UChar* symbols, unsigned symbolsSize)
+{
+ ASSERT(number > 0);
+ ASSERT(symbolsSize >= 1);
+ unsigned numberShadow = number;
+ --numberShadow;
+
+ // The asterisks list-style-type is the worst case; we show |numberShadow| asterisks.
+ Vector<UChar> letters;
+ letters.append(symbols[numberShadow % symbolsSize]);
+ unsigned numSymbols = numberShadow / symbolsSize;
+ while (numSymbols--)
+ letters.append(symbols[numberShadow % symbolsSize]);
+ return String::adopt(letters);
+}
+
+static String toAlphabetic(int number, const UChar* alphabet, unsigned alphabetSize)
+{
+ return toAlphabeticOrNumeric(number, alphabet, alphabetSize, AlphabeticSequence);
+}
+
+static String toNumeric(int number, const UChar* numerals, unsigned numeralsSize)
+{
+ return toAlphabeticOrNumeric(number, numerals, numeralsSize, NumericSequence);
+}
+
+template <size_t size> static inline String toAlphabetic(int number, const UChar(&alphabet)[size])
+{
+ return toAlphabetic(number, alphabet, size);
+}
+
+template <size_t size> static inline String toNumeric(int number, const UChar(&alphabet)[size])
+{
+ return toNumeric(number, alphabet, size);
+}
+
+template <size_t size> static inline String toSymbolic(int number, const UChar(&alphabet)[size])
+{
+ return toSymbolic(number, alphabet, size);
+}
+
+static int toHebrewUnder1000(int number, UChar letters[5])
+{
+ // FIXME: CSS3 mentions various refinements not implemented here.
+ // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
+ ASSERT(number >= 0 && number < 1000);
+ int length = 0;
+ int fourHundreds = number / 400;
+ for (int i = 0; i < fourHundreds; i++)
+ letters[length++] = 1511 + 3;
+ number %= 400;
+ if (number / 100)
+ letters[length++] = 1511 + (number / 100) - 1;
+ number %= 100;
+ if (number == 15 || number == 16) {
+ letters[length++] = 1487 + 9;
+ letters[length++] = 1487 + number - 9;
+ } else {
+ if (int tens = number / 10) {
+ static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
+ letters[length++] = hebrewTens[tens - 1];
+ }
+ if (int ones = number % 10)
+ letters[length++] = 1487 + ones;
+ }
+ ASSERT(length <= 5);
+ return length;
+}
+
+static String toHebrew(int number)
+{
+ // FIXME: CSS3 mentions ways to make this work for much larger numbers.
+ ASSERT(number >= 0 && number <= 999999);
+
+ if (number == 0) {
+ static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
+ return String(hebrewZero, 3);
+ }
+
+ const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
+ UChar letters[lettersSize];
+
+ int length;
+ if (number < 1000)
+ length = 0;
+ else {
+ length = toHebrewUnder1000(number / 1000, letters);
+ letters[length++] = '\'';
+ number = number % 1000;
+ }
+ length += toHebrewUnder1000(number, letters + length);
+
+ ASSERT(length <= lettersSize);
+ return String(letters, length);
+}
+
+static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
+{
+ ASSERT(number >= 0 && number < 10000);
+ int length = 0;
+
+ int lowerOffset = upper ? 0 : 0x0030;
+
+ if (int thousands = number / 1000) {
+ if (thousands == 7) {
+ letters[length++] = 0x0548 + lowerOffset;
+ letters[length++] = 0x0552 + lowerOffset;
+ if (addCircumflex)
+ letters[length++] = 0x0302;
+ } else {
+ letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
+ if (addCircumflex)
+ letters[length++] = 0x0302;
+ }
+ }
+
+ if (int hundreds = (number / 100) % 10) {
+ letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
+ if (addCircumflex)
+ letters[length++] = 0x0302;
+ }
+
+ if (int tens = (number / 10) % 10) {
+ letters[length++] = (0x053A - 1 + lowerOffset) + tens;
+ if (addCircumflex)
+ letters[length++] = 0x0302;
+ }
+
+ if (int ones = number % 10) {
+ letters[length++] = (0x531 - 1 + lowerOffset) + ones;
+ if (addCircumflex)
+ letters[length++] = 0x0302;
+ }
+
+ return length;
+}
+
+static String toArmenian(int number, bool upper)
+{
+ ASSERT(number >= 1 && number <= 99999999);
+
+ const int lettersSize = 18; // twice what toArmenianUnder10000 needs
+ UChar letters[lettersSize];
+
+ int length = toArmenianUnder10000(number / 10000, upper, true, letters);
+ length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
+
+ ASSERT(length <= lettersSize);
+ return String(letters, length);
+}
+
+static String toGeorgian(int number)
+{
+ ASSERT(number >= 1 && number <= 19999);
+
+ const int lettersSize = 5;
+ UChar letters[lettersSize];
+
+ int length = 0;
+
+ if (number > 9999)
+ letters[length++] = 0x10F5;
+
+ if (int thousands = (number / 1000) % 10) {
+ static const UChar georgianThousands[9] = {
+ 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
+ };
+ letters[length++] = georgianThousands[thousands - 1];
+ }
+
+ if (int hundreds = (number / 100) % 10) {
+ static const UChar georgianHundreds[9] = {
+ 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
+ };
+ letters[length++] = georgianHundreds[hundreds - 1];
+ }
+
+ if (int tens = (number / 10) % 10) {
+ static const UChar georgianTens[9] = {
+ 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
+ };
+ letters[length++] = georgianTens[tens - 1];
+ }
+
+ if (int ones = number % 10) {
+ static const UChar georgianOnes[9] = {
+ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
+ };
+ letters[length++] = georgianOnes[ones - 1];
+ }
+
+ ASSERT(length <= lettersSize);
+ return String(letters, length);
+}
+
+// The table uses the order from the CSS3 specification:
+// first 3 group markers, then 3 digit markers, then ten digits.
+static String toCJKIdeographic(int number, const UChar table[16])
+{
+ ASSERT(number >= 0);
+
+ enum AbstractCJKChar {
+ noChar,
+ secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
+ secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
+ digit0, digit1, digit2, digit3, digit4,
+ digit5, digit6, digit7, digit8, digit9
+ };
+
+ if (number == 0)
+ return String(&table[digit0 - 1], 1);
+
+ const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
+ const int bufferLength = 4 * groupLength;
+ AbstractCJKChar buffer[bufferLength] = { noChar };
+
+ for (int i = 0; i < 4; ++i) {
+ int groupValue = number % 10000;
+ number /= 10000;
+
+ // Process least-significant group first, but put it in the buffer last.
+ AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
+
+ if (groupValue && i)
+ group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
+
+ // Put in the four digits and digit markers for any non-zero digits.
+ group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
+ if (number != 0 || groupValue > 9) {
+ int digitValue = ((groupValue / 10) % 10);
+ group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
+ if (digitValue)
+ group[5] = secondDigitMarker;
+ }
+ if (number != 0 || groupValue > 99) {
+ int digitValue = ((groupValue / 100) % 10);
+ group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
+ if (digitValue)
+ group[3] = thirdDigitMarker;
+ }
+ if (number != 0 || groupValue > 999) {
+ int digitValue = groupValue / 1000;
+ group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
+ if (digitValue)
+ group[1] = fourthDigitMarker;
+ }
+
+ // Remove the tens digit, but leave the marker, for any group that has
+ // a value of less than 20.
+ if (groupValue < 20) {
+ ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
+ group[4] = noChar;
+ }
+
+ if (number == 0)
+ break;
+ }
+
+ // Convert into characters, omitting consecutive runs of digit0 and
+ // any trailing digit0.
+ int length = 0;
+ UChar characters[bufferLength];
+ AbstractCJKChar last = noChar;
+ for (int i = 0; i < bufferLength; ++i) {
+ AbstractCJKChar a = buffer[i];
+ if (a != noChar) {
+ if (a != digit0 || last != digit0)
+ characters[length++] = table[a - 1];
+ last = a;
+ }
+ }
+ if (last == digit0)
+ --length;
+
+ return String(characters, length);
+}
+
+static EListStyleType effectiveListMarkerType(EListStyleType type, int value)
+{
+ // Note, the following switch statement has been explicitly grouped
+ // by list-style-type ordinal range.
+ switch (type) {
+ case ArabicIndic:
+ case Bengali:
+ case BinaryListStyle:
+ case Cambodian:
+ case Circle:
+ case DecimalLeadingZero:
+ case DecimalListStyle:
+ case Devanagari:
+ case Disc:
+ case Gujarati:
+ case Gurmukhi:
+ case Kannada:
+ case Khmer:
+ case Lao:
+ case LowerHexadecimal:
+ case Malayalam:
+ case Mongolian:
+ case Myanmar:
+ case NoneListStyle:
+ case Octal:
+ case Oriya:
+ case Persian:
+ case Square:
+ case Telugu:
+ case Thai:
+ case Tibetan:
+ case UpperHexadecimal:
+ case Urdu:
+ return type; // Can represent all ordinals.
+ case Armenian:
+ return (value < 1 || value > 99999999) ? DecimalListStyle : type;
+ case CJKIdeographic:
+ return (value < 0) ? DecimalListStyle : type;
+ case Georgian:
+ return (value < 1 || value > 19999) ? DecimalListStyle : type;
+ case Hebrew:
+ return (value < 0 || value > 999999) ? DecimalListStyle : type;
+ case LowerRoman:
+ case UpperRoman:
+ return (value < 1 || value > 3999) ? DecimalListStyle : type;
+ case Afar:
+ case Amharic:
+ case AmharicAbegede:
+ case Asterisks:
+ case CjkEarthlyBranch:
+ case CjkHeavenlyStem:
+ case Ethiopic:
+ case EthiopicAbegede:
+ case EthiopicAbegedeAmEt:
+ case EthiopicAbegedeGez:
+ case EthiopicAbegedeTiEr:
+ case EthiopicAbegedeTiEt:
+ case EthiopicHalehameAaEr:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAmEt:
+ case EthiopicHalehameGez:
+ case EthiopicHalehameOmEt:
+ case EthiopicHalehameSidEt:
+ case EthiopicHalehameSoEt:
+ case EthiopicHalehameTiEr:
+ case EthiopicHalehameTiEt:
+ case EthiopicHalehameTig:
+ case Footnotes:
+ case Hangul:
+ case HangulConsonant:
+ case Hiragana:
+ case HiraganaIroha:
+ case Katakana:
+ case KatakanaIroha:
+ case LowerAlpha:
+ case LowerArmenian:
+ case LowerGreek:
+ case LowerLatin:
+ case LowerNorwegian:
+ case Oromo:
+ case Sidama:
+ case Somali:
+ case Tigre:
+ case TigrinyaEr:
+ case TigrinyaErAbegede:
+ case TigrinyaEt:
+ case TigrinyaEtAbegede:
+ case UpperAlpha:
+ case UpperArmenian:
+ case UpperGreek:
+ case UpperLatin:
+ case UpperNorwegian:
+ return (value < 1) ? DecimalListStyle : type;
+ }
+
+ ASSERT_NOT_REACHED();
+ return type;
+}
+
+static UChar listMarkerSuffix(EListStyleType type, int value)
+{
+ // If the list-style-type cannot represent |value| because it's outside its
+ // ordinal range then we fall back to some list style that can represent |value|.
+ EListStyleType effectiveType = effectiveListMarkerType(type, value);
+
+ // Note, the following switch statement has been explicitly
+ // grouped by list-style-type suffix.
+ switch (effectiveType) {
+ case Asterisks:
+ case Circle:
+ case Disc:
+ case Footnotes:
+ case NoneListStyle:
+ case Square:
+ return ' ';
+ case Afar:
+ case Amharic:
+ case AmharicAbegede:
+ case Ethiopic:
+ case EthiopicAbegede:
+ case EthiopicAbegedeAmEt:
+ case EthiopicAbegedeGez:
+ case EthiopicAbegedeTiEr:
+ case EthiopicAbegedeTiEt:
+ case EthiopicHalehameAaEr:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAmEt:
+ case EthiopicHalehameGez:
+ case EthiopicHalehameOmEt:
+ case EthiopicHalehameSidEt:
+ case EthiopicHalehameSoEt:
+ case EthiopicHalehameTiEr:
+ case EthiopicHalehameTiEt:
+ case EthiopicHalehameTig:
+ case Oromo:
+ case Sidama:
+ case Somali:
+ case Tigre:
+ case TigrinyaEr:
+ case TigrinyaErAbegede:
+ case TigrinyaEt:
+ case TigrinyaEtAbegede:
+ return ethiopicPrefaceColon;
+ case Armenian:
+ case ArabicIndic:
+ case Bengali:
+ case BinaryListStyle:
+ case Cambodian:
+ case CJKIdeographic:
+ case CjkEarthlyBranch:
+ case CjkHeavenlyStem:
+ case DecimalLeadingZero:
+ case DecimalListStyle:
+ case Devanagari:
+ case Georgian:
+ case Gujarati:
+ case Gurmukhi:
+ case Hangul:
+ case HangulConsonant:
+ case Hebrew:
+ case Hiragana:
+ case HiraganaIroha:
+ case Kannada:
+ case Katakana:
+ case KatakanaIroha:
+ case Khmer:
+ case Lao:
+ case LowerAlpha:
+ case LowerArmenian:
+ case LowerGreek:
+ case LowerHexadecimal:
+ case LowerLatin:
+ case LowerNorwegian:
+ case LowerRoman:
+ case Malayalam:
+ case Mongolian:
+ case Myanmar:
+ case Octal:
+ case Oriya:
+ case Persian:
+ case Telugu:
+ case Thai:
+ case Tibetan:
+ case UpperAlpha:
+ case UpperArmenian:
+ case UpperGreek:
+ case UpperHexadecimal:
+ case UpperLatin:
+ case UpperNorwegian:
+ case UpperRoman:
+ case Urdu:
+ return '.';
+ }
+
+ ASSERT_NOT_REACHED();
+ return '.';
+}
+
+String listMarkerText(EListStyleType type, int value)
+{
+ // If the list-style-type, say hebrew, cannot represent |value| because it's outside
+ // its ordinal range then we fallback to some list style that can represent |value|.
+ switch (effectiveListMarkerType(type, value)) {
+ case NoneListStyle:
+ return "";
+
+ case Asterisks: {
+ static const UChar asterisksSymbols[1] = {
+ 0x002A
+ };
+ return toSymbolic(value, asterisksSymbols);
+ }
+ // We use the same characters for text security.
+ // See RenderText::setInternalString.
+ case Circle:
+ return String(&whiteBullet, 1);
+ case Disc:
+ return String(&bullet, 1);
+ case Footnotes: {
+ static const UChar footnotesSymbols[4] = {
+ 0x002A, 0x2051, 0x2020, 0x2021
+ };
+ return toSymbolic(value, footnotesSymbols);
+ }
+ case Square:
+ // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
+ // instead, but I think this looks better.
+ return String(&blackSquare, 1);
+
+ case DecimalListStyle:
+ return String::number(value);
+ case DecimalLeadingZero:
+ if (value < -9 || value > 9)
+ return String::number(value);
+ if (value < 0)
+ return "-0" + String::number(-value); // -01 to -09
+ return "0" + String::number(value); // 00 to 09
+
+ case ArabicIndic: {
+ static const UChar arabicIndicNumerals[10] = {
+ 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 0x0668, 0x0669
+ };
+ return toNumeric(value, arabicIndicNumerals);
+ }
+ case BinaryListStyle: {
+ static const UChar binaryNumerals[2] = {
+ '0', '1'
+ };
+ return toNumeric(value, binaryNumerals);
+ }
+ case Bengali: {
+ static const UChar bengaliNumerals[10] = {
+ 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 0x09EE, 0x09EF
+ };
+ return toNumeric(value, bengaliNumerals);
+ }
+ case Cambodian:
+ case Khmer: {
+ static const UChar khmerNumerals[10] = {
+ 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 0x17E8, 0x17E9
+ };
+ return toNumeric(value, khmerNumerals);
+ }
+ case Devanagari: {
+ static const UChar devanagariNumerals[10] = {
+ 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 0x096E, 0x096F
+ };
+ return toNumeric(value, devanagariNumerals);
+ }
+ case Gujarati: {
+ static const UChar gujaratiNumerals[10] = {
+ 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 0x0AEE, 0x0AEF
+ };
+ return toNumeric(value, gujaratiNumerals);
+ }
+ case Gurmukhi: {
+ static const UChar gurmukhiNumerals[10] = {
+ 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 0x0A6E, 0x0A6F
+ };
+ return toNumeric(value, gurmukhiNumerals);
+ }
+ case Kannada: {
+ static const UChar kannadaNumerals[10] = {
+ 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 0x0CEE, 0x0CEF
+ };
+ return toNumeric(value, kannadaNumerals);
+ }
+ case LowerHexadecimal: {
+ static const UChar lowerHexadecimalNumerals[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+ return toNumeric(value, lowerHexadecimalNumerals);
+ }
+ case Lao: {
+ static const UChar laoNumerals[10] = {
+ 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 0x0ED8, 0x0ED9
+ };
+ return toNumeric(value, laoNumerals);
+ }
+ case Malayalam: {
+ static const UChar malayalamNumerals[10] = {
+ 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 0x0D6E, 0x0D6F
+ };
+ return toNumeric(value, malayalamNumerals);
+ }
+ case Mongolian: {
+ static const UChar mongolianNumerals[10] = {
+ 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 0x1818, 0x1819
+ };
+ return toNumeric(value, mongolianNumerals);
+ }
+ case Myanmar: {
+ static const UChar myanmarNumerals[10] = {
+ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 0x1048, 0x1049
+ };
+ return toNumeric(value, myanmarNumerals);
+ }
+ case Octal: {
+ static const UChar octalNumerals[8] = {
+ '0', '1', '2', '3', '4', '5', '6', '7'
+ };
+ return toNumeric(value, octalNumerals);
+ }
+ case Oriya: {
+ static const UChar oriyaNumerals[10] = {
+ 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 0x0B6E, 0x0B6F
+ };
+ return toNumeric(value, oriyaNumerals);
+ }
+ case Persian:
+ case Urdu: {
+ static const UChar urduNumerals[10] = {
+ 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 0x06F8, 0x06F9
+ };
+ return toNumeric(value, urduNumerals);
+ }
+ case Telugu: {
+ static const UChar teluguNumerals[10] = {
+ 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 0x0C6E, 0x0C6F
+ };
+ return toNumeric(value, teluguNumerals);
+ }
+ case Tibetan: {
+ static const UChar tibetanNumerals[10] = {
+ 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 0x0F28, 0x0F29
+ };
+ return toNumeric(value, tibetanNumerals);
+ }
+ case Thai: {
+ static const UChar thaiNumerals[10] = {
+ 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59
+ };
+ return toNumeric(value, thaiNumerals);
+ }
+ case UpperHexadecimal: {
+ static const UChar upperHexadecimalNumerals[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ return toNumeric(value, upperHexadecimalNumerals);
+ }
+
+ case LowerAlpha:
+ case LowerLatin: {
+ static const UChar lowerLatinAlphabet[26] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
+ };
+ return toAlphabetic(value, lowerLatinAlphabet);
+ }
+ case UpperAlpha:
+ case UpperLatin: {
+ static const UChar upperLatinAlphabet[26] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+ };
+ return toAlphabetic(value, upperLatinAlphabet);
+ }
+ case LowerGreek: {
+ static const UChar lowerGreekAlphabet[24] = {
+ 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+ 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
+ 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
+ };
+ return toAlphabetic(value, lowerGreekAlphabet);
+ }
+
+ case Hiragana: {
+ // FIXME: This table comes from the CSS3 draft, and is probably
+ // incorrect, given the comments in that draft.
+ static const UChar hiraganaAlphabet[48] = {
+ 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
+ 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
+ 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
+ 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
+ 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
+ 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
+ };
+ return toAlphabetic(value, hiraganaAlphabet);
+ }
+ case HiraganaIroha: {
+ // FIXME: This table comes from the CSS3 draft, and is probably
+ // incorrect, given the comments in that draft.
+ static const UChar hiraganaIrohaAlphabet[47] = {
+ 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
+ 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
+ 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
+ 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
+ 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
+ 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
+ };
+ return toAlphabetic(value, hiraganaIrohaAlphabet);
+ }
+ case Katakana: {
+ // FIXME: This table comes from the CSS3 draft, and is probably
+ // incorrect, given the comments in that draft.
+ static const UChar katakanaAlphabet[48] = {
+ 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
+ 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
+ 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
+ 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
+ 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
+ 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
+ };
+ return toAlphabetic(value, katakanaAlphabet);
+ }
+ case KatakanaIroha: {
+ // FIXME: This table comes from the CSS3 draft, and is probably
+ // incorrect, given the comments in that draft.
+ static const UChar katakanaIrohaAlphabet[47] = {
+ 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
+ 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
+ 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
+ 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
+ 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
+ 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
+ };
+ return toAlphabetic(value, katakanaIrohaAlphabet);
+ }
+
+ case Afar:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAaEr: {
+ static const UChar ethiopicHalehameAaErAlphabet[18] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1260, 0x1270, 0x1290,
+ 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12E8, 0x12F0, 0x1308, 0x1338, 0x1348
+ };
+ return toAlphabetic(value, ethiopicHalehameAaErAlphabet);
+ }
+ case Amharic:
+ case EthiopicHalehameAmEt: {
+ static const UChar ethiopicHalehameAmEtAlphabet[33] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
+ 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8,
+ 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320,
+ 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
+ };
+ return toAlphabetic(value, ethiopicHalehameAmEtAlphabet);
+ }
+ case AmharicAbegede:
+ case EthiopicAbegedeAmEt: {
+ static const UChar ethiopicAbegedeAmEtAlphabet[33] = {
+ 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
+ 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
+ 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1228, 0x1230, 0x1238,
+ 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
+ };
+ return toAlphabetic(value, ethiopicAbegedeAmEtAlphabet);
+ }
+ case CjkEarthlyBranch: {
+ static const UChar cjkEarthlyBranchAlphabet[12] = {
+ 0x5B50, 0x4E11, 0x5BC5, 0x536F, 0x8FB0, 0x5DF3, 0x5348, 0x672A, 0x7533,
+ 0x9149, 0x620C, 0x4EA5
+ };
+ return toAlphabetic(value, cjkEarthlyBranchAlphabet);
+ }
+ case CjkHeavenlyStem: {
+ static const UChar cjkHeavenlyStemAlphabet[10] = {
+ 0x7532, 0x4E59, 0x4E19, 0x4E01, 0x620A, 0x5DF1, 0x5E9A, 0x8F9B, 0x58EC,
+ 0x7678
+ };
+ return toAlphabetic(value, cjkHeavenlyStemAlphabet);
+ }
+ case Ethiopic:
+ case EthiopicHalehameGez: {
+ static const UChar ethiopicHalehameGezAlphabet[26] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1240, 0x1260,
+ 0x1270, 0x1280, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
+ 0x12F0, 0x1308, 0x1320, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
+ };
+ return toAlphabetic(value, ethiopicHalehameGezAlphabet);
+ }
+ case EthiopicAbegede:
+ case EthiopicAbegedeGez: {
+ static const UChar ethiopicAbegedeGezAlphabet[26] = {
+ 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1200, 0x12C8, 0x12D8, 0x1210, 0x1320,
+ 0x12E8, 0x12A8, 0x1208, 0x1218, 0x1290, 0x1220, 0x12D0, 0x1348, 0x1338,
+ 0x1240, 0x1228, 0x1230, 0x1270, 0x1280, 0x1340, 0x1330, 0x1350
+ };
+ return toAlphabetic(value, ethiopicAbegedeGezAlphabet);
+ }
+ case HangulConsonant: {
+ static const UChar hangulConsonantAlphabet[14] = {
+ 0x3131, 0x3134, 0x3137, 0x3139, 0x3141, 0x3142, 0x3145, 0x3147, 0x3148,
+ 0x314A, 0x314B, 0x314C, 0x314D, 0x314E
+ };
+ return toAlphabetic(value, hangulConsonantAlphabet);
+ }
+ case Hangul: {
+ static const UChar hangulAlphabet[14] = {
+ 0xAC00, 0xB098, 0xB2E4, 0xB77C, 0xB9C8, 0xBC14, 0xC0AC, 0xC544, 0xC790,
+ 0xCC28, 0xCE74, 0xD0C0, 0xD30C, 0xD558
+ };
+ return toAlphabetic(value, hangulAlphabet);
+ }
+ case Oromo:
+ case EthiopicHalehameOmEt: {
+ static const UChar ethiopicHalehameOmEtAlphabet[25] = {
+ 0x1200, 0x1208, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260, 0x1270,
+ 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0, 0x12F8,
+ 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
+ };
+ return toAlphabetic(value, ethiopicHalehameOmEtAlphabet);
+ }
+ case Sidama:
+ case EthiopicHalehameSidEt: {
+ static const UChar ethiopicHalehameSidEtAlphabet[26] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
+ 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12C8, 0x12E8, 0x12F0,
+ 0x12F8, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348
+ };
+ return toAlphabetic(value, ethiopicHalehameSidEtAlphabet);
+ }
+ case Somali:
+ case EthiopicHalehameSoEt: {
+ static const UChar ethiopicHalehameSoEtAlphabet[22] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
+ 0x1270, 0x1290, 0x12A0, 0x12A8, 0x12B8, 0x12C8, 0x12D0, 0x12E8, 0x12F0,
+ 0x1300, 0x1308, 0x1338, 0x1348
+ };
+ return toAlphabetic(value, ethiopicHalehameSoEtAlphabet);
+ }
+ case Tigre:
+ case EthiopicHalehameTig: {
+ static const UChar ethiopicHalehameTigAlphabet[27] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1260,
+ 0x1270, 0x1278, 0x1290, 0x12A0, 0x12A8, 0x12C8, 0x12D0, 0x12D8, 0x12E8,
+ 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328, 0x1338, 0x1330, 0x1348, 0x1350
+ };
+ return toAlphabetic(value, ethiopicHalehameTigAlphabet);
+ }
+ case TigrinyaEr:
+ case EthiopicHalehameTiEr: {
+ static const UChar ethiopicHalehameTiErAlphabet[31] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1228, 0x1230, 0x1238, 0x1240, 0x1250,
+ 0x1260, 0x1270, 0x1278, 0x1290, 0x1298, 0x12A0, 0x12A8, 0x12B8, 0x12C8,
+ 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308, 0x1320, 0x1328,
+ 0x1330, 0x1338, 0x1348, 0x1350
+ };
+ return toAlphabetic(value, ethiopicHalehameTiErAlphabet);
+ }
+ case TigrinyaErAbegede:
+ case EthiopicAbegedeTiEr: {
+ static const UChar ethiopicAbegedeTiErAlphabet[31] = {
+ 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
+ 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
+ 0x1298, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230, 0x1238,
+ 0x1270, 0x1278, 0x1330, 0x1350
+ };
+ return toAlphabetic(value, ethiopicAbegedeTiErAlphabet);
+ }
+ case TigrinyaEt:
+ case EthiopicHalehameTiEt: {
+ static const UChar ethiopicHalehameTiEtAlphabet[34] = {
+ 0x1200, 0x1208, 0x1210, 0x1218, 0x1220, 0x1228, 0x1230, 0x1238, 0x1240,
+ 0x1250, 0x1260, 0x1270, 0x1278, 0x1280, 0x1290, 0x1298, 0x12A0, 0x12A8,
+ 0x12B8, 0x12C8, 0x12D0, 0x12D8, 0x12E0, 0x12E8, 0x12F0, 0x1300, 0x1308,
+ 0x1320, 0x1328, 0x1330, 0x1338, 0x1340, 0x1348, 0x1350
+ };
+ return toAlphabetic(value, ethiopicHalehameTiEtAlphabet);
+ }
+ case TigrinyaEtAbegede:
+ case EthiopicAbegedeTiEt: {
+ static const UChar ethiopicAbegedeTiEtAlphabet[34] = {
+ 0x12A0, 0x1260, 0x1308, 0x12F0, 0x1300, 0x1200, 0x12C8, 0x12D8, 0x12E0,
+ 0x1210, 0x1320, 0x1328, 0x12E8, 0x12A8, 0x12B8, 0x1208, 0x1218, 0x1290,
+ 0x1298, 0x1220, 0x12D0, 0x1348, 0x1338, 0x1240, 0x1250, 0x1228, 0x1230,
+ 0x1238, 0x1270, 0x1278, 0x1280, 0x1340, 0x1330, 0x1350
+ };
+ return toAlphabetic(value, ethiopicAbegedeTiEtAlphabet);
+ }
+ case UpperGreek: {
+ static const UChar upperGreekAlphabet[24] = {
+ 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399,
+ 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3,
+ 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9
+ };
+ return toAlphabetic(value, upperGreekAlphabet);
+ }
+ case LowerNorwegian: {
+ static const UChar lowerNorwegianAlphabet[29] = {
+ 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069,
+ 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072,
+ 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x00E6,
+ 0x00F8, 0x00E5
+ };
+ return toAlphabetic(value, lowerNorwegianAlphabet);
+ }
+ case UpperNorwegian: {
+ static const UChar upperNorwegianAlphabet[29] = {
+ 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049,
+ 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
+ 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x00C6,
+ 0x00D8, 0x00C5
+ };
+ return toAlphabetic(value, upperNorwegianAlphabet);
+ }
+ case CJKIdeographic: {
+ static const UChar traditionalChineseInformalTable[16] = {
+ 0x842C, 0x5104, 0x5146,
+ 0x5341, 0x767E, 0x5343,
+ 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
+ 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
+ };
+ return toCJKIdeographic(value, traditionalChineseInformalTable);
+ }
+
+ case LowerRoman:
+ return toRoman(value, false);
+ case UpperRoman:
+ return toRoman(value, true);
+
+ case Armenian:
+ case UpperArmenian:
+ // CSS3 says "armenian" means "lower-armenian".
+ // But the CSS2.1 test suite contains uppercase test results for "armenian",
+ // so we'll match the test suite.
+ return toArmenian(value, true);
+ case LowerArmenian:
+ return toArmenian(value, false);
+ case Georgian:
+ return toGeorgian(value);
+ case Hebrew:
+ return toHebrew(value);
+ }
+
+ ASSERT_NOT_REACHED();
+ return "";
+}
+
+RenderListMarker::RenderListMarker(RenderListItem* item)
+ : RenderBox(item->document())
+ , m_listItem(item)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is Inline
+ setReplaced(true); // pretend to be replaced
+}
+
+RenderListMarker::~RenderListMarker()
+{
+ if (m_image)
+ m_image->removeClient(this);
+}
+
+void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
+ setNeedsLayoutAndPrefWidthsRecalc();
+
+ RenderBox::styleWillChange(diff, newStyle);
+}
+
+void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBox::styleDidChange(diff, oldStyle);
+
+ if (m_image != style()->listStyleImage()) {
+ if (m_image)
+ m_image->removeClient(this);
+ m_image = style()->listStyleImage();
+ if (m_image)
+ m_image->addClient(this);
+ }
+}
+
+InlineBox* RenderListMarker::createInlineBox()
+{
+ InlineBox* result = RenderBox::createInlineBox();
+ result->setIsText(isText());
+ return result;
+}
+
+bool RenderListMarker::isImage() const
+{
+ return m_image && !m_image->errorOccurred();
+}
+
+IntRect RenderListMarker::localSelectionRect()
+{
+ InlineBox* box = inlineBoxWrapper();
+ if (!box)
+ return IntRect(0, 0, width(), height());
+ RootInlineBox* root = m_inlineBoxWrapper->root();
+ int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
+ if (root->block()->style()->isHorizontalWritingMode())
+ return IntRect(0, newLogicalTop, width(), root->selectionHeight());
+ return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
+}
+
+void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.phase != PaintPhaseForeground)
+ return;
+
+ if (style()->visibility() != VISIBLE)
+ return;
+
+ IntPoint boxOrigin(tx + x(), ty + y());
+ IntRect overflowRect(visualOverflowRect());
+ overflowRect.move(boxOrigin.x(), boxOrigin.y());
+ overflowRect.inflate(maximalOutlineSize(paintInfo.phase));
+
+ if (!paintInfo.rect.intersects(overflowRect))
+ return;
+
+ IntRect box(boxOrigin, IntSize(width(), height()));
+
+ IntRect marker = getRelativeMarkerRect();
+ marker.move(boxOrigin.x(), boxOrigin.y());
+
+ GraphicsContext* context = paintInfo.context;
+
+ if (isImage()) {
+#if PLATFORM(MAC)
+ if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
+ paintCustomHighlight(tx, ty, style()->highlight(), true);
+#endif
+ context->drawImage(m_image->image(this, marker.size()), style()->colorSpace(), marker);
+ if (selectionState() != SelectionNone) {
+ IntRect selRect = localSelectionRect();
+ selRect.move(boxOrigin.x(), boxOrigin.y());
+ context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace());
+ }
+ return;
+ }
+
+#if PLATFORM(MAC)
+ // FIXME: paint gap between marker and list item proper
+ if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
+ paintCustomHighlight(tx, ty, style()->highlight(), true);
+#endif
+
+ if (selectionState() != SelectionNone) {
+ IntRect selRect = localSelectionRect();
+ selRect.move(boxOrigin.x(), boxOrigin.y());
+ context->fillRect(selRect, selectionBackgroundColor(), style()->colorSpace());
+ }
+
+ const Color color(style()->visitedDependentColor(CSSPropertyColor));
+ context->setStrokeColor(color, style()->colorSpace());
+ context->setStrokeStyle(SolidStroke);
+ context->setStrokeThickness(1.0f);
+ context->setFillColor(color, style()->colorSpace());
+
+ EListStyleType type = style()->listStyleType();
+ switch (type) {
+ case Disc:
+ context->drawEllipse(marker);
+ return;
+ case Circle:
+ context->setFillColor(Color::transparent, ColorSpaceDeviceRGB);
+ context->drawEllipse(marker);
+ return;
+ case Square:
+ context->drawRect(marker);
+ return;
+ case NoneListStyle:
+ return;
+ case Afar:
+ case Amharic:
+ case AmharicAbegede:
+ case ArabicIndic:
+ case Armenian:
+ case BinaryListStyle:
+ case Bengali:
+ case Cambodian:
+ case CJKIdeographic:
+ case CjkEarthlyBranch:
+ case CjkHeavenlyStem:
+ case DecimalLeadingZero:
+ case DecimalListStyle:
+ case Devanagari:
+ case Ethiopic:
+ case EthiopicAbegede:
+ case EthiopicAbegedeAmEt:
+ case EthiopicAbegedeGez:
+ case EthiopicAbegedeTiEr:
+ case EthiopicAbegedeTiEt:
+ case EthiopicHalehameAaEr:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAmEt:
+ case EthiopicHalehameGez:
+ case EthiopicHalehameOmEt:
+ case EthiopicHalehameSidEt:
+ case EthiopicHalehameSoEt:
+ case EthiopicHalehameTiEr:
+ case EthiopicHalehameTiEt:
+ case EthiopicHalehameTig:
+ case Georgian:
+ case Gujarati:
+ case Gurmukhi:
+ case Hangul:
+ case HangulConsonant:
+ case Hebrew:
+ case Hiragana:
+ case HiraganaIroha:
+ case Kannada:
+ case Katakana:
+ case KatakanaIroha:
+ case Khmer:
+ case Lao:
+ case LowerAlpha:
+ case LowerArmenian:
+ case LowerGreek:
+ case LowerHexadecimal:
+ case LowerLatin:
+ case LowerNorwegian:
+ case LowerRoman:
+ case Malayalam:
+ case Mongolian:
+ case Myanmar:
+ case Octal:
+ case Oriya:
+ case Oromo:
+ case Persian:
+ case Sidama:
+ case Somali:
+ case Telugu:
+ case Thai:
+ case Tibetan:
+ case Tigre:
+ case TigrinyaEr:
+ case TigrinyaErAbegede:
+ case TigrinyaEt:
+ case TigrinyaEtAbegede:
+ case UpperAlpha:
+ case UpperArmenian:
+ case UpperGreek:
+ case UpperHexadecimal:
+ case UpperLatin:
+ case UpperNorwegian:
+ case UpperRoman:
+ case Urdu:
+ case Asterisks:
+ case Footnotes:
+ break;
+ }
+ if (m_text.isEmpty())
+ return;
+
+ TextRun textRun(m_text);
+
+ if (!style()->isHorizontalWritingMode()) {
+ marker.move(-boxOrigin.x(), -boxOrigin.y());
+ marker = marker.transposedRect();
+ marker.move(box.x(), box.y() - logicalHeight());
+ context->save();
+ context->translate(marker.x(), marker.bottom());
+ context->rotate(static_cast<float>(deg2rad(90.)));
+ context->translate(-marker.x(), -marker.bottom());
+ }
+
+ IntPoint textOrigin = IntPoint(marker.x(), marker.y() + style()->font().ascent());
+
+ if (type == Asterisks || type == Footnotes)
+ context->drawText(style()->font(), textRun, textOrigin);
+ else {
+ // Text is not arbitrary. We can judge whether it's RTL from the first character,
+ // and we only need to handle the direction RightToLeft for now.
+ bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
+ Vector<UChar> reversedText;
+ if (textNeedsReversing) {
+ int length = m_text.length();
+ reversedText.grow(length);
+ for (int i = 0; i < length; ++i)
+ reversedText[length - i - 1] = m_text[i];
+ textRun = TextRun(reversedText.data(), length);
+ }
+
+ const Font& font = style()->font();
+ const UChar suffix = listMarkerSuffix(type, m_listItem->value());
+ if (style()->isLeftToRightDirection()) {
+ int width = font.width(textRun);
+ context->drawText(style()->font(), textRun, textOrigin);
+ UChar suffixSpace[2] = { suffix, ' ' };
+ context->drawText(style()->font(), TextRun(suffixSpace, 2), textOrigin + IntSize(width, 0));
+ } else {
+ UChar spaceSuffix[2] = { ' ', suffix };
+ TextRun spaceSuffixRun(spaceSuffix, 2);
+ int width = font.width(spaceSuffixRun);
+ context->drawText(style()->font(), spaceSuffixRun, textOrigin);
+ context->drawText(style()->font(), textRun, textOrigin + IntSize(width, 0));
+ }
+ }
+
+ if (!style()->isHorizontalWritingMode())
+ context->restore();
+}
+
+void RenderListMarker::layout()
+{
+ ASSERT(needsLayout());
+
+ if (isImage()) {
+ setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
+ setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
+ } else {
+ setLogicalWidth(minPreferredLogicalWidth());
+ setLogicalHeight(style()->font().height());
+ }
+
+ setMarginStart(0);
+ setMarginEnd(0);
+
+ Length startMargin = style()->marginStart();
+ Length endMargin = style()->marginEnd();
+ if (startMargin.isFixed())
+ setMarginStart(startMargin.value());
+ if (endMargin.isFixed())
+ setMarginEnd(endMargin.value());
+
+ setNeedsLayout(false);
+}
+
+void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
+{
+ // A list marker can't have a background or border image, so no need to call the base class method.
+ if (o != m_image->data())
+ return;
+
+ if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
+ setNeedsLayoutAndPrefWidthsRecalc();
+ else
+ repaint();
+}
+
+void RenderListMarker::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ m_text = "";
+
+ const Font& font = style()->font();
+
+ if (isImage()) {
+ // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
+ // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
+ int bulletWidth = font.ascent() / 2;
+ m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth));
+ IntSize imageSize = m_image->imageSize(this, style()->effectiveZoom());
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = style()->isHorizontalWritingMode() ? imageSize.width() : imageSize.height();
+ setPreferredLogicalWidthsDirty(false);
+ updateMargins();
+ return;
+ }
+
+ int logicalWidth = 0;
+ EListStyleType type = style()->listStyleType();
+ switch (type) {
+ case NoneListStyle:
+ break;
+ case Asterisks:
+ case Footnotes:
+ m_text = listMarkerText(type, m_listItem->value());
+ logicalWidth = font.width(m_text); // no suffix for these types
+ break;
+ case Circle:
+ case Disc:
+ case Square:
+ m_text = listMarkerText(type, 0); // value is ignored for these types
+ logicalWidth = (font.ascent() * 2 / 3 + 1) / 2 + 2;
+ break;
+ case Afar:
+ case Amharic:
+ case AmharicAbegede:
+ case ArabicIndic:
+ case Armenian:
+ case BinaryListStyle:
+ case Bengali:
+ case Cambodian:
+ case CJKIdeographic:
+ case CjkEarthlyBranch:
+ case CjkHeavenlyStem:
+ case DecimalLeadingZero:
+ case DecimalListStyle:
+ case Devanagari:
+ case Ethiopic:
+ case EthiopicAbegede:
+ case EthiopicAbegedeAmEt:
+ case EthiopicAbegedeGez:
+ case EthiopicAbegedeTiEr:
+ case EthiopicAbegedeTiEt:
+ case EthiopicHalehameAaEr:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAmEt:
+ case EthiopicHalehameGez:
+ case EthiopicHalehameOmEt:
+ case EthiopicHalehameSidEt:
+ case EthiopicHalehameSoEt:
+ case EthiopicHalehameTiEr:
+ case EthiopicHalehameTiEt:
+ case EthiopicHalehameTig:
+ case Georgian:
+ case Gujarati:
+ case Gurmukhi:
+ case Hangul:
+ case HangulConsonant:
+ case Hebrew:
+ case Hiragana:
+ case HiraganaIroha:
+ case Kannada:
+ case Katakana:
+ case KatakanaIroha:
+ case Khmer:
+ case Lao:
+ case LowerAlpha:
+ case LowerArmenian:
+ case LowerGreek:
+ case LowerHexadecimal:
+ case LowerLatin:
+ case LowerNorwegian:
+ case LowerRoman:
+ case Malayalam:
+ case Mongolian:
+ case Myanmar:
+ case Octal:
+ case Oriya:
+ case Oromo:
+ case Persian:
+ case Sidama:
+ case Somali:
+ case Telugu:
+ case Thai:
+ case Tibetan:
+ case Tigre:
+ case TigrinyaEr:
+ case TigrinyaErAbegede:
+ case TigrinyaEt:
+ case TigrinyaEtAbegede:
+ case UpperAlpha:
+ case UpperArmenian:
+ case UpperGreek:
+ case UpperHexadecimal:
+ case UpperLatin:
+ case UpperNorwegian:
+ case UpperRoman:
+ case Urdu:
+ m_text = listMarkerText(type, m_listItem->value());
+ if (m_text.isEmpty())
+ logicalWidth = 0;
+ else {
+ int itemWidth = font.width(m_text);
+ UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
+ int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2));
+ logicalWidth = itemWidth + suffixSpaceWidth;
+ }
+ break;
+ }
+
+ m_minPreferredLogicalWidth = logicalWidth;
+ m_maxPreferredLogicalWidth = logicalWidth;
+
+ setPreferredLogicalWidthsDirty(false);
+
+ updateMargins();
+}
+
+void RenderListMarker::updateMargins()
+{
+ const Font& font = style()->font();
+
+ int marginStart = 0;
+ int marginEnd = 0;
+
+ if (isInside()) {
+ if (isImage())
+ marginEnd = cMarkerPadding;
+ else switch (style()->listStyleType()) {
+ case Disc:
+ case Circle:
+ case Square:
+ marginStart = -1;
+ marginEnd = font.ascent() - minPreferredLogicalWidth() + 1;
+ break;
+ default:
+ break;
+ }
+ } else {
+ if (style()->isLeftToRightDirection()) {
+ if (isImage())
+ marginStart = -minPreferredLogicalWidth() - cMarkerPadding;
+ else {
+ int offset = font.ascent() * 2 / 3;
+ switch (style()->listStyleType()) {
+ case Disc:
+ case Circle:
+ case Square:
+ marginStart = -offset - cMarkerPadding - 1;
+ break;
+ case NoneListStyle:
+ break;
+ default:
+ marginStart = m_text.isEmpty() ? 0 : -minPreferredLogicalWidth() - offset / 2;
+ }
+ }
+ marginEnd = -marginStart - minPreferredLogicalWidth();
+ } else {
+ if (isImage())
+ marginEnd = cMarkerPadding;
+ else {
+ int offset = font.ascent() * 2 / 3;
+ switch (style()->listStyleType()) {
+ case Disc:
+ case Circle:
+ case Square:
+ marginEnd = offset + cMarkerPadding + 1 - minPreferredLogicalWidth();
+ break;
+ case NoneListStyle:
+ break;
+ default:
+ marginEnd = m_text.isEmpty() ? 0 : offset / 2;
+ }
+ }
+ marginStart = -marginEnd - minPreferredLogicalWidth();
+ }
+
+ }
+
+ style()->setMarginStart(Length(marginStart, Fixed));
+ style()->setMarginEnd(Length(marginEnd, Fixed));
+}
+
+int RenderListMarker::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
+{
+ if (!isImage())
+ return m_listItem->lineHeight(firstLine, direction, PositionOfInteriorLineBoxes);
+ return RenderBox::lineHeight(firstLine, direction, linePositionMode);
+}
+
+int RenderListMarker::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
+{
+ if (!isImage())
+ return m_listItem->baselinePosition(baselineType, firstLine, direction, PositionOfInteriorLineBoxes);
+ return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
+}
+
+String RenderListMarker::suffix() const
+{
+ EListStyleType type = style()->listStyleType();
+ const UChar suffix = listMarkerSuffix(type, m_listItem->value());
+
+ Vector<UChar> resultVector;
+ resultVector.append(suffix);
+
+ // If the suffix is not ' ', an extra space is needed
+ if (suffix != ' ') {
+ if (style()->isLeftToRightDirection())
+ resultVector.append(' ');
+ else
+ resultVector.prepend(' ');
+ }
+
+ return String::adopt(resultVector);
+}
+
+bool RenderListMarker::isInside() const
+{
+ return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
+}
+
+IntRect RenderListMarker::getRelativeMarkerRect()
+{
+ if (isImage())
+ return IntRect(0, 0, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
+
+ IntRect relativeRect;
+ EListStyleType type = style()->listStyleType();
+ switch (type) {
+ case Asterisks:
+ case Footnotes: {
+ const Font& font = style()->font();
+ relativeRect = IntRect(0, 0, font.width(m_text), font.height());
+ break;
+ }
+ case Disc:
+ case Circle:
+ case Square: {
+ // FIXME: Are these particular rounding rules necessary?
+ const Font& font = style()->font();
+ int ascent = font.ascent();
+ int bulletWidth = (ascent * 2 / 3 + 1) / 2;
+ relativeRect = IntRect(1, 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
+ break;
+ }
+ case NoneListStyle:
+ return IntRect();
+ case Afar:
+ case Amharic:
+ case AmharicAbegede:
+ case ArabicIndic:
+ case Armenian:
+ case BinaryListStyle:
+ case Bengali:
+ case Cambodian:
+ case CJKIdeographic:
+ case CjkEarthlyBranch:
+ case CjkHeavenlyStem:
+ case DecimalLeadingZero:
+ case DecimalListStyle:
+ case Devanagari:
+ case Ethiopic:
+ case EthiopicAbegede:
+ case EthiopicAbegedeAmEt:
+ case EthiopicAbegedeGez:
+ case EthiopicAbegedeTiEr:
+ case EthiopicAbegedeTiEt:
+ case EthiopicHalehameAaEr:
+ case EthiopicHalehameAaEt:
+ case EthiopicHalehameAmEt:
+ case EthiopicHalehameGez:
+ case EthiopicHalehameOmEt:
+ case EthiopicHalehameSidEt:
+ case EthiopicHalehameSoEt:
+ case EthiopicHalehameTiEr:
+ case EthiopicHalehameTiEt:
+ case EthiopicHalehameTig:
+ case Georgian:
+ case Gujarati:
+ case Gurmukhi:
+ case Hangul:
+ case HangulConsonant:
+ case Hebrew:
+ case Hiragana:
+ case HiraganaIroha:
+ case Kannada:
+ case Katakana:
+ case KatakanaIroha:
+ case Khmer:
+ case Lao:
+ case LowerAlpha:
+ case LowerArmenian:
+ case LowerGreek:
+ case LowerHexadecimal:
+ case LowerLatin:
+ case LowerNorwegian:
+ case LowerRoman:
+ case Malayalam:
+ case Mongolian:
+ case Myanmar:
+ case Octal:
+ case Oriya:
+ case Oromo:
+ case Persian:
+ case Sidama:
+ case Somali:
+ case Telugu:
+ case Thai:
+ case Tibetan:
+ case Tigre:
+ case TigrinyaEr:
+ case TigrinyaErAbegede:
+ case TigrinyaEt:
+ case TigrinyaEtAbegede:
+ case UpperAlpha:
+ case UpperArmenian:
+ case UpperGreek:
+ case UpperHexadecimal:
+ case UpperLatin:
+ case UpperNorwegian:
+ case UpperRoman:
+ case Urdu:
+ if (m_text.isEmpty())
+ return IntRect();
+ const Font& font = style()->font();
+ int itemWidth = font.width(m_text);
+ UChar suffixSpace[2] = { listMarkerSuffix(type, m_listItem->value()), ' ' };
+ int suffixSpaceWidth = font.width(TextRun(suffixSpace, 2));
+ relativeRect = IntRect(0, 0, itemWidth + suffixSpaceWidth, font.height());
+ }
+
+ if (!style()->isHorizontalWritingMode()) {
+ relativeRect = relativeRect.transposedRect();
+ relativeRect.setX(width() - relativeRect.x() - relativeRect.width());
+ }
+
+ return relativeRect;
+}
+
+void RenderListMarker::setSelectionState(SelectionState state)
+{
+ RenderBox::setSelectionState(state);
+ if (InlineBox* box = inlineBoxWrapper())
+ if (RootInlineBox* root = box->root())
+ root->setHasSelectedChildren(state != SelectionNone);
+ containingBlock()->setSelectionState(state);
+}
+
+IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
+{
+ ASSERT(!needsLayout());
+
+ if (selectionState() == SelectionNone || !inlineBoxWrapper())
+ return IntRect();
+
+ RootInlineBox* root = inlineBoxWrapper()->root();
+ IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
+
+ if (clipToVisibleContent)
+ computeRectForRepaint(repaintContainer, rect);
+ else
+ rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
+
+ return rect;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderListMarker.h b/Source/WebCore/rendering/RenderListMarker.h
new file mode 100644
index 0000000..d23e674
--- /dev/null
+++ b/Source/WebCore/rendering/RenderListMarker.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 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 RenderListMarker_h
+#define RenderListMarker_h
+
+#include "RenderBox.h"
+
+namespace WebCore {
+
+class RenderListItem;
+
+String listMarkerText(EListStyleType, int value);
+
+// Used to render the list item's marker.
+// The RenderListMarker always has to be a child of a RenderListItem.
+class RenderListMarker : public RenderBox {
+public:
+ RenderListMarker(RenderListItem*);
+ virtual ~RenderListMarker();
+
+ virtual void computePreferredLogicalWidths();
+
+ const String& text() const { return m_text; }
+ String suffix() const;
+
+ bool isInside() const;
+
+private:
+ virtual const char* renderName() const { return "RenderListMarker"; }
+
+ virtual bool isListMarker() const { return true; }
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual void layout();
+
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+ virtual InlineBox* createInlineBox();
+
+ virtual int lineHeight(bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
+ virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
+
+ bool isImage() const;
+ bool isText() const { return !isImage(); }
+
+ virtual void setSelectionState(SelectionState);
+ virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true);
+ virtual bool canBeSelectionLeaf() const { return true; }
+
+ void updateMargins();
+
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ IntRect getRelativeMarkerRect();
+ IntRect localSelectionRect();
+
+ String m_text;
+ RefPtr<StyleImage> m_image;
+ RenderListItem* m_listItem;
+};
+
+inline RenderListMarker* toRenderListMarker(RenderObject* object)
+{
+ ASSERT(!object || object->isListMarker());
+ return static_cast<RenderListMarker*>(object);
+}
+
+inline const RenderListMarker* toRenderListMarker(const RenderObject* object)
+{
+ ASSERT(!object || object->isListMarker());
+ return static_cast<const RenderListMarker*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderListMarker(const RenderListMarker*);
+
+} // namespace WebCore
+
+#endif // RenderListMarker_h
diff --git a/Source/WebCore/rendering/RenderMarquee.cpp b/Source/WebCore/rendering/RenderMarquee.cpp
new file mode 100644
index 0000000..1c08831
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMarquee.cpp
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 "RenderMarquee.h"
+
+#include "FrameView.h"
+#include "HTMLMarqueeElement.h"
+#include "HTMLNames.h"
+#include "RenderLayer.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderMarquee::RenderMarquee(RenderLayer* l)
+ : m_layer(l), m_currentLoop(0)
+ , m_totalLoops(0)
+ , m_timer(this, &RenderMarquee::timerFired)
+ , m_start(0), m_end(0), m_speed(0), m_reset(false)
+ , m_suspended(false), m_stopped(false), m_direction(MAUTO)
+{
+}
+
+RenderMarquee::~RenderMarquee()
+{
+}
+
+int RenderMarquee::marqueeSpeed() const
+{
+ int result = m_layer->renderer()->style()->marqueeSpeed();
+ Node* n = m_layer->renderer()->node();
+ if (n && n->hasTagName(marqueeTag)) {
+ HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(n);
+ result = max(result, marqueeElt->minimumDelay());
+ }
+ return result;
+}
+
+EMarqueeDirection RenderMarquee::direction() const
+{
+ // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
+ // For now just map MAUTO to MBACKWARD
+ EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
+ TextDirection dir = m_layer->renderer()->style()->direction();
+ if (result == MAUTO)
+ result = MBACKWARD;
+ if (result == MFORWARD)
+ result = (dir == LTR) ? MRIGHT : MLEFT;
+ if (result == MBACKWARD)
+ result = (dir == LTR) ? MLEFT : MRIGHT;
+
+ // Now we have the real direction. Next we check to see if the increment is negative.
+ // If so, then we reverse the direction.
+ Length increment = m_layer->renderer()->style()->marqueeIncrement();
+ if (increment.isNegative())
+ result = static_cast<EMarqueeDirection>(-result);
+
+ return result;
+}
+
+bool RenderMarquee::isHorizontal() const
+{
+ return direction() == MLEFT || direction() == MRIGHT;
+}
+
+int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
+{
+ RenderBox* box = m_layer->renderBox();
+ ASSERT(box);
+ RenderStyle* s = box->style();
+ if (isHorizontal()) {
+ bool ltr = s->isLeftToRightDirection();
+ int clientWidth = box->clientWidth();
+ int contentWidth = ltr ? box->rightLayoutOverflow() : box->leftLayoutOverflow();
+ if (ltr)
+ contentWidth += (box->paddingRight() - box->borderLeft());
+ else {
+ contentWidth = box->width() - contentWidth;
+ contentWidth += (box->paddingLeft() - box->borderRight());
+ }
+ if (dir == MRIGHT) {
+ if (stopAtContentEdge)
+ return max(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? contentWidth : clientWidth;
+ }
+ else {
+ if (stopAtContentEdge)
+ return min(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+ else
+ return ltr ? -clientWidth : -contentWidth;
+ }
+ }
+ else {
+ int contentHeight = box->bottomLayoutOverflow() - box->borderTop() + box->paddingBottom();
+ int clientHeight = box->clientHeight();
+ if (dir == MUP) {
+ if (stopAtContentEdge)
+ return min(contentHeight - clientHeight, 0);
+ else
+ return -clientHeight;
+ }
+ else {
+ if (stopAtContentEdge)
+ return max(contentHeight - clientHeight, 0);
+ else
+ return contentHeight;
+ }
+ }
+}
+
+void RenderMarquee::start()
+{
+ if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().isZero()
+#if ENABLE(WCSS) && ENABLE(XHTMLMP)
+ || (m_layer->renderer()->document()->isXHTMLMPDocument() && !m_layer->renderer()->style()->marqueeLoopCount())
+#endif
+ )
+ return;
+
+ // 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, including the marquee.
+ FrameView* frameView = m_layer->renderer()->document()->view();
+ if (frameView)
+ frameView->pauseScheduledEvents();
+
+ if (!m_suspended && !m_stopped) {
+ if (isHorizontal())
+ m_layer->scrollToOffset(m_start, 0, false, false);
+ else
+ m_layer->scrollToOffset(0, m_start, false, false);
+ }
+ else {
+ m_suspended = false;
+ m_stopped = false;
+ }
+
+ m_timer.startRepeating(speed() * 0.001);
+
+ if (frameView)
+ frameView->resumeScheduledEvents();
+}
+
+void RenderMarquee::suspend()
+{
+ m_timer.stop();
+ m_suspended = true;
+}
+
+void RenderMarquee::stop()
+{
+ m_timer.stop();
+ m_stopped = true;
+}
+
+void RenderMarquee::updateMarqueePosition()
+{
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate) {
+ EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+ m_start = computePosition(direction(), behavior == MALTERNATE);
+ m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
+ if (!m_stopped)
+ start();
+ }
+}
+
+void RenderMarquee::updateMarqueeStyle()
+{
+ RenderStyle* s = m_layer->renderer()->style();
+
+ if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops))
+ m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
+
+ m_totalLoops = s->marqueeLoopCount();
+ m_direction = s->marqueeDirection();
+
+ if (m_layer->renderer()->isHTMLMarquee()) {
+ // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
+ // one loop.
+ if (m_totalLoops <= 0 && s->marqueeBehavior() == MSLIDE)
+ m_totalLoops = 1;
+
+ // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
+ // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
+ // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
+ // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
+ // marquee element.
+ // FIXME: Bring these up with the CSS WG.
+ if (isHorizontal() && m_layer->renderer()->childrenInline()) {
+ s->setWhiteSpace(NOWRAP);
+ s->setTextAlign(TAAUTO);
+ }
+ }
+
+ // Marquee height hack!! Make sure that, if it is a horizontal marquee, the height attribute is overridden
+ // if it is smaller than the font size. If it is a vertical marquee and height is not specified, we default
+ // to a marquee of 200px.
+ if (isHorizontal()) {
+ if (s->height().isFixed() && s->height().value() < s->fontSize())
+ s->setHeight(Length(s->fontSize(), Fixed));
+ } else if (s->height().isAuto()) //vertical marquee with no specified height
+ s->setHeight(Length(200, Fixed));
+
+ if (speed() != marqueeSpeed()) {
+ m_speed = marqueeSpeed();
+ if (m_timer.isActive())
+ m_timer.startRepeating(speed() * 0.001);
+ }
+
+ // Check the loop count to see if we should now stop.
+ bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+ if (activate && !m_timer.isActive())
+ m_layer->renderer()->setNeedsLayout(true);
+ else if (!activate && m_timer.isActive())
+ m_timer.stop();
+}
+
+void RenderMarquee::timerFired(Timer<RenderMarquee>*)
+{
+ if (m_layer->renderer()->needsLayout())
+ return;
+
+ if (m_reset) {
+ m_reset = false;
+ if (isHorizontal())
+ m_layer->scrollToXOffset(m_start);
+ else
+ m_layer->scrollToYOffset(m_start);
+ return;
+ }
+
+ RenderStyle* s = m_layer->renderer()->style();
+
+ int endPoint = m_end;
+ int range = m_end - m_start;
+ int newPos;
+ if (range == 0)
+ newPos = m_end;
+ else {
+ bool addIncrement = direction() == MUP || direction() == MLEFT;
+ bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
+ if (isReversed) {
+ // We're going in the reverse direction.
+ endPoint = m_start;
+ range = -range;
+ addIncrement = !addIncrement;
+ }
+ bool positive = range > 0;
+ int clientSize = (isHorizontal() ? m_layer->renderBox()->clientWidth() : m_layer->renderBox()->clientHeight());
+ int increment = abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize));
+ int currentPos = (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
+ newPos = currentPos + (addIncrement ? increment : -increment);
+ if (positive)
+ newPos = min(newPos, endPoint);
+ else
+ newPos = max(newPos, endPoint);
+ }
+
+ if (newPos == endPoint) {
+ m_currentLoop++;
+ if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops)
+ m_timer.stop();
+ else if (s->marqueeBehavior() != MALTERNATE)
+ m_reset = true;
+ }
+
+ if (isHorizontal())
+ m_layer->scrollToXOffset(newPos);
+ else
+ m_layer->scrollToYOffset(newPos);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderMarquee.h b/Source/WebCore/rendering/RenderMarquee.h
new file mode 100644
index 0000000..79998ed
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMarquee.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2003 Apple Computer, Inc.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 RenderMarquee_h
+#define RenderMarquee_h
+
+#include "Length.h"
+#include "RenderStyleConstants.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+class RenderLayer;
+
+// This class handles the auto-scrolling of layers with overflow: marquee.
+class RenderMarquee : public Noncopyable {
+public:
+ explicit RenderMarquee(RenderLayer*);
+ virtual ~RenderMarquee();
+
+ int speed() const { return m_speed; }
+ int marqueeSpeed() const;
+
+ EMarqueeDirection reverseDirection() const { return static_cast<EMarqueeDirection>(-direction()); }
+ EMarqueeDirection direction() const;
+
+ bool isHorizontal() const;
+
+ int computePosition(EMarqueeDirection, bool stopAtClientEdge);
+
+ void setEnd(int end) { m_end = end; }
+
+ void start();
+ void suspend();
+ void stop();
+
+ void updateMarqueeStyle();
+ void updateMarqueePosition();
+
+private:
+ void timerFired(Timer<RenderMarquee>*);
+
+ RenderLayer* m_layer;
+ int m_currentLoop;
+ int m_totalLoops;
+ Timer<RenderMarquee> m_timer;
+ int m_start;
+ int m_end;
+ int m_speed;
+ Length m_height;
+ bool m_reset: 1;
+ bool m_suspended : 1;
+ bool m_stopped : 1;
+ EMarqueeDirection m_direction : 4;
+};
+
+} // namespace WebCore
+
+#endif // RenderMarquee_h
diff --git a/Source/WebCore/rendering/RenderMedia.cpp b/Source/WebCore/rendering/RenderMedia.cpp
new file mode 100644
index 0000000..f59a995
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMedia.cpp
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2007, 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.
+ *
+ * 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"
+
+#if ENABLE(VIDEO)
+#include "RenderMedia.h"
+
+#include "EventNames.h"
+#include "FloatConversion.h"
+#include "HTMLNames.h"
+#include "MediaControlElements.h"
+#include "MouseEvent.h"
+#include "Page.h"
+#include "RenderLayer.h"
+#include "RenderTheme.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+
+#if PLATFORM(ANDROID)
+#define TOUCH_DELAY 4
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static const double cTimeUpdateRepeatDelay = 0.2;
+static const double cOpacityAnimationRepeatDelay = 0.05;
+
+RenderMedia::RenderMedia(HTMLMediaElement* video)
+ : RenderImage(video)
+ , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
+ , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
+ , m_mouseOver(false)
+ , m_opacityAnimationStartTime(0)
+ , m_opacityAnimationDuration(0)
+ , m_opacityAnimationFrom(0)
+ , m_opacityAnimationTo(1.0f)
+#if PLATFORM(ANDROID)
+ , m_lastTouch(0)
+#endif
+{
+ setImageResource(RenderImageResource::create());
+}
+
+RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize)
+ : RenderImage(video)
+ , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired)
+ , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired)
+ , m_mouseOver(false)
+ , m_opacityAnimationStartTime(0)
+ , m_opacityAnimationDuration(0)
+ , m_opacityAnimationFrom(0)
+ , m_opacityAnimationTo(1.0f)
+#if PLATFORM(ANDROID)
+ , m_lastTouch(0)
+#endif
+{
+ setImageResource(RenderImageResource::create());
+ setIntrinsicSize(intrinsicSize);
+}
+
+RenderMedia::~RenderMedia()
+{
+}
+
+void RenderMedia::destroy()
+{
+ if (m_controlsShadowRoot && m_controlsShadowRoot->renderer()) {
+
+ // detach the panel before removing the shadow renderer to prevent a crash in m_controlsShadowRoot->detach()
+ // when display: style changes
+ m_panel->detach();
+
+ removeChild(m_controlsShadowRoot->renderer());
+ m_controlsShadowRoot->detach();
+ m_controlsShadowRoot = 0;
+ }
+ RenderImage::destroy();
+}
+
+HTMLMediaElement* RenderMedia::mediaElement() const
+{
+ return static_cast<HTMLMediaElement*>(node());
+}
+
+MediaPlayer* RenderMedia::player() const
+{
+ return mediaElement()->player();
+}
+
+void RenderMedia::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderImage::styleDidChange(diff, oldStyle);
+
+ if (m_controlsShadowRoot) {
+ if (m_panel)
+ m_panel->updateStyle();
+ if (m_muteButton)
+ m_muteButton->updateStyle();
+ if (m_playButton)
+ m_playButton->updateStyle();
+ if (m_seekBackButton)
+ m_seekBackButton->updateStyle();
+ if (m_seekForwardButton)
+ m_seekForwardButton->updateStyle();
+ if (m_rewindButton)
+ m_rewindButton->updateStyle();
+ if (m_returnToRealtimeButton)
+ m_returnToRealtimeButton->updateStyle();
+ if (m_toggleClosedCaptionsButton)
+ m_toggleClosedCaptionsButton->updateStyle();
+ if (m_statusDisplay)
+ m_statusDisplay->updateStyle();
+ if (m_timelineContainer)
+ m_timelineContainer->updateStyle();
+ if (m_timeline)
+ m_timeline->updateStyle();
+ if (m_fullscreenButton)
+ m_fullscreenButton->updateStyle();
+ if (m_currentTimeDisplay)
+ m_currentTimeDisplay->updateStyle();
+ if (m_timeRemainingDisplay)
+ m_timeRemainingDisplay->updateStyle();
+ if (m_volumeSliderContainer)
+ m_volumeSliderContainer->updateStyle();
+ if (m_volumeSliderMuteButton)
+ m_volumeSliderMuteButton->updateStyle();
+ if (m_volumeSlider)
+ m_volumeSlider->updateStyle();
+ }
+}
+
+void RenderMedia::layout()
+{
+ IntSize oldSize = contentBoxRect().size();
+
+ RenderImage::layout();
+
+ RenderBox* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderBox() : 0;
+ if (!controlsRenderer)
+ return;
+ IntSize newSize = contentBoxRect().size();
+ if (newSize != oldSize || controlsRenderer->needsLayout()) {
+
+ if (m_currentTimeDisplay && m_timeRemainingDisplay) {
+ bool shouldShowTimeDisplays = shouldShowTimeDisplayControls();
+ m_currentTimeDisplay->setVisible(shouldShowTimeDisplays);
+ m_timeRemainingDisplay->setVisible(shouldShowTimeDisplays);
+ }
+
+ controlsRenderer->setLocation(borderLeft() + paddingLeft(), borderTop() + paddingTop());
+ controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed));
+ controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed));
+ controlsRenderer->setNeedsLayout(true, false);
+ controlsRenderer->layout();
+ setChildNeedsLayout(false);
+ }
+}
+
+void RenderMedia::createControlsShadowRoot()
+{
+ ASSERT(!m_controlsShadowRoot);
+ m_controlsShadowRoot = MediaControlShadowRootElement::create(mediaElement());
+ addChild(m_controlsShadowRoot->renderer());
+}
+
+void RenderMedia::createPanel()
+{
+ ASSERT(!m_panel);
+ m_panel = MediaControlElement::create(mediaElement(), MEDIA_CONTROLS_PANEL);
+ m_panel->attachToParent(m_controlsShadowRoot.get());
+}
+
+void RenderMedia::createMuteButton()
+{
+ ASSERT(!m_muteButton);
+ m_muteButton = MediaControlMuteButtonElement::create(mediaElement(), MediaControlMuteButtonElement::Controller);
+ m_muteButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createPlayButton()
+{
+ ASSERT(!m_playButton);
+ m_playButton = MediaControlPlayButtonElement::create(mediaElement());
+ m_playButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createSeekBackButton()
+{
+ ASSERT(!m_seekBackButton);
+ m_seekBackButton = MediaControlSeekButtonElement::create(mediaElement(), MEDIA_CONTROLS_SEEK_BACK_BUTTON);
+ m_seekBackButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createSeekForwardButton()
+{
+ ASSERT(!m_seekForwardButton);
+ m_seekForwardButton = MediaControlSeekButtonElement::create(mediaElement(), MEDIA_CONTROLS_SEEK_FORWARD_BUTTON);
+ m_seekForwardButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createRewindButton()
+{
+ ASSERT(!m_rewindButton);
+ m_rewindButton = MediaControlRewindButtonElement::create(mediaElement());
+ m_rewindButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createReturnToRealtimeButton()
+{
+ ASSERT(!m_returnToRealtimeButton);
+ m_returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(mediaElement());
+ m_returnToRealtimeButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createToggleClosedCaptionsButton()
+{
+ ASSERT(!m_toggleClosedCaptionsButton);
+ m_toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(mediaElement());
+ m_toggleClosedCaptionsButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createStatusDisplay()
+{
+ ASSERT(!m_statusDisplay);
+ m_statusDisplay = MediaControlStatusDisplayElement::create(mediaElement());
+ m_statusDisplay->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createTimelineContainer()
+{
+ ASSERT(!m_timelineContainer);
+ m_timelineContainer = MediaControlTimelineContainerElement::create(mediaElement());
+ m_timelineContainer->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createTimeline()
+{
+ ASSERT(!m_timeline);
+ m_timeline = MediaControlTimelineElement::create(mediaElement());
+ m_timeline->setAttribute(precisionAttr, "float");
+ m_timeline->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createVolumeSliderContainer()
+{
+ ASSERT(!m_volumeSliderContainer);
+ m_volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(mediaElement());
+ m_volumeSliderContainer->attachToParent(m_panel.get());
+}
+
+void RenderMedia::createVolumeSlider()
+{
+ ASSERT(!m_volumeSlider);
+ m_volumeSlider = MediaControlVolumeSliderElement::create(mediaElement());
+ m_volumeSlider->setAttribute(precisionAttr, "float");
+ m_volumeSlider->setAttribute(maxAttr, "1");
+ m_volumeSlider->setAttribute(valueAttr, String::number(mediaElement()->volume()));
+ m_volumeSlider->attachToParent(m_volumeSliderContainer.get());
+}
+
+void RenderMedia::createVolumeSliderMuteButton()
+{
+ ASSERT(!m_volumeSliderMuteButton);
+ m_volumeSliderMuteButton = MediaControlMuteButtonElement::create(mediaElement(), MediaControlMuteButtonElement::VolumeSlider);
+ m_volumeSliderMuteButton->attachToParent(m_volumeSliderContainer.get());
+
+}
+
+void RenderMedia::createCurrentTimeDisplay()
+{
+ ASSERT(!m_currentTimeDisplay);
+ m_currentTimeDisplay = MediaControlTimeDisplayElement::create(mediaElement(), MEDIA_CONTROLS_CURRENT_TIME_DISPLAY);
+ m_currentTimeDisplay->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createTimeRemainingDisplay()
+{
+ ASSERT(!m_timeRemainingDisplay);
+ m_timeRemainingDisplay = MediaControlTimeDisplayElement::create(mediaElement(), MEDIA_CONTROLS_TIME_REMAINING_DISPLAY);
+ m_timeRemainingDisplay->attachToParent(m_timelineContainer.get());
+}
+
+void RenderMedia::createFullscreenButton()
+{
+ ASSERT(!m_fullscreenButton);
+ m_fullscreenButton = MediaControlFullscreenButtonElement::create(mediaElement());
+ m_fullscreenButton->attachToParent(m_panel.get());
+}
+
+void RenderMedia::updateFromElement()
+{
+ updateControls();
+}
+
+void RenderMedia::updateControls()
+{
+ HTMLMediaElement* media = mediaElement();
+ if (!media->controls() || !media->inActiveDocument()) {
+ if (m_controlsShadowRoot) {
+ m_controlsShadowRoot->detach();
+ m_panel = 0;
+ m_muteButton = 0;
+ m_playButton = 0;
+ m_statusDisplay = 0;
+ m_timelineContainer = 0;
+ m_timeline = 0;
+ m_seekBackButton = 0;
+ m_seekForwardButton = 0;
+ m_rewindButton = 0;
+ m_returnToRealtimeButton = 0;
+ m_currentTimeDisplay = 0;
+ m_timeRemainingDisplay = 0;
+ m_fullscreenButton = 0;
+ m_volumeSliderContainer = 0;
+ m_volumeSlider = 0;
+ m_volumeSliderMuteButton = 0;
+ m_controlsShadowRoot = 0;
+ m_toggleClosedCaptionsButton = 0;
+ }
+ m_opacityAnimationTo = 1.0f;
+ m_opacityAnimationTimer.stop();
+ m_timeUpdateTimer.stop();
+ return;
+ }
+
+ if (!m_controlsShadowRoot) {
+ createControlsShadowRoot();
+ createPanel();
+ if (m_panel) {
+ createRewindButton();
+ createPlayButton();
+ createReturnToRealtimeButton();
+ createStatusDisplay();
+ createTimelineContainer();
+ if (m_timelineContainer) {
+ createCurrentTimeDisplay();
+ createTimeline();
+ createTimeRemainingDisplay();
+ }
+ createSeekBackButton();
+ createSeekForwardButton();
+ createToggleClosedCaptionsButton();
+ createFullscreenButton();
+ createMuteButton();
+ createVolumeSliderContainer();
+ if (m_volumeSliderContainer) {
+ createVolumeSlider();
+ createVolumeSliderMuteButton();
+ }
+ m_panel->attach();
+ }
+ }
+
+ if (media->canPlay()) {
+ if (m_timeUpdateTimer.isActive())
+ m_timeUpdateTimer.stop();
+ } else if (style()->visibility() == VISIBLE && m_timeline && m_timeline->renderer() && m_timeline->renderer()->style()->display() != NONE) {
+ m_timeUpdateTimer.startRepeating(cTimeUpdateRepeatDelay);
+ }
+
+
+ if (m_panel) {
+ // update() might alter the opacity of the element, especially if we are in the middle
+ // of an animation. This is the only element concerned as we animate only this element.
+ float opacityBeforeChangingStyle = m_panel->renderer() ? m_panel->renderer()->style()->opacity() : 0;
+ m_panel->update();
+ changeOpacity(m_panel.get(), opacityBeforeChangingStyle);
+ }
+ if (m_muteButton)
+ m_muteButton->update();
+ if (m_playButton)
+ m_playButton->update();
+ if (m_timelineContainer)
+ m_timelineContainer->update();
+ if (m_volumeSliderContainer)
+ m_volumeSliderContainer->update();
+ if (m_timeline)
+ m_timeline->update();
+ if (m_currentTimeDisplay)
+ m_currentTimeDisplay->update();
+ if (m_timeRemainingDisplay)
+ m_timeRemainingDisplay->update();
+ if (m_seekBackButton)
+ m_seekBackButton->update();
+ if (m_seekForwardButton)
+ m_seekForwardButton->update();
+ if (m_rewindButton)
+ m_rewindButton->update();
+ if (m_returnToRealtimeButton)
+ m_returnToRealtimeButton->update();
+ if (m_toggleClosedCaptionsButton)
+ m_toggleClosedCaptionsButton->update();
+ if (m_statusDisplay)
+ m_statusDisplay->update();
+ if (m_fullscreenButton)
+ m_fullscreenButton->update();
+ if (m_volumeSlider)
+ m_volumeSlider->update();
+ if (m_volumeSliderMuteButton)
+ m_volumeSliderMuteButton->update();
+
+ updateTimeDisplay();
+ updateControlVisibility();
+}
+
+void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*)
+{
+ if (m_timeline)
+ m_timeline->update(false);
+ updateTimeDisplay();
+}
+
+void RenderMedia::updateTimeDisplay()
+{
+ if (!m_currentTimeDisplay || !m_currentTimeDisplay->renderer() || m_currentTimeDisplay->renderer()->style()->display() == NONE || style()->visibility() != VISIBLE)
+ return;
+
+ float now = mediaElement()->currentTime();
+ float duration = mediaElement()->duration();
+
+ // Allow the theme to format the time
+ ExceptionCode ec;
+ m_currentTimeDisplay->setInnerText(theme()->formatMediaControlsCurrentTime(now, duration), ec);
+ m_currentTimeDisplay->setCurrentValue(now);
+ m_timeRemainingDisplay->setInnerText(theme()->formatMediaControlsRemainingTime(now, duration), ec);
+ m_timeRemainingDisplay->setCurrentValue(now - duration);
+}
+
+void RenderMedia::updateControlVisibility()
+{
+ if (!m_panel || !m_panel->renderer())
+ return;
+
+ // Don't fade for audio controls.
+ HTMLMediaElement* media = mediaElement();
+ if (!media->hasVideo())
+ return;
+
+ // Don't fade if the media element is not visible
+ if (style()->visibility() != VISIBLE)
+ return;
+
+#if PLATFORM(ANDROID)
+ if (WTF::currentTime() - m_lastTouch > TOUCH_DELAY)
+ m_mouseOver = false;
+ else
+ m_mouseOver = true;
+#endif
+
+ bool shouldHideController = !m_mouseOver && !media->canPlay();
+
+ // Do fading manually, css animations don't work with shadow trees
+
+ float animateFrom = m_panel->renderer()->style()->opacity();
+ float animateTo = shouldHideController ? 0.0f : 1.0f;
+
+ if (animateFrom == animateTo)
+ return;
+
+ if (m_opacityAnimationTimer.isActive()) {
+ if (m_opacityAnimationTo == animateTo)
+ return;
+ m_opacityAnimationTimer.stop();
+ }
+
+ if (animateFrom < animateTo)
+ m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeInDuration();
+ else
+ m_opacityAnimationDuration = m_panel->renderer()->theme()->mediaControlsFadeOutDuration();
+
+ m_opacityAnimationFrom = animateFrom;
+ m_opacityAnimationTo = animateTo;
+
+ m_opacityAnimationStartTime = currentTime();
+ m_opacityAnimationTimer.startRepeating(cOpacityAnimationRepeatDelay);
+}
+
+void RenderMedia::changeOpacity(HTMLElement* e, float opacity)
+{
+ if (!e || !e->renderer() || !e->renderer()->style())
+ return;
+ RefPtr<RenderStyle> s = RenderStyle::clone(e->renderer()->style());
+ s->setOpacity(opacity);
+ // z-index can't be auto if opacity is used
+ s->setZIndex(0);
+ e->renderer()->setStyle(s.release());
+}
+
+void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*)
+{
+ double time = currentTime() - m_opacityAnimationStartTime;
+ if (time >= m_opacityAnimationDuration) {
+ time = m_opacityAnimationDuration;
+ m_opacityAnimationTimer.stop();
+ }
+ float opacity = narrowPrecisionToFloat(m_opacityAnimationFrom + (m_opacityAnimationTo - m_opacityAnimationFrom) * time / m_opacityAnimationDuration);
+ changeOpacity(m_panel.get(), opacity);
+}
+
+void RenderMedia::updateVolumeSliderContainer(bool visible)
+{
+ if (!mediaElement()->hasAudio() || !m_volumeSliderContainer || !m_volumeSlider)
+ return;
+
+ if (visible && !m_volumeSliderContainer->isVisible()) {
+ if (!m_muteButton || !m_muteButton->renderer() || !m_muteButton->renderBox())
+ return;
+
+ RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement();
+ int height = s->height().isPercent() ? 0 : s->height().value();
+ int width = s->width().isPercent() ? 0 : s->width().value();
+ IntPoint offset = document()->page()->theme()->volumeSliderOffsetFromMuteButton(m_muteButton->renderer()->node(), IntSize(width, height));
+ int x = offset.x() + m_muteButton->renderBox()->offsetLeft();
+ int y = offset.y() + m_muteButton->renderBox()->offsetTop();
+
+ m_volumeSliderContainer->setPosition(x, y);
+ m_volumeSliderContainer->setVisible(true);
+ m_volumeSliderContainer->update();
+ m_volumeSlider->update();
+ } else if (!visible && m_volumeSliderContainer->isVisible()) {
+ m_volumeSliderContainer->setVisible(false);
+ m_volumeSliderContainer->updateStyle();
+ }
+}
+
+#if PLATFORM(ANDROID)
+void RenderMedia::updateLastTouch()
+{
+ m_lastTouch = WTF::currentTime();
+}
+#endif
+
+void RenderMedia::forwardEvent(Event* event)
+{
+#if PLATFORM(ANDROID)
+ if (event->isMouseEvent())
+ updateLastTouch();
+#if ENABLE(TOUCH_EVENTS)
+ if (event->isTouchEvent())
+ updateLastTouch();
+#endif
+#endif
+
+ if (event->isMouseEvent() && m_controlsShadowRoot) {
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ IntPoint point(mouseEvent->absoluteLocation());
+
+ bool defaultHandled = false;
+ if (m_volumeSliderMuteButton && m_volumeSliderMuteButton->hitTest(point)) {
+ m_volumeSliderMuteButton->defaultEventHandler(event);
+ defaultHandled = event->defaultHandled();
+ }
+
+ bool showVolumeSlider = false;
+ if (!defaultHandled && m_muteButton && m_muteButton->hitTest(point)) {
+ m_muteButton->defaultEventHandler(event);
+ if (event->type() != eventNames().mouseoutEvent)
+ showVolumeSlider = true;
+ }
+
+ if (m_volumeSliderContainer && m_volumeSliderContainer->hitTest(point))
+ showVolumeSlider = true;
+
+ if (m_volumeSlider && m_volumeSlider->hitTest(point)) {
+ m_volumeSlider->defaultEventHandler(event);
+ showVolumeSlider = true;
+ }
+
+ updateVolumeSliderContainer(showVolumeSlider);
+
+ if (m_playButton && m_playButton->hitTest(point))
+ m_playButton->defaultEventHandler(event);
+
+ if (m_seekBackButton && m_seekBackButton->hitTest(point))
+ m_seekBackButton->defaultEventHandler(event);
+
+ if (m_seekForwardButton && m_seekForwardButton->hitTest(point))
+ m_seekForwardButton->defaultEventHandler(event);
+
+ if (m_rewindButton && m_rewindButton->hitTest(point))
+ m_rewindButton->defaultEventHandler(event);
+
+ if (m_returnToRealtimeButton && m_returnToRealtimeButton->hitTest(point))
+ m_returnToRealtimeButton->defaultEventHandler(event);
+
+ if (m_toggleClosedCaptionsButton && m_toggleClosedCaptionsButton->hitTest(point))
+ m_toggleClosedCaptionsButton->defaultEventHandler(event);
+
+ if (m_timeline && m_timeline->hitTest(point))
+ m_timeline->defaultEventHandler(event);
+
+ if (m_fullscreenButton && m_fullscreenButton->hitTest(point))
+ m_fullscreenButton->defaultEventHandler(event);
+
+ if (event->type() == eventNames().mouseoverEvent) {
+ m_mouseOver = true;
+ updateControlVisibility();
+ }
+ if (event->type() == eventNames().mouseoutEvent) {
+ // When the scrollbar thumb captures mouse events, we should treat the mouse as still being over our renderer if the new target is a descendant
+ Node* mouseOverNode = mouseEvent->relatedTarget() ? mouseEvent->relatedTarget()->toNode() : 0;
+ RenderObject* mouseOverRenderer = mouseOverNode ? mouseOverNode->renderer() : 0;
+ m_mouseOver = mouseOverRenderer && mouseOverRenderer->isDescendantOf(this);
+ updateControlVisibility();
+ }
+ }
+}
+
+// We want the timeline slider to be at least 100 pixels wide.
+static const int minWidthToDisplayTimeDisplays = 16 + 16 + 45 + 100 + 45 + 16 + 1;
+
+bool RenderMedia::shouldShowTimeDisplayControls() const
+{
+ if (!m_currentTimeDisplay && !m_timeRemainingDisplay)
+ return false;
+
+ int width = mediaElement()->renderBox()->width();
+ return width >= minWidthToDisplayTimeDisplays * style()->effectiveZoom();
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/RenderMedia.h b/Source/WebCore/rendering/RenderMedia.h
new file mode 100644
index 0000000..817252d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMedia.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2007, 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.
+ *
+ * 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 RenderMedia_h
+#define RenderMedia_h
+
+#if ENABLE(VIDEO)
+
+#include "RenderImage.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+class HTMLInputElement;
+class HTMLMediaElement;
+class MediaControlMuteButtonElement;
+class MediaControlPlayButtonElement;
+class MediaControlSeekButtonElement;
+class MediaControlRewindButtonElement;
+class MediaControlReturnToRealtimeButtonElement;
+class MediaControlToggleClosedCaptionsButtonElement;
+class MediaControlTimelineElement;
+class MediaControlVolumeSliderElement;
+class MediaControlFullscreenButtonElement;
+class MediaControlTimeDisplayElement;
+class MediaControlStatusDisplayElement;
+class MediaControlTimelineContainerElement;
+class MediaControlVolumeSliderContainerElement;
+class MediaControlElement;
+class MediaPlayer;
+
+class RenderMedia : public RenderImage {
+public:
+ RenderMedia(HTMLMediaElement*);
+ RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize);
+ virtual ~RenderMedia();
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ HTMLMediaElement* mediaElement() const;
+ MediaPlayer* player() const;
+
+ bool shouldShowTimeDisplayControls() const;
+
+ void updateFromElement();
+ void updatePlayer();
+ void updateControls();
+ void updateTimeDisplay();
+
+ void forwardEvent(Event*);
+#if PLATFORM(ANDROID)
+ void updateLastTouch();
+#endif
+
+protected:
+ virtual void layout();
+
+private:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual void destroy();
+
+ virtual const char* renderName() const { return "RenderMedia"; }
+ virtual bool isMedia() const { return true; }
+ virtual bool isImage() const { return false; }
+
+ void createControlsShadowRoot();
+ void destroyControlsShadowRoot();
+ void createPanel();
+ void createMuteButton();
+ void createPlayButton();
+ void createSeekBackButton();
+ void createSeekForwardButton();
+ void createRewindButton();
+ void createReturnToRealtimeButton();
+ void createToggleClosedCaptionsButton();
+ void createStatusDisplay();
+ void createTimelineContainer();
+ void createTimeline();
+ void createVolumeSliderContainer();
+ void createVolumeSlider();
+ void createVolumeSliderMuteButton();
+ void createCurrentTimeDisplay();
+ void createTimeRemainingDisplay();
+ void createFullscreenButton();
+
+ void timeUpdateTimerFired(Timer<RenderMedia>*);
+
+ void updateControlVisibility();
+ void changeOpacity(HTMLElement*, float opacity);
+ void opacityAnimationTimerFired(Timer<RenderMedia>*);
+
+ void updateVolumeSliderContainer(bool visible);
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return true; }
+
+ RefPtr<HTMLElement> m_controlsShadowRoot;
+ RefPtr<MediaControlElement> m_panel;
+ RefPtr<MediaControlMuteButtonElement> m_muteButton;
+ RefPtr<MediaControlPlayButtonElement> m_playButton;
+ RefPtr<MediaControlSeekButtonElement> m_seekBackButton;
+ RefPtr<MediaControlSeekButtonElement> m_seekForwardButton;
+ RefPtr<MediaControlRewindButtonElement> m_rewindButton;
+ RefPtr<MediaControlReturnToRealtimeButtonElement> m_returnToRealtimeButton;
+ RefPtr<MediaControlToggleClosedCaptionsButtonElement> m_toggleClosedCaptionsButton;
+ RefPtr<MediaControlTimelineElement> m_timeline;
+ RefPtr<MediaControlVolumeSliderElement> m_volumeSlider;
+ RefPtr<MediaControlMuteButtonElement> m_volumeSliderMuteButton;
+ RefPtr<MediaControlFullscreenButtonElement> m_fullscreenButton;
+ RefPtr<MediaControlTimelineContainerElement> m_timelineContainer;
+ RefPtr<MediaControlVolumeSliderContainerElement> m_volumeSliderContainer;
+ RefPtr<MediaControlTimeDisplayElement> m_currentTimeDisplay;
+ RefPtr<MediaControlTimeDisplayElement> m_timeRemainingDisplay;
+ RefPtr<MediaControlStatusDisplayElement> m_statusDisplay;
+ RenderObjectChildList m_children;
+ Node* m_lastUnderNode;
+ Node* m_nodeUnderMouse;
+
+ Timer<RenderMedia> m_timeUpdateTimer;
+ Timer<RenderMedia> m_opacityAnimationTimer;
+ bool m_mouseOver;
+ double m_opacityAnimationStartTime;
+ double m_opacityAnimationDuration;
+ float m_opacityAnimationFrom;
+ float m_opacityAnimationTo;
+#if PLATFORM(ANDROID)
+ double m_lastTouch;
+#endif
+};
+
+inline RenderMedia* toRenderMedia(RenderObject* object)
+{
+ ASSERT(!object || object->isMedia());
+ return static_cast<RenderMedia*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderMedia(const RenderMedia*);
+
+} // namespace WebCore
+
+#endif
+#endif // RenderMedia_h
diff --git a/Source/WebCore/rendering/RenderMediaControls.cpp b/Source/WebCore/rendering/RenderMediaControls.cpp
new file mode 100644
index 0000000..9c4757c
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMediaControls.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "RenderMediaControls.h"
+
+#include "GraphicsContext.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "RenderTheme.h"
+#include <CoreGraphics/CoreGraphics.h>
+#include <WebKitSystemInterface/WebKitSystemInterface.h>
+
+#if PLATFORM(WIN)
+// The Windows version of WKSI defines these functions as capitalized, while the Mac version defines them as lower case.
+#define wkMediaControllerThemeAvailable(themeStyle) WKMediaControllerThemeAvailable(themeStyle)
+#define wkHitTestMediaUIPart(part, themeStyle, bounds, point) WKHitTestMediaUIPart(part, themeStyle, bounds, point)
+#define wkMeasureMediaUIPart(part, themeStyle, bounds, naturalSize) WKMeasureMediaUIPart(part, themeStyle, bounds, naturalSize)
+#define wkDrawMediaUIPart(part, themeStyle, context, rect, state) WKDrawMediaUIPart(part, themeStyle, context, rect, state)
+#define wkDrawMediaSliderTrack(themeStyle, context, rect, timeLoaded, currentTime, duration, state) WKDrawMediaSliderTrack(themeStyle, context, rect, timeLoaded, currentTime, duration, state)
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+#if ENABLE(VIDEO)
+
+static WKMediaControllerThemeState determineState(RenderObject* o)
+{
+ int result = 0;
+ RenderTheme* theme = o->theme();
+ if (!theme->isEnabled(o) || theme->isReadOnlyControl(o))
+ result |= WKMediaControllerFlagDisabled;
+ if (theme->isPressed(o))
+ result |= WKMediaControllerFlagPressed;
+ if (theme->isFocused(o))
+ result |= WKMediaControllerFlagFocused;
+ return static_cast<WKMediaControllerThemeState>(result);
+}
+
+// Utility to scale when the UI part are not scaled by wkDrawMediaUIPart
+static FloatRect getUnzoomedRectAndAdjustCurrentContext(RenderObject* o, const PaintInfo& paintInfo, const IntRect &originalRect)
+{
+ float zoomLevel = o->style()->effectiveZoom();
+ FloatRect unzoomedRect(originalRect);
+ if (zoomLevel != 1.0f) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+ return unzoomedRect;
+}
+
+static const int mediaSliderThumbWidth = 13;
+static const int mediaSliderThumbHeight = 14;
+
+void RenderMediaControls::adjustMediaSliderThumbSize(RenderObject* o)
+{
+ ControlPart part = o->style()->appearance();
+
+ if (part != MediaSliderThumbPart && part != MediaVolumeSliderThumbPart)
+ return;
+
+ CGSize size;
+ wkMeasureMediaUIPart(part == MediaSliderThumbPart ? MediaSliderThumb : MediaVolumeSliderThumb, WKMediaControllerThemeQuickTime, 0, &size);
+
+ float zoomLevel = o->style()->effectiveZoom();
+ o->style()->setWidth(Length(static_cast<int>(size.width * zoomLevel), Fixed));
+ o->style()->setHeight(Length(static_cast<int>(size.height * zoomLevel), Fixed));
+}
+
+bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ static const int themeStyle = WKMediaControllerThemeQuickTime;
+ paintInfo.context->save();
+ switch (part) {
+ case MediaFullscreenButton:
+ wkDrawMediaUIPart(WKMediaUIPartFullscreenButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaShowClosedCaptionsButton:
+ case MediaHideClosedCaptionsButton:
+ if (MediaControlToggleClosedCaptionsButtonElement* btn = static_cast<MediaControlToggleClosedCaptionsButtonElement*>(o->node())) {
+ bool captionsVisible = btn->displayType() == MediaHideClosedCaptionsButton;
+ wkDrawMediaUIPart(captionsVisible ? WKMediaUIPartHideClosedCaptionsButton : WKMediaUIPartShowClosedCaptionsButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ }
+ break;
+ case MediaMuteButton:
+ case MediaUnMuteButton:
+ if (MediaControlMuteButtonElement* btn = static_cast<MediaControlMuteButtonElement*>(o->node())) {
+ bool audioEnabled = btn->displayType() == MediaMuteButton;
+ wkDrawMediaUIPart(audioEnabled ? WKMediaUIPartMuteButton : WKMediaUIPartUnMuteButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ }
+ break;
+ case MediaPauseButton:
+ case MediaPlayButton:
+ if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o->node())) {
+ bool canPlay = btn->displayType() == MediaPlayButton;
+ wkDrawMediaUIPart(canPlay ? WKMediaUIPartPlayButton : WKMediaUIPartPauseButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ }
+ break;
+ case MediaRewindButton:
+ wkDrawMediaUIPart(WKMediaUIPartRewindButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaSeekBackButton:
+ wkDrawMediaUIPart(WKMediaUIPartSeekBackButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaSeekForwardButton:
+ wkDrawMediaUIPart(WKMediaUIPartSeekForwardButton, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaSlider: {
+ if (HTMLMediaElement* mediaElement = toParentMediaElement(o)) {
+ FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r);
+ wkDrawMediaSliderTrack(themeStyle, paintInfo.context->platformContext(), unzoomedRect, mediaElement->percentLoaded() * mediaElement->duration(), mediaElement->currentTime(), mediaElement->duration(), determineState(o));
+ }
+ break;
+ }
+ case MediaSliderThumb:
+ wkDrawMediaUIPart(WKMediaUIPartTimelineSliderThumb, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaVolumeSliderContainer:
+ wkDrawMediaUIPart(WKMediaUIPartVolumeSliderContainer, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaVolumeSlider:
+ wkDrawMediaUIPart(WKMediaUIPartVolumeSlider, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaVolumeSliderThumb:
+ wkDrawMediaUIPart(WKMediaUIPartVolumeSliderThumb, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaTimelineContainer:
+ wkDrawMediaUIPart(WKMediaUIPartBackground, themeStyle, paintInfo.context->platformContext(), r, determineState(o));
+ break;
+ case MediaCurrentTimeDisplay:
+ ASSERT_NOT_REACHED();
+ break;
+ case MediaTimeRemainingDisplay:
+ ASSERT_NOT_REACHED();
+ break;
+ case MediaControlsPanel:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ paintInfo.context->restore();
+
+ return false;
+}
+
+IntPoint RenderMediaControls::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size)
+{
+ static const int xOffset = -4;
+ static const int yOffset = 5;
+
+ float zoomLevel = muteButton->renderer()->style()->effectiveZoom();
+ int y = yOffset * zoomLevel + muteButton->renderBox()->offsetHeight() - size.height();
+ FloatPoint absPoint = muteButton->renderer()->localToAbsolute(FloatPoint(muteButton->renderBox()->offsetLeft(), y), true, true);
+ if (absPoint.y() < 0)
+ y = muteButton->renderBox()->height();
+ return IntPoint(xOffset * zoomLevel, y);
+
+}
+#endif // #if ENABLE(VIDEO)
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderMediaControls.h b/Source/WebCore/rendering/RenderMediaControls.h
new file mode 100644
index 0000000..9edeef1
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMediaControls.h
@@ -0,0 +1,48 @@
+/*
+ * 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 RenderMediaControls_h
+#define RenderMediaControls_h
+
+#if ENABLE(VIDEO)
+
+#include "RenderObject.h"
+#include "MediaControlElements.h"
+
+namespace WebCore {
+
+class HTMLMediaElement;
+class RenderMediaControls {
+public:
+ static bool paintMediaControlsPart(MediaControlElementType, RenderObject*, const PaintInfo&, const IntRect&);
+ static void adjustMediaSliderThumbSize(RenderObject*);
+ static IntPoint volumeSliderOffsetFromMuteButton(Node*, const IntSize&);
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(VIDEO)
+
+#endif // RenderMediaControls_h
diff --git a/Source/WebCore/rendering/RenderMediaControlsChromium.cpp b/Source/WebCore/rendering/RenderMediaControlsChromium.cpp
new file mode 100644
index 0000000..f938a52
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMediaControlsChromium.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2009 Apple Inc.
+ * Copyright (C) 2009 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:
+ * 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 "RenderMediaControlsChromium.h"
+
+#include "Gradient.h"
+#include "GraphicsContext.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+
+namespace WebCore {
+
+#if ENABLE(VIDEO)
+
+typedef WTF::HashMap<const char*, Image*> MediaControlImageMap;
+static MediaControlImageMap* gMediaControlImageMap = 0;
+
+static Image* platformResource(const char* name)
+{
+ if (!gMediaControlImageMap)
+ gMediaControlImageMap = new MediaControlImageMap();
+ if (Image* image = gMediaControlImageMap->get(name))
+ return image;
+ if (Image* image = Image::loadPlatformResource(name).releaseRef()) {
+ gMediaControlImageMap->set(name, image);
+ return image;
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static bool hasSource(const HTMLMediaElement* mediaElement)
+{
+ return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY
+ && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE;
+}
+
+static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image)
+{
+ IntRect imageRect = image->rect();
+ context->drawImage(image, ColorSpaceDeviceRGB, rect);
+ return true;
+}
+
+static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(object);
+ if (!mediaElement)
+ return false;
+
+ static Image* soundFull = platformResource("mediaSoundFull");
+ static Image* soundNone = platformResource("mediaSoundNone");
+ static Image* soundDisabled = platformResource("mediaSoundDisabled");
+
+ if (!hasSource(mediaElement) || !mediaElement->hasAudio())
+ return paintMediaButton(paintInfo.context, rect, soundDisabled);
+
+ return paintMediaButton(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull);
+}
+
+static bool paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(object);
+ if (!mediaElement)
+ return false;
+
+ static Image* mediaPlay = platformResource("mediaPlay");
+ static Image* mediaPause = platformResource("mediaPause");
+ static Image* mediaPlayDisabled = platformResource("mediaPlayDisabled");
+
+ if (!hasSource(mediaElement))
+ return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled);
+
+ return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause);
+}
+
+static Image* getMediaSliderThumb()
+{
+ static Image* mediaSliderThumb = platformResource("mediaSliderThumb");
+ return mediaSliderThumb;
+}
+
+static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(object);
+ if (!mediaElement)
+ return false;
+
+ RenderStyle* style = object->style();
+ GraphicsContext* context = paintInfo.context;
+
+ // Draw the border of the time bar.
+ // FIXME: this should be a rounded rect but need to fix GraphicsContextSkia first.
+ // https://bugs.webkit.org/show_bug.cgi?id=30143
+ context->save();
+ context->setShouldAntialias(true);
+ context->setStrokeStyle(SolidStroke);
+ context->setStrokeColor(style->visitedDependentColor(CSSPropertyBorderLeftColor), ColorSpaceDeviceRGB);
+ context->setStrokeThickness(style->borderLeftWidth());
+ context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor), ColorSpaceDeviceRGB);
+ context->drawRect(rect);
+ context->restore();
+
+ // Draw the buffered ranges.
+ // FIXME: Draw multiple ranges if there are multiple buffered ranges.
+ IntRect bufferedRect = rect;
+ bufferedRect.inflate(-style->borderLeftWidth());
+
+ double bufferedWidth = 0.0;
+ if (mediaElement->percentLoaded() > 0.0) {
+ // Account for the width of the slider thumb.
+ Image* mediaSliderThumb = getMediaSliderThumb();
+ double thumbWidth = mediaSliderThumb->width() / 2.0 + 1.0;
+ double rectWidth = bufferedRect.width() - thumbWidth;
+ if (rectWidth < 0.0)
+ rectWidth = 0.0;
+ bufferedWidth = rectWidth * mediaElement->percentLoaded() + thumbWidth;
+ }
+ bufferedRect.setWidth(bufferedWidth);
+
+ // Don't bother drawing an empty area.
+ if (!bufferedRect.isEmpty()) {
+ IntPoint sliderTopLeft = bufferedRect.location();
+ IntPoint sliderTopRight = sliderTopLeft;
+ sliderTopRight.move(0, bufferedRect.height());
+
+ RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
+ Color startColor = object->style()->visitedDependentColor(CSSPropertyColor);
+ gradient->addColorStop(0.0, startColor);
+ gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
+
+ context->save();
+ context->setStrokeStyle(NoStroke);
+ context->setFillGradient(gradient);
+ context->fillRect(bufferedRect);
+ context->restore();
+ }
+
+ return true;
+}
+
+static bool paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ if (!object->parent()->isSlider())
+ return false;
+
+ HTMLMediaElement* mediaElement = toParentMediaElement(object->parent());
+ if (!mediaElement)
+ return false;
+
+ if (!hasSource(mediaElement))
+ return true;
+
+ Image* mediaSliderThumb = getMediaSliderThumb();
+ return paintMediaButton(paintInfo.context, rect, mediaSliderThumb);
+}
+
+static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(object);
+ if (!mediaElement)
+ return false;
+
+ GraphicsContext* context = paintInfo.context;
+ Color originalColor = context->strokeColor();
+ if (originalColor != Color::white)
+ context->setStrokeColor(Color::white, ColorSpaceDeviceRGB);
+
+ int x = rect.x() + rect.width() / 2;
+ context->drawLine(IntPoint(x, rect.y()), IntPoint(x, rect.y() + rect.height()));
+
+ if (originalColor != Color::white)
+ context->setStrokeColor(originalColor, ColorSpaceDeviceRGB);
+ return true;
+}
+
+static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ if (!object->parent()->isSlider())
+ return false;
+
+ static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb");
+ return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb);
+}
+
+static bool paintMediaTimelineContainer(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(object);
+ if (!mediaElement)
+ return false;
+
+ if (!rect.isEmpty()) {
+ GraphicsContext* context = paintInfo.context;
+ Color originalColor = context->strokeColor();
+ float originalThickness = context->strokeThickness();
+ StrokeStyle originalStyle = context->strokeStyle();
+
+ context->setStrokeStyle(SolidStroke);
+
+ // Draw the left border using CSS defined width and color.
+ context->setStrokeThickness(object->style()->borderLeftWidth());
+ context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderLeftColor).rgb(), ColorSpaceDeviceRGB);
+ context->drawLine(IntPoint(rect.x() + 1, rect.y()),
+ IntPoint(rect.x() + 1, rect.y() + rect.height()));
+
+ // Draw the right border using CSS defined width and color.
+ context->setStrokeThickness(object->style()->borderRightWidth());
+ context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderRightColor).rgb(), ColorSpaceDeviceRGB);
+ context->drawLine(IntPoint(rect.x() + rect.width() - 1, rect.y()),
+ IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height()));
+
+ context->setStrokeColor(originalColor, ColorSpaceDeviceRGB);
+ context->setStrokeThickness(originalThickness);
+ context->setStrokeStyle(originalStyle);
+ }
+ return true;
+}
+
+bool RenderMediaControlsChromium::shouldRenderMediaControlPart(ControlPart part, Element* e)
+{
+ UNUSED_PARAM(e);
+
+ switch (part) {
+ case MediaMuteButtonPart:
+ case MediaPlayButtonPart:
+ case MediaSliderPart:
+ case MediaSliderThumbPart:
+ case MediaVolumeSliderContainerPart:
+ case MediaVolumeSliderPart:
+ case MediaVolumeSliderThumbPart:
+ case MediaControlsBackgroundPart:
+ case MediaCurrentTimePart:
+ case MediaTimeRemainingPart:
+ return true;
+ default:
+ ;
+ }
+ return false;
+}
+
+bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ switch (part) {
+ case MediaMuteButton:
+ case MediaUnMuteButton:
+ return paintMediaMuteButton(object, paintInfo, rect);
+ case MediaPauseButton:
+ case MediaPlayButton:
+ return paintMediaPlayButton(object, paintInfo, rect);
+ case MediaSlider:
+ return paintMediaSlider(object, paintInfo, rect);
+ case MediaSliderThumb:
+ return paintMediaSliderThumb(object, paintInfo, rect);
+ case MediaVolumeSlider:
+ return paintMediaVolumeSlider(object, paintInfo, rect);
+ case MediaVolumeSliderThumb:
+ return paintMediaVolumeSliderThumb(object, paintInfo, rect);
+ case MediaTimelineContainer:
+ return paintMediaTimelineContainer(object, paintInfo, rect);
+ case MediaVolumeSliderMuteButton:
+ case MediaFullscreenButton:
+ case MediaSeekBackButton:
+ case MediaSeekForwardButton:
+ case MediaVolumeSliderContainer:
+ case MediaCurrentTimeDisplay:
+ case MediaTimeRemainingDisplay:
+ case MediaControlsPanel:
+ case MediaRewindButton:
+ case MediaReturnToRealtimeButton:
+ case MediaStatusDisplay:
+ case MediaShowClosedCaptionsButton:
+ case MediaHideClosedCaptionsButton:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ return false;
+}
+
+void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderObject* object)
+{
+ static Image* mediaSliderThumb = platformResource("mediaSliderThumb");
+ static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb");
+
+ Image* thumbImage = 0;
+ if (object->style()->appearance() == MediaSliderThumbPart)
+ thumbImage = mediaSliderThumb;
+ else if (object->style()->appearance() == MediaVolumeSliderThumbPart)
+ thumbImage = mediaVolumeSliderThumb;
+
+ float zoomLevel = object->style()->effectiveZoom();
+ if (thumbImage) {
+ object->style()->setWidth(Length(static_cast<int>(thumbImage->width() * zoomLevel), Fixed));
+ object->style()->setHeight(Length(static_cast<int>(thumbImage->height() * zoomLevel), Fixed));
+ }
+}
+
+#endif // #if ENABLE(VIDEO)
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderMediaControlsChromium.h b/Source/WebCore/rendering/RenderMediaControlsChromium.h
new file mode 100644
index 0000000..060a9d4
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMediaControlsChromium.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 Apple Inc.
+ * Copyright (C) 2009 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:
+ * 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 RenderMediaControlsChromium_h
+#define RenderMediaControlsChromium_h
+
+#include "RenderObject.h"
+#include "MediaControlElements.h"
+
+namespace WebCore {
+
+class HTMLMediaElement;
+class RenderMediaControlsChromium {
+public:
+ static bool shouldRenderMediaControlPart(ControlPart, Element*);
+ static bool paintMediaControlsPart(MediaControlElementType, RenderObject*, const PaintInfo&, const IntRect&);
+ static void adjustMediaSliderThumbSize(RenderObject*);
+};
+
+} // namespace WebCore
+
+#endif // RenderMediaControlsChromium_h
diff --git a/Source/WebCore/rendering/RenderMenuList.cpp b/Source/WebCore/rendering/RenderMenuList.cpp
new file mode 100644
index 0000000..5ad661f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMenuList.cpp
@@ -0,0 +1,562 @@
+/*
+ * This file is part of the select element renderer in WebCore.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * 2009 Torch Mobile Inc. All rights reserved. (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.
+ *
+ */
+
+#include "config.h"
+#include "RenderMenuList.h"
+
+#include "AXObjectCache.h"
+#include "Chrome.h"
+#include "CSSStyleSelector.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "HTMLNames.h"
+#include "NodeRenderStyle.h"
+#include "OptionElement.h"
+#include "OptionGroupElement.h"
+#include "PopupMenu.h"
+#include "RenderBR.h"
+#include "RenderScrollbar.h"
+#include "RenderTheme.h"
+#include "SelectElement.h"
+#include <math.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderMenuList::RenderMenuList(Element* element)
+ : RenderFlexibleBox(element)
+ , m_buttonText(0)
+ , m_innerBlock(0)
+ , m_optionsChanged(true)
+ , m_optionsWidth(0)
+ , m_lastSelectedIndex(-1)
+ , m_popup(0)
+ , m_popupIsVisible(false)
+{
+}
+
+RenderMenuList::~RenderMenuList()
+{
+ if (m_popup)
+ m_popup->disconnectClient();
+ m_popup = 0;
+}
+
+void RenderMenuList::createInnerBlock()
+{
+ if (m_innerBlock) {
+ ASSERT(firstChild() == m_innerBlock);
+ ASSERT(!m_innerBlock->nextSibling());
+ return;
+ }
+
+ // Create an anonymous block.
+ ASSERT(!firstChild());
+ m_innerBlock = createAnonymousBlock();
+ adjustInnerStyle();
+ RenderFlexibleBox::addChild(m_innerBlock);
+}
+
+void RenderMenuList::adjustInnerStyle()
+{
+ m_innerBlock->style()->setBoxFlex(1.0f);
+
+ m_innerBlock->style()->setPaddingLeft(Length(theme()->popupInternalPaddingLeft(style()), Fixed));
+ m_innerBlock->style()->setPaddingRight(Length(theme()->popupInternalPaddingRight(style()), Fixed));
+ m_innerBlock->style()->setPaddingTop(Length(theme()->popupInternalPaddingTop(style()), Fixed));
+ m_innerBlock->style()->setPaddingBottom(Length(theme()->popupInternalPaddingBottom(style()), Fixed));
+
+ if (document()->page()->chrome()->selectItemWritingDirectionIsNatural()) {
+ // Items in the popup will not respect the CSS text-align and direction properties,
+ // so we must adjust our own style to match.
+ m_innerBlock->style()->setTextAlign(LEFT);
+ TextDirection direction = (m_buttonText && m_buttonText->text()->defaultWritingDirection() == WTF::Unicode::RightToLeft) ? RTL : LTR;
+ m_innerBlock->style()->setDirection(direction);
+ }
+}
+
+void RenderMenuList::addChild(RenderObject* newChild, RenderObject* beforeChild)
+{
+ createInnerBlock();
+ m_innerBlock->addChild(newChild, beforeChild);
+}
+
+void RenderMenuList::removeChild(RenderObject* oldChild)
+{
+ if (oldChild == m_innerBlock || !m_innerBlock) {
+ RenderFlexibleBox::removeChild(oldChild);
+ m_innerBlock = 0;
+ } else
+ m_innerBlock->removeChild(oldChild);
+}
+
+void RenderMenuList::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+
+ if (m_buttonText)
+ m_buttonText->setStyle(style());
+ if (m_innerBlock) // RenderBlock handled updating the anonymous block's style.
+ adjustInnerStyle();
+
+ setReplaced(isInline());
+
+ bool fontChanged = !oldStyle || oldStyle->font() != style()->font();
+ if (fontChanged)
+ updateOptionsWidth();
+}
+
+void RenderMenuList::updateOptionsWidth()
+{
+ float maxOptionWidth = 0;
+ const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
+ int size = listItems.size();
+ for (int i = 0; i < size; ++i) {
+ Element* element = listItems[i];
+ OptionElement* optionElement = toOptionElement(element);
+ if (!optionElement)
+ continue;
+
+ String text = optionElement->textIndentedToRespectGroupLabel();
+ if (theme()->popupOptionSupportsTextIndent()) {
+ // Add in the option's text indent. We can't calculate percentage values for now.
+ float optionWidth = 0;
+ if (RenderStyle* optionStyle = element->renderStyle())
+ optionWidth += optionStyle->textIndent().calcMinValue(0);
+ if (!text.isEmpty())
+ optionWidth += style()->font().floatWidth(text);
+ maxOptionWidth = max(maxOptionWidth, optionWidth);
+ } else if (!text.isEmpty())
+ maxOptionWidth = max(maxOptionWidth, style()->font().floatWidth(text));
+ }
+
+ int width = static_cast<int>(ceilf(maxOptionWidth));
+ if (m_optionsWidth == width)
+ return;
+
+ m_optionsWidth = width;
+ if (parent())
+ setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+void RenderMenuList::updateFromElement()
+{
+ if (m_optionsChanged) {
+ updateOptionsWidth();
+ m_optionsChanged = false;
+ }
+
+ if (m_popupIsVisible)
+ m_popup->updateFromElement();
+ else
+ setTextFromOption(toSelectElement(static_cast<Element*>(node()))->selectedIndex());
+}
+
+void RenderMenuList::setTextFromOption(int optionIndex)
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ int size = listItems.size();
+
+ int i = select->optionToListIndex(optionIndex);
+ String text = "";
+ if (i >= 0 && i < size) {
+ if (OptionElement* optionElement = toOptionElement(listItems[i]))
+ text = optionElement->textIndentedToRespectGroupLabel();
+ }
+
+ setText(text.stripWhiteSpace());
+}
+
+void RenderMenuList::setText(const String& s)
+{
+ if (s.isEmpty()) {
+ if (!m_buttonText || !m_buttonText->isBR()) {
+ if (m_buttonText)
+ m_buttonText->destroy();
+ m_buttonText = new (renderArena()) RenderBR(document());
+ m_buttonText->setStyle(style());
+ addChild(m_buttonText);
+ }
+ } else {
+ if (m_buttonText && !m_buttonText->isBR())
+ m_buttonText->setText(s.impl());
+ else {
+ if (m_buttonText)
+ m_buttonText->destroy();
+ m_buttonText = new (renderArena()) RenderText(document(), s.impl());
+ m_buttonText->setStyle(style());
+ addChild(m_buttonText);
+ }
+ adjustInnerStyle();
+ }
+}
+
+String RenderMenuList::text() const
+{
+ return m_buttonText ? m_buttonText->text() : 0;
+}
+
+IntRect RenderMenuList::controlClipRect(int tx, int ty) const
+{
+ // Clip to the intersection of the content box and the content box for the inner box
+ // This will leave room for the arrows which sit in the inner box padding,
+ // and if the inner box ever spills out of the outer box, that will get clipped too.
+ IntRect outerBox(tx + borderLeft() + paddingLeft(),
+ ty + borderTop() + paddingTop(),
+ contentWidth(),
+ contentHeight());
+
+ IntRect innerBox(tx + m_innerBlock->x() + m_innerBlock->paddingLeft(),
+ ty + m_innerBlock->y() + m_innerBlock->paddingTop(),
+ m_innerBlock->contentWidth(),
+ m_innerBlock->contentHeight());
+
+ return intersection(outerBox, innerBox);
+}
+
+void RenderMenuList::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 = max(m_optionsWidth, theme()->minimumMenuListSize(style())) + m_innerBlock->paddingLeft() + m_innerBlock->paddingRight();
+
+ 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 RenderMenuList::showPopup()
+{
+ if (m_popupIsVisible)
+ return;
+
+ // Create m_innerBlock here so it ends up as the first child.
+ // This is important because otherwise we might try to create m_innerBlock
+ // inside the showPopup call and it would fail.
+ createInnerBlock();
+ if (!m_popup)
+ m_popup = document()->page()->chrome()->createPopupMenu(this);
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ m_popupIsVisible = true;
+
+ // Compute the top left taking transforms into account, but use
+ // the actual width of the element to size the popup.
+ FloatPoint absTopLeft = localToAbsolute(FloatPoint(), false, true);
+ IntRect absBounds = absoluteBoundingBoxRect();
+ absBounds.setLocation(roundedIntPoint(absTopLeft));
+ m_popup->show(absBounds, document()->view(),
+ select->optionToListIndex(select->selectedIndex()));
+}
+
+void RenderMenuList::hidePopup()
+{
+ if (m_popup)
+ m_popup->hide();
+}
+
+void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange)
+{
+ // Check to ensure a page navigation has not occurred while
+ // the popup was up.
+ Document* doc = static_cast<Element*>(node())->document();
+ if (!doc || doc != doc->frame()->document())
+ return;
+
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ select->setSelectedIndexByUser(select->listToOptionIndex(listIndex), true, fireOnChange);
+}
+
+#if ENABLE(NO_LISTBOX_RENDERING)
+void RenderMenuList::listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow)
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ select->listBoxSelectItem(listIndex, allowMultiplySelections, shift, fireOnChangeNow);
+}
+
+bool RenderMenuList::multiple()
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ return select->multiple();
+}
+#endif
+
+void RenderMenuList::didSetSelectedIndex()
+{
+ int index = selectedIndex();
+ if (m_lastSelectedIndex == index)
+ return;
+
+ m_lastSelectedIndex = index;
+
+ if (AXObjectCache::accessibilityEnabled())
+ document()->axObjectCache()->postNotification(this, AXObjectCache::AXMenuListValueChanged, true, PostSynchronously);
+}
+
+String RenderMenuList::itemText(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return String();
+ Element* element = listItems[listIndex];
+ if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element))
+ return optionGroupElement->groupLabelText();
+ else if (OptionElement* optionElement = toOptionElement(element))
+ return optionElement->textIndentedToRespectGroupLabel();
+ return String();
+}
+
+String RenderMenuList::itemLabel(unsigned) const
+{
+ return String();
+}
+
+String RenderMenuList::itemIcon(unsigned) const
+{
+ return String();
+}
+
+String RenderMenuList::itemAccessibilityText(unsigned listIndex) const
+{
+ // Allow the accessible name be changed if necessary.
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return String();
+
+ return listItems[listIndex]->getAttribute(aria_labelAttr);
+}
+
+String RenderMenuList::itemToolTip(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return String();
+ Element* element = listItems[listIndex];
+ return element->title();
+}
+
+bool RenderMenuList::itemIsEnabled(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return false;
+ Element* element = listItems[listIndex];
+ if (!isOptionElement(element))
+ return false;
+
+ bool groupEnabled = true;
+ if (Element* parentElement = element->parentElement()) {
+ if (isOptionGroupElement(parentElement))
+ groupEnabled = parentElement->isEnabledFormControl();
+ }
+ if (!groupEnabled)
+ return false;
+
+ return element->isEnabledFormControl();
+}
+
+PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size()) {
+ // If we are making an out of bounds access, then we want to use the style
+ // of a different option element (index 0). However, if there isn't an option element
+ // before at index 0, we fall back to the menu's style.
+ if (!listIndex)
+ return menuStyle();
+
+ // Try to retrieve the style of an option element we know exists (index 0).
+ listIndex = 0;
+ }
+ Element* element = listItems[listIndex];
+
+ RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle();
+ return style ? PopupMenuStyle(style->visitedDependentColor(CSSPropertyColor), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE, style->display() == NONE, style->textIndent(), style->direction()) : menuStyle();
+}
+
+Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return style()->visitedDependentColor(CSSPropertyBackgroundColor);
+ Element* element = listItems[listIndex];
+
+ Color backgroundColor;
+ if (element->renderStyle())
+ backgroundColor = element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor);
+ // If the item has an opaque background color, return that.
+ if (!backgroundColor.hasAlpha())
+ return backgroundColor;
+
+ // Otherwise, the item's background is overlayed on top of the menu background.
+ backgroundColor = style()->visitedDependentColor(CSSPropertyBackgroundColor).blend(backgroundColor);
+ if (!backgroundColor.hasAlpha())
+ return backgroundColor;
+
+ // If the menu background is not opaque, then add an opaque white background behind.
+ return Color(Color::white).blend(backgroundColor);
+}
+
+PopupMenuStyle RenderMenuList::menuStyle() const
+{
+ RenderStyle* s = m_innerBlock ? m_innerBlock->style() : style();
+ return PopupMenuStyle(s->visitedDependentColor(CSSPropertyColor), s->visitedDependentColor(CSSPropertyBackgroundColor), s->font(), s->visibility() == VISIBLE, s->display() == NONE, s->textIndent(), s->direction());
+}
+
+HostWindow* RenderMenuList::hostWindow() const
+{
+ return document()->view()->hostWindow();
+}
+
+PassRefPtr<Scrollbar> RenderMenuList::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
+{
+ RefPtr<Scrollbar> widget;
+ bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
+ if (hasCustomScrollbarStyle)
+ widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
+ else
+ widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
+ return widget.release();
+}
+
+int RenderMenuList::clientInsetLeft() const
+{
+ return 0;
+}
+
+int RenderMenuList::clientInsetRight() const
+{
+ return 0;
+}
+
+int RenderMenuList::clientPaddingLeft() const
+{
+ return paddingLeft();
+}
+
+const int endOfLinePadding = 2;
+int RenderMenuList::clientPaddingRight() const
+{
+ if (style()->appearance() == MenulistPart || style()->appearance() == MenulistButtonPart) {
+ // For these appearance values, the theme applies padding to leave room for the
+ // drop-down button. But leaving room for the button inside the popup menu itself
+ // looks strange, so we return a small default padding to avoid having a large empty
+ // space appear on the side of the popup menu.
+ return endOfLinePadding;
+ }
+
+ // If the appearance isn't MenulistPart, then the select is styled (non-native), so
+ // we want to return the user specified padding.
+ return paddingRight();
+}
+
+int RenderMenuList::listSize() const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ return select->listItems().size();
+}
+
+int RenderMenuList::selectedIndex() const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ return select->optionToListIndex(select->selectedIndex());
+}
+
+void RenderMenuList::popupDidHide()
+{
+ m_popupIsVisible = false;
+}
+
+bool RenderMenuList::itemIsSeparator(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return false;
+ Element* element = listItems[listIndex];
+ return element->hasTagName(hrTag);
+}
+
+bool RenderMenuList::itemIsLabel(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return false;
+ Element* element = listItems[listIndex];
+ return isOptionGroupElement(element);
+}
+
+bool RenderMenuList::itemIsSelected(unsigned listIndex) const
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ const Vector<Element*>& listItems = select->listItems();
+ if (listIndex >= listItems.size())
+ return false;
+ Element* element = listItems[listIndex];
+ if (OptionElement* optionElement = toOptionElement(element))
+ return optionElement->selected();
+ return false;
+}
+
+void RenderMenuList::setTextFromItem(unsigned listIndex)
+{
+ SelectElement* select = toSelectElement(static_cast<Element*>(node()));
+ setTextFromOption(select->listToOptionIndex(listIndex));
+}
+
+FontSelector* RenderMenuList::fontSelector() const
+{
+ return document()->styleSelector()->fontSelector();
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderMenuList.h b/Source/WebCore/rendering/RenderMenuList.h
new file mode 100644
index 0000000..2c99b1e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMenuList.h
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the select element renderer in WebCore.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 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 RenderMenuList_h
+#define RenderMenuList_h
+
+#include "PopupMenu.h"
+#include "PopupMenuClient.h"
+#include "RenderFlexibleBox.h"
+
+#if PLATFORM(MAC)
+#define POPUP_MENU_PULLS_DOWN 0
+#else
+#define POPUP_MENU_PULLS_DOWN 1
+#endif
+
+namespace WebCore {
+
+class RenderText;
+
+#if ENABLE(NO_LISTBOX_RENDERING)
+class RenderMenuList : public RenderFlexibleBox, private ListPopupMenuClient {
+#else
+class RenderMenuList : public RenderFlexibleBox, private PopupMenuClient {
+#endif
+
+public:
+ RenderMenuList(Element*);
+ virtual ~RenderMenuList();
+
+public:
+ bool popupIsVisible() const { return m_popupIsVisible; }
+ void showPopup();
+ void hidePopup();
+
+ void setOptionsChanged(bool changed) { m_optionsChanged = changed; }
+
+ void didSetSelectedIndex();
+
+ String text() const;
+
+private:
+ virtual bool isMenuList() const { return true; }
+
+ virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0);
+ virtual void removeChild(RenderObject*);
+ virtual bool createsAnonymousWrapper() const { return true; }
+ virtual bool canHaveChildren() const { return false; }
+
+ virtual void updateFromElement();
+
+ virtual bool hasControlClip() const { return true; }
+ virtual IntRect controlClipRect(int tx, int ty) const;
+
+ virtual const char* renderName() const { return "RenderMenuList"; }
+
+ virtual void computePreferredLogicalWidths();
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return true; }
+
+ // PopupMenuClient methods
+ virtual String itemText(unsigned listIndex) const;
+ virtual String itemLabel(unsigned listIndex) const;
+ virtual String itemIcon(unsigned listIndex) const;
+ virtual String itemToolTip(unsigned listIndex) const;
+ virtual String itemAccessibilityText(unsigned listIndex) const;
+ virtual bool itemIsEnabled(unsigned listIndex) const;
+ virtual PopupMenuStyle itemStyle(unsigned listIndex) const;
+ virtual PopupMenuStyle menuStyle() const;
+ virtual int clientInsetLeft() const;
+ virtual int clientInsetRight() const;
+ virtual int clientPaddingLeft() const;
+ virtual int clientPaddingRight() const;
+ virtual int listSize() const;
+ virtual int selectedIndex() const;
+ virtual void popupDidHide();
+ virtual bool itemIsSeparator(unsigned listIndex) const;
+ virtual bool itemIsLabel(unsigned listIndex) const;
+ virtual bool itemIsSelected(unsigned listIndex) const;
+ virtual void setTextFromItem(unsigned listIndex);
+ virtual bool valueShouldChangeOnHotTrack() const { return true; }
+ virtual bool shouldPopOver() const { return !POPUP_MENU_PULLS_DOWN; }
+ virtual void valueChanged(unsigned listIndex, bool fireOnChange = true);
+ virtual void selectionChanged(unsigned, bool) {}
+ virtual void selectionCleared() {}
+ virtual FontSelector* fontSelector() const;
+ virtual HostWindow* hostWindow() const;
+ virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize);
+
+#if ENABLE(NO_LISTBOX_RENDERING)
+ virtual void listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow = true);
+ virtual bool multiple();
+#endif
+
+ virtual bool hasLineIfEmpty() const { return true; }
+
+ Color itemBackgroundColor(unsigned listIndex) const;
+
+ void createInnerBlock();
+ void adjustInnerStyle();
+ void setText(const String&);
+ void setTextFromOption(int optionIndex);
+ void updateOptionsWidth();
+
+ RenderText* m_buttonText;
+ RenderBlock* m_innerBlock;
+
+ bool m_optionsChanged;
+ int m_optionsWidth;
+
+ int m_lastSelectedIndex;
+
+ RefPtr<PopupMenu> m_popup;
+ bool m_popupIsVisible;
+};
+
+inline RenderMenuList* toRenderMenuList(RenderObject* object)
+{
+ ASSERT(!object || object->isMenuList());
+ return static_cast<RenderMenuList*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderMenuList(const RenderMenuList*);
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderMeter.cpp b/Source/WebCore/rendering/RenderMeter.cpp
new file mode 100644
index 0000000..6439651
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMeter.cpp
@@ -0,0 +1,261 @@
+/*
+ * 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(METER_TAG)
+
+#include "RenderMeter.h"
+
+#include "HTMLMeterElement.h"
+#include "HTMLNames.h"
+#include "RenderTheme.h"
+#include "ShadowElement.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+class MeterPartElement : public ShadowBlockElement {
+public:
+ static PassRefPtr<MeterPartElement> createForPart(HTMLElement*, PseudoId);
+
+ void hide();
+ void restoreVisibility();
+
+ virtual void updateStyleForPart(PseudoId);
+
+private:
+ MeterPartElement(HTMLElement*);
+ void saveVisibility();
+
+ EVisibility m_originalVisibility;
+};
+
+MeterPartElement::MeterPartElement(HTMLElement* shadowParent)
+ : ShadowBlockElement(shadowParent)
+{
+}
+
+PassRefPtr<MeterPartElement> MeterPartElement::createForPart(HTMLElement* shadowParent, PseudoId pseudoId)
+{
+ RefPtr<MeterPartElement> ret = adoptRef(new MeterPartElement(shadowParent));
+ ret->initAsPart(pseudoId);
+ ret->saveVisibility();
+ return ret;
+}
+
+void MeterPartElement::hide()
+{
+ if (renderer())
+ renderer()->style()->setVisibility(HIDDEN);
+}
+
+void MeterPartElement::restoreVisibility()
+{
+ if (renderer())
+ renderer()->style()->setVisibility(m_originalVisibility);
+}
+
+void MeterPartElement::updateStyleForPart(PseudoId pseudoId)
+{
+ if (renderer()->style()->styleType() == pseudoId)
+ return;
+
+ ShadowBlockElement::updateStyleForPart(pseudoId);
+ saveVisibility();
+}
+
+void MeterPartElement::saveVisibility()
+{
+ m_originalVisibility = renderer()->style()->visibility();
+}
+
+RenderMeter::RenderMeter(HTMLMeterElement* element)
+ : RenderIndicator(element)
+{
+}
+
+RenderMeter::~RenderMeter()
+{
+ if (shadowAttached()) {
+ m_verticalValuePart->detach();
+ m_verticalBarPart->detach();
+ m_horizontalValuePart->detach();
+ m_horizontalBarPart->detach();
+ }
+}
+
+PassRefPtr<MeterPartElement> RenderMeter::createPart(PseudoId pseudoId)
+{
+ RefPtr<MeterPartElement> element = MeterPartElement::createForPart(static_cast<HTMLElement*>(node()), pseudoId);
+ if (element->renderer())
+ addChild(element->renderer());
+ return element;
+}
+
+void RenderMeter::updateFromElement()
+{
+ if (!shadowAttached()) {
+ m_horizontalBarPart = createPart(barPseudoId(HORIZONTAL));
+ m_horizontalValuePart = createPart(valuePseudoId(HORIZONTAL));
+ m_verticalBarPart = createPart(barPseudoId(VERTICAL));
+ m_verticalValuePart = createPart(valuePseudoId(VERTICAL));
+ }
+
+ m_horizontalBarPart->updateStyleForPart(barPseudoId(HORIZONTAL));
+ m_horizontalValuePart->updateStyleForPart(valuePseudoId(HORIZONTAL));
+ m_verticalBarPart->updateStyleForPart(barPseudoId(VERTICAL));
+ m_verticalValuePart->updateStyleForPart(valuePseudoId(VERTICAL));
+ RenderIndicator::updateFromElement();
+}
+
+void RenderMeter::computeLogicalWidth()
+{
+ RenderBox::computeLogicalWidth();
+ setWidth(theme()->meterSizeForBounds(this, frameRect()).width());
+}
+
+void RenderMeter::computeLogicalHeight()
+{
+ RenderBox::computeLogicalHeight();
+ setHeight(theme()->meterSizeForBounds(this, frameRect()).height());
+}
+
+void RenderMeter::layoutParts()
+{
+ m_horizontalBarPart->layoutAsPart(barPartRect());
+ m_horizontalValuePart->layoutAsPart(valuePartRect(HORIZONTAL));
+ m_verticalBarPart->layoutAsPart(barPartRect());
+ m_verticalValuePart->layoutAsPart(valuePartRect(VERTICAL));
+
+ if (shouldHaveParts()) {
+ if (HORIZONTAL == orientation()) {
+ m_verticalBarPart->hide();
+ m_verticalValuePart->hide();
+ m_horizontalBarPart->restoreVisibility();
+ m_horizontalValuePart->restoreVisibility();
+ } else {
+ m_verticalBarPart->restoreVisibility();
+ m_verticalValuePart->restoreVisibility();
+ m_horizontalBarPart->hide();
+ m_horizontalValuePart->hide();
+ }
+ } else {
+ m_verticalBarPart->hide();
+ m_verticalValuePart->hide();
+ m_horizontalBarPart->hide();
+ m_horizontalValuePart->hide();
+ }
+}
+
+bool RenderMeter::shouldHaveParts() const
+{
+ EBoxOrient currentOrientation = orientation();
+ bool hasTheme = theme()->supportsMeter(style()->appearance(), HORIZONTAL == currentOrientation);
+ if (!hasTheme)
+ return true;
+ bool shadowsHaveStyle = ShadowBlockElement::partShouldHaveStyle(this, barPseudoId(currentOrientation)) || ShadowBlockElement::partShouldHaveStyle(this, valuePseudoId(currentOrientation));
+ if (shadowsHaveStyle)
+ return true;
+ return false;
+}
+
+double RenderMeter::valueRatio() const
+{
+ HTMLMeterElement* element = static_cast<HTMLMeterElement*>(node());
+ double min = element->min();
+ double max = element->max();
+ double value = element->value();
+
+ if (max <= min)
+ return 0;
+ return (value - min) / (max - min);
+}
+
+IntRect RenderMeter::barPartRect() const
+{
+ return IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround(width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom());
+}
+
+IntRect RenderMeter::valuePartRect(EBoxOrient asOrientation) const
+{
+ IntRect rect = barPartRect();
+
+ if (HORIZONTAL == asOrientation) {
+ int width = static_cast<int>(rect.width()*valueRatio());
+ if (!style()->isLeftToRightDirection()) {
+ rect.setX(rect.x() + (rect.width() - width));
+ rect.setWidth(width);
+ } else
+ rect.setWidth(width);
+ } else {
+ int height = static_cast<int>(rect.height()*valueRatio());
+ rect.setY(rect.y() + (rect.height() - height));
+ rect.setHeight(height);
+ }
+
+ return rect;
+}
+
+EBoxOrient RenderMeter::orientation() const
+{
+ IntRect rect = barPartRect();
+ return rect.height() <= rect.width() ? HORIZONTAL : VERTICAL;
+}
+
+PseudoId RenderMeter::valuePseudoId(EBoxOrient asOrientation) const
+{
+ HTMLMeterElement* element = static_cast<HTMLMeterElement*>(node());
+
+ if (HORIZONTAL == asOrientation) {
+ switch (element->gaugeRegion()) {
+ case HTMLMeterElement::GaugeRegionOptimum:
+ return METER_HORIZONTAL_OPTIMUM;
+ case HTMLMeterElement::GaugeRegionSuboptimal:
+ return METER_HORIZONTAL_SUBOPTIMAL;
+ case HTMLMeterElement::GaugeRegionEvenLessGood:
+ return METER_HORIZONTAL_EVEN_LESS_GOOD;
+ }
+ } else {
+ switch (element->gaugeRegion()) {
+ case HTMLMeterElement::GaugeRegionOptimum:
+ return METER_VERTICAL_OPTIMUM;
+ case HTMLMeterElement::GaugeRegionSuboptimal:
+ return METER_VERTICAL_SUBOPTIMAL;
+ case HTMLMeterElement::GaugeRegionEvenLessGood:
+ return METER_VERTICAL_EVEN_LESS_GOOD;
+ }
+ }
+
+ ASSERT_NOT_REACHED();
+ return NOPSEUDO;
+}
+
+PseudoId RenderMeter::barPseudoId(EBoxOrient asOrientation) const
+{
+ return HORIZONTAL == asOrientation ? METER_HORIZONTAL_BAR : METER_VERTICAL_BAR;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/RenderMeter.h b/Source/WebCore/rendering/RenderMeter.h
new file mode 100644
index 0000000..2f5f5f5
--- /dev/null
+++ b/Source/WebCore/rendering/RenderMeter.h
@@ -0,0 +1,80 @@
+/*
+ * 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 RenderMeter_h
+#define RenderMeter_h
+
+#if ENABLE(METER_TAG)
+#include "RenderBlock.h"
+#include "RenderIndicator.h"
+#include "RenderWidget.h"
+
+
+namespace WebCore {
+
+class HTMLMeterElement;
+class MeterPartElement;
+
+class RenderMeter : public RenderIndicator {
+public:
+ RenderMeter(HTMLMeterElement*);
+ virtual ~RenderMeter();
+
+private:
+ virtual const char* renderName() const { return "RenderMeter"; }
+ virtual bool isMeter() const { return true; }
+ virtual void updateFromElement();
+ virtual void computeLogicalWidth();
+ virtual void computeLogicalHeight();
+
+ virtual void layoutParts();
+
+ bool shadowAttached() const { return m_horizontalBarPart; }
+ IntRect valuePartRect(EBoxOrient) const;
+ PseudoId valuePseudoId(EBoxOrient) const;
+ IntRect barPartRect() const;
+ PseudoId barPseudoId(EBoxOrient) const;
+ EBoxOrient orientation() const;
+
+ double valueRatio() const;
+ bool shouldHaveParts() const;
+ PassRefPtr<MeterPartElement> createPart(PseudoId);
+
+ RefPtr<MeterPartElement> m_horizontalBarPart;
+ RefPtr<MeterPartElement> m_horizontalValuePart;
+ RefPtr<MeterPartElement> m_verticalBarPart;
+ RefPtr<MeterPartElement> m_verticalValuePart;
+};
+
+inline RenderMeter* toRenderMeter(RenderObject* object)
+{
+ ASSERT(!object || object->isMeter());
+ return static_cast<RenderMeter*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderMeter(const RenderMeter*);
+
+} // namespace WebCore
+
+#endif
+
+#endif // RenderMeter_h
+
diff --git a/Source/WebCore/rendering/RenderObject.cpp b/Source/WebCore/rendering/RenderObject.cpp
new file mode 100644
index 0000000..27a53d0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderObject.cpp
@@ -0,0 +1,2776 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (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.
+ *
+ */
+
+#include "config.h"
+#include "RenderObject.h"
+
+#include "AXObjectCache.h"
+#include "Chrome.h"
+#include "CSSStyleSelector.h"
+#include "DashArray.h"
+#include "EditingBoundary.h"
+#include "FloatQuad.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "HitTestResult.h"
+#include "Page.h"
+#include "RenderArena.h"
+#include "RenderCounter.h"
+#include "RenderFlexibleBox.h"
+#include "RenderImage.h"
+#include "RenderImageResourceStyleImage.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderListItem.h"
+#include "RenderRuby.h"
+#include "RenderRubyText.h"
+#include "RenderTableCell.h"
+#include "RenderTableCol.h"
+#include "RenderTableRow.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "TransformState.h"
+#include "htmlediting.h"
+#include <algorithm>
+#ifdef ANDROID_LAYOUT
+#include "Settings.h"
+#endif
+#include <stdio.h>
+#include <wtf/RefCountedLeakCounter.h>
+#include <wtf/UnusedParam.h>
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerCompositor.h"
+#endif
+
+#if ENABLE(WML)
+#include "WMLNames.h"
+#endif
+
+#if ENABLE(SVG)
+#include "RenderSVGResourceContainer.h"
+#include "SVGRenderSupport.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+#ifndef NDEBUG
+static void* baseOfRenderObjectBeingDeleted;
+#endif
+
+bool RenderObject::s_affectsParentBlock = false;
+
+void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw()
+{
+ return renderArena->allocate(sz);
+}
+
+void RenderObject::operator delete(void* ptr, size_t sz)
+{
+ ASSERT(baseOfRenderObjectBeingDeleted == ptr);
+
+ // Stash size where destroy can find it.
+ *(size_t *)ptr = sz;
+}
+
+RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
+{
+ Document* doc = node->document();
+ RenderArena* arena = doc->renderArena();
+
+ // Minimal support for content properties replacing an entire element.
+ // Works only if we have exactly one piece of content and it's a URL.
+ // Otherwise acts as if we didn't support this feature.
+ const ContentData* contentData = style->contentData();
+ if (contentData && !contentData->next() && contentData->isImage() && doc != node) {
+ RenderImage* image = new (arena) RenderImage(node);
+ image->setStyle(style);
+ if (StyleImage* styleImage = contentData->image())
+ image->setImageResource(RenderImageResourceStyleImage::create(styleImage));
+ else
+ image->setImageResource(RenderImageResource::create());
+ return image;
+ }
+
+ if (node->hasTagName(rubyTag)) {
+ if (style->display() == INLINE)
+ return new (arena) RenderRubyAsInline(node);
+ else if (style->display() == BLOCK)
+ return new (arena) RenderRubyAsBlock(node);
+ }
+ // treat <rt> as ruby text ONLY if it still has its default treatment of block
+ if (node->hasTagName(rtTag) && style->display() == BLOCK)
+ return new (arena) RenderRubyText(node);
+
+ switch (style->display()) {
+ case NONE:
+ return 0;
+ case INLINE:
+ return new (arena) RenderInline(node);
+ case BLOCK:
+ case INLINE_BLOCK:
+ case RUN_IN:
+ case COMPACT:
+ return new (arena) RenderBlock(node);
+ case LIST_ITEM:
+ return new (arena) RenderListItem(node);
+ case TABLE:
+ case INLINE_TABLE:
+ return new (arena) RenderTable(node);
+ case TABLE_ROW_GROUP:
+ case TABLE_HEADER_GROUP:
+ case TABLE_FOOTER_GROUP:
+ return new (arena) RenderTableSection(node);
+ case TABLE_ROW:
+ return new (arena) RenderTableRow(node);
+ case TABLE_COLUMN_GROUP:
+ case TABLE_COLUMN:
+ return new (arena) RenderTableCol(node);
+ case TABLE_CELL:
+ return new (arena) RenderTableCell(node);
+ case TABLE_CAPTION:
+#if ENABLE(WCSS)
+ // As per the section 17.1 of the spec WAP-239-WCSS-20011026-a.pdf,
+ // the marquee box inherits and extends the characteristics of the
+ // principal block box ([CSS2] section 9.2.1).
+ case WAP_MARQUEE:
+#endif
+ return new (arena) RenderBlock(node);
+ case BOX:
+ case INLINE_BOX:
+ return new (arena) RenderFlexibleBox(node);
+ }
+
+ return 0;
+}
+
+#ifndef NDEBUG
+static WTF::RefCountedLeakCounter renderObjectCounter("RenderObject");
+#endif
+
+RenderObject::RenderObject(Node* node)
+ : CachedResourceClient()
+ , m_style(0)
+ , m_node(node)
+ , m_parent(0)
+ , m_previous(0)
+ , m_next(0)
+#ifndef NDEBUG
+ , m_hasAXObject(false)
+ , m_setNeedsLayoutForbidden(false)
+#endif
+ , m_needsLayout(false)
+ , m_needsPositionedMovementLayout(false)
+ , m_normalChildNeedsLayout(false)
+ , m_posChildNeedsLayout(false)
+ , m_preferredLogicalWidthsDirty(false)
+ , m_floating(false)
+ , m_positioned(false)
+ , m_relPositioned(false)
+ , m_paintBackground(false)
+ , m_isAnonymous(node == node->document())
+ , m_isText(false)
+ , m_isBox(false)
+ , m_inline(true)
+ , m_replaced(false)
+ , m_isDragging(false)
+ , m_hasLayer(false)
+ , m_hasOverflowClip(false)
+ , m_hasTransform(false)
+ , m_hasReflection(false)
+ , m_hasOverrideSize(false)
+ , m_hasCounterNodeMap(false)
+ , m_everHadLayout(false)
+ , m_childrenInline(false)
+ , m_marginBeforeQuirk(false)
+ , m_marginAfterQuirk(false)
+ , m_hasMarkupTruncation(false)
+ , m_selectionState(SelectionNone)
+ , m_hasColumns(false)
+ , m_cellWidthChanged(false)
+{
+#ifndef NDEBUG
+ renderObjectCounter.increment();
+#endif
+ ASSERT(node);
+}
+
+RenderObject::~RenderObject()
+{
+ ASSERT(!node() || documentBeingDestroyed() || !frame()->view() || frame()->view()->layoutRoot() != this);
+#ifndef NDEBUG
+ ASSERT(!m_hasAXObject);
+ renderObjectCounter.decrement();
+#endif
+}
+
+RenderTheme* RenderObject::theme() const
+{
+ ASSERT(document()->page());
+
+ return document()->page()->theme();
+}
+
+bool RenderObject::isDescendantOf(const RenderObject* obj) const
+{
+ for (const RenderObject* r = this; r; r = r->m_parent) {
+ if (r == obj)
+ return true;
+ }
+ return false;
+}
+
+bool RenderObject::isBody() const
+{
+ return node() && node()->hasTagName(bodyTag);
+}
+
+bool RenderObject::isHR() const
+{
+ return node() && node()->hasTagName(hrTag);
+}
+
+bool RenderObject::isLegend() const
+{
+ return node() && (node()->hasTagName(legendTag)
+#if ENABLE(WML)
+ || node()->hasTagName(WMLNames::insertedLegendTag)
+#endif
+ );
+}
+
+bool RenderObject::isHTMLMarquee() const
+{
+ return node() && node()->renderer() == this && node()->hasTagName(marqueeTag);
+}
+
+void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild)
+{
+ RenderObjectChildList* children = virtualChildren();
+ ASSERT(children);
+ if (!children)
+ return;
+
+ bool needsTable = false;
+
+ if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP)
+ needsTable = !isTable();
+ else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION)
+ needsTable = !isTable();
+ else if (newChild->isTableSection())
+ needsTable = !isTable();
+ else if (newChild->isTableRow())
+ needsTable = !isTableSection();
+ else if (newChild->isTableCell()) {
+ needsTable = !isTableRow();
+ // I'm not 100% sure this is the best way to fix this, but without this
+ // change we recurse infinitely when trying to render the CSS2 test page:
+ // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html.
+ // See Radar 2925291.
+ if (needsTable && isTableCell() && !children->firstChild() && !newChild->isTableCell())
+ needsTable = false;
+ }
+
+ if (needsTable) {
+ RenderTable* table;
+ RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild();
+ if (afterChild && afterChild->isAnonymous() && afterChild->isTable())
+ table = toRenderTable(afterChild);
+ else {
+ table = new (renderArena()) RenderTable(document() /* is anonymous */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE);
+ table->setStyle(newStyle.release());
+ addChild(table, beforeChild);
+ }
+ table->addChild(newChild);
+ } else {
+ // Just add it...
+ children->insertChildNode(this, newChild, beforeChild);
+ }
+ RenderCounter::rendererSubtreeAttached(newChild);
+ if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) {
+ RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText();
+ if (textToTransform)
+ toRenderText(newChild)->setText(textToTransform.release(), true);
+ }
+}
+
+void RenderObject::removeChild(RenderObject* oldChild)
+{
+ RenderObjectChildList* children = virtualChildren();
+ ASSERT(children);
+ if (!children)
+ return;
+
+ // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode
+ // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
+ // layout anyway).
+ if (oldChild->isFloatingOrPositioned())
+ toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists();
+
+ children->removeChildNode(this, oldChild);
+}
+
+RenderObject* RenderObject::nextInPreOrder() const
+{
+ if (RenderObject* o = firstChild())
+ return o;
+
+ return nextInPreOrderAfterChildren();
+}
+
+RenderObject* RenderObject::nextInPreOrderAfterChildren() const
+{
+ RenderObject* o;
+ if (!(o = nextSibling())) {
+ o = parent();
+ while (o && !o->nextSibling())
+ o = o->parent();
+ if (o)
+ o = o->nextSibling();
+ }
+
+ return o;
+}
+
+RenderObject* RenderObject::nextInPreOrder(RenderObject* stayWithin) const
+{
+ if (RenderObject* o = firstChild())
+ return o;
+
+ return nextInPreOrderAfterChildren(stayWithin);
+}
+
+RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin) const
+{
+ if (this == stayWithin)
+ return 0;
+
+ const RenderObject* current = this;
+ RenderObject* next;
+ while (!(next = current->nextSibling())) {
+ current = current->parent();
+ if (!current || current == stayWithin)
+ return 0;
+ }
+ return next;
+}
+
+RenderObject* RenderObject::previousInPreOrder() const
+{
+ if (RenderObject* o = previousSibling()) {
+ while (o->lastChild())
+ o = o->lastChild();
+ return o;
+ }
+
+ return parent();
+}
+
+RenderObject* RenderObject::childAt(unsigned index) const
+{
+ RenderObject* child = firstChild();
+ for (unsigned i = 0; child && i < index; i++)
+ child = child->nextSibling();
+ return child;
+}
+
+RenderObject* RenderObject::firstLeafChild() const
+{
+ RenderObject* r = firstChild();
+ while (r) {
+ RenderObject* n = 0;
+ n = r->firstChild();
+ if (!n)
+ break;
+ r = n;
+ }
+ return r;
+}
+
+RenderObject* RenderObject::lastLeafChild() const
+{
+ RenderObject* r = lastChild();
+ while (r) {
+ RenderObject* n = 0;
+ n = r->lastChild();
+ if (!n)
+ break;
+ r = n;
+ }
+ return r;
+}
+
+static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject,
+ RenderLayer*& beforeChild)
+{
+ if (obj->hasLayer()) {
+ if (!beforeChild && newObject) {
+ // We need to figure out the layer that follows newObject. We only do
+ // this the first time we find a child layer, and then we update the
+ // pointer values for newObject and beforeChild used by everyone else.
+ beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
+ newObject = 0;
+ }
+ parentLayer->addChild(toRenderBoxModelObject(obj)->layer(), beforeChild);
+ return;
+ }
+
+ for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling())
+ addLayers(curr, parentLayer, newObject, beforeChild);
+}
+
+void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject)
+{
+ if (!parentLayer)
+ return;
+
+ RenderObject* object = newObject;
+ RenderLayer* beforeChild = 0;
+ WebCore::addLayers(this, parentLayer, object, beforeChild);
+}
+
+void RenderObject::removeLayers(RenderLayer* parentLayer)
+{
+ if (!parentLayer)
+ return;
+
+ if (hasLayer()) {
+ parentLayer->removeChild(toRenderBoxModelObject(this)->layer());
+ return;
+ }
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->removeLayers(parentLayer);
+}
+
+void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent)
+{
+ if (!newParent)
+ return;
+
+ if (hasLayer()) {
+ RenderLayer* layer = toRenderBoxModelObject(this)->layer();
+ ASSERT(oldParent == layer->parent());
+ if (oldParent)
+ oldParent->removeChild(layer);
+ newParent->addChild(layer);
+ return;
+ }
+
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->moveLayers(oldParent, newParent);
+}
+
+RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint,
+ bool checkParent)
+{
+ // Error check the parent layer passed in. If it's null, we can't find anything.
+ if (!parentLayer)
+ return 0;
+
+ // Step 1: If our layer is a child of the desired parent, then return our layer.
+ RenderLayer* ourLayer = hasLayer() ? toRenderBoxModelObject(this)->layer() : 0;
+ if (ourLayer && ourLayer->parent() == parentLayer)
+ return ourLayer;
+
+ // Step 2: If we don't have a layer, or our layer is the desired parent, then descend
+ // into our siblings trying to find the next layer whose parent is the desired parent.
+ if (!ourLayer || ourLayer == parentLayer) {
+ for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild();
+ curr; curr = curr->nextSibling()) {
+ RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false);
+ if (nextLayer)
+ return nextLayer;
+ }
+ }
+
+ // Step 3: If our layer is the desired parent layer, then we're finished. We didn't
+ // find anything.
+ if (parentLayer == ourLayer)
+ return 0;
+
+ // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that
+ // follow us to see if we can locate a layer.
+ if (checkParent && parent())
+ return parent()->findNextLayer(parentLayer, this, true);
+
+ return 0;
+}
+
+RenderLayer* RenderObject::enclosingLayer() const
+{
+ const RenderObject* curr = this;
+ while (curr) {
+ RenderLayer* layer = curr->hasLayer() ? toRenderBoxModelObject(curr)->layer() : 0;
+ if (layer)
+ return layer;
+ curr = curr->parent();
+ }
+ return 0;
+}
+
+RenderBox* RenderObject::enclosingBox() const
+{
+ RenderObject* curr = const_cast<RenderObject*>(this);
+ while (curr) {
+ if (curr->isBox())
+ return toRenderBox(curr);
+ curr = curr->parent();
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const
+{
+ RenderObject* curr = const_cast<RenderObject*>(this);
+ while (curr) {
+ if (curr->isBoxModelObject())
+ return toRenderBoxModelObject(curr);
+ curr = curr->parent();
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+RenderBlock* RenderObject::firstLineBlock() const
+{
+ return 0;
+}
+
+void RenderObject::setPreferredLogicalWidthsDirty(bool b, bool markParents)
+{
+ bool alreadyDirty = m_preferredLogicalWidthsDirty;
+ m_preferredLogicalWidthsDirty = b;
+ if (b && !alreadyDirty && markParents && (isText() || (style()->position() != FixedPosition && style()->position() != AbsolutePosition)))
+ invalidateContainerPreferredLogicalWidths();
+}
+
+void RenderObject::invalidateContainerPreferredLogicalWidths()
+{
+ // In order to avoid pathological behavior when inlines are deeply nested, we do include them
+ // in the chain that we mark dirty (even though they're kind of irrelevant).
+ RenderObject* o = isTableCell() ? containingBlock() : container();
+ while (o && !o->m_preferredLogicalWidthsDirty) {
+ // Don't invalidate the outermost object of an unrooted subtree. That object will be
+ // invalidated when the subtree is added to the document.
+ RenderObject* container = o->isTableCell() ? o->containingBlock() : o->container();
+ if (!container && !o->isRenderView())
+ break;
+
+ o->m_preferredLogicalWidthsDirty = true;
+ if (o->style()->position() == FixedPosition || o->style()->position() == AbsolutePosition)
+ // A positioned object has no effect on the min/max width of its containing block ever.
+ // We can optimize this case and not go up any further.
+ break;
+ o = container;
+ }
+}
+
+void RenderObject::setLayerNeedsFullRepaint()
+{
+ ASSERT(hasLayer());
+ toRenderBoxModelObject(this)->layer()->setNeedsFullRepaint(true);
+}
+
+RenderBlock* RenderObject::containingBlock() const
+{
+ if (isTableCell()) {
+ const RenderTableCell* cell = toRenderTableCell(this);
+ if (parent() && cell->section())
+ return cell->table();
+ return 0;
+ }
+
+ if (isRenderView())
+ return const_cast<RenderView*>(toRenderView(this));
+
+ RenderObject* o = parent();
+ if (!isText() && m_style->position() == FixedPosition) {
+ while (o && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock()))
+ o = o->parent();
+ } else if (!isText() && m_style->position() == AbsolutePosition) {
+ while (o && (o->style()->position() == StaticPosition || (o->isInline() && !o->isReplaced())) && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) {
+ // For relpositioned inlines, we return the nearest enclosing block. We don't try
+ // to return the inline itself. This allows us to avoid having a positioned objects
+ // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result
+ // from this method. The container() method can actually be used to obtain the
+ // inline directly.
+ if (o->style()->position() == RelativePosition && o->isInline() && !o->isReplaced())
+ return o->containingBlock();
+#if ENABLE(SVG)
+ if (o->isSVGForeignObject()) //foreignObject is the containing block for contents inside it
+ break;
+#endif
+
+ o = o->parent();
+ }
+ } else {
+ while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection()
+ || o->isTableCol() || o->isFrameSet() || o->isMedia()
+#if ENABLE(SVG)
+ || o->isSVGContainer() || o->isSVGRoot()
+#endif
+ ))
+ o = o->parent();
+ }
+
+ if (!o || !o->isRenderBlock())
+ return 0; // This can still happen in case of an orphaned tree
+
+ return toRenderBlock(o);
+}
+
+static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer)
+{
+ // Nobody will use multiple layers without wanting fancy positioning.
+ if (layer->next())
+ return true;
+
+ // Make sure we have a valid image.
+ StyleImage* img = layer->image();
+ if (!img || !img->canRender(renderer->style()->effectiveZoom()))
+ return false;
+
+ if (!layer->xPosition().isZero() || !layer->yPosition().isZero())
+ return true;
+
+ if (layer->size().type == SizeLength) {
+ if (layer->size().size.width().isPercent() || layer->size().size.height().isPercent())
+ return true;
+ } else if (layer->size().type == Contain || layer->size().type == Cover || img->usesImageContainerSize())
+ return true;
+
+ return false;
+}
+
+bool RenderObject::mustRepaintBackgroundOrBorder() const
+{
+ if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers()))
+ return true;
+
+ // If we don't have a background/border/mask, then nothing to do.
+ if (!hasBoxDecorations())
+ return false;
+
+ if (mustRepaintFillLayers(this, style()->backgroundLayers()))
+ return true;
+
+ // Our fill layers are ok. Let's check border.
+ if (style()->hasBorder()) {
+ // Border images are not ok.
+ StyleImage* borderImage = style()->borderImage().image();
+ bool shouldPaintBorderImage = borderImage && borderImage->canRender(style()->effectiveZoom());
+
+ // If the image hasn't loaded, we're still using the normal border style.
+ if (shouldPaintBorderImage && borderImage->isLoaded())
+ return true;
+ }
+
+ return false;
+}
+
+void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
+ BoxSide s, Color c, EBorderStyle style,
+ int adjbw1, int adjbw2)
+{
+ int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1);
+
+ if (style == DOUBLE && width < 3)
+ style = SOLID;
+
+ switch (style) {
+ case BNONE:
+ case BHIDDEN:
+ return;
+ case DOTTED:
+ case DASHED:
+ graphicsContext->setStrokeColor(c, m_style->colorSpace());
+ graphicsContext->setStrokeThickness(width);
+ graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke);
+
+ if (width > 0)
+ switch (s) {
+ case BSBottom:
+ case BSTop:
+ graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2));
+ break;
+ case BSRight:
+ case BSLeft:
+ graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2));
+ break;
+ }
+ break;
+ case DOUBLE: {
+ int third = (width + 1) / 3;
+
+ if (adjbw1 == 0 && adjbw2 == 0) {
+ graphicsContext->setStrokeStyle(NoStroke);
+ graphicsContext->setFillColor(c, m_style->colorSpace());
+ switch (s) {
+ case BSTop:
+ case BSBottom:
+ graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, third));
+ graphicsContext->drawRect(IntRect(x1, y2 - third, x2 - x1, third));
+ break;
+ case BSLeft:
+ graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1));
+ graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1));
+ break;
+ case BSRight:
+ graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1));
+ graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1));
+ break;
+ }
+ } else {
+ int adjbw1bigthird = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 3;
+ int adjbw2bigthird = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 3;
+
+ switch (s) {
+ case BSTop:
+ drawLineForBoxSide(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0),
+ y1, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y1 + third,
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawLineForBoxSide(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0),
+ y2 - third, x2 - max((adjbw2 * 2 + 1) / 3, 0), y2,
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSLeft:
+ drawLineForBoxSide(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0),
+ x1 + third, y2 - max((-adjbw2 * 2 + 1) / 3, 0),
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawLineForBoxSide(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0),
+ x2, y2 - max((adjbw2 * 2 + 1) / 3, 0),
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSBottom:
+ drawLineForBoxSide(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0),
+ y1, x2 - max((adjbw2 * 2 + 1) / 3, 0), y1 + third,
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawLineForBoxSide(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0),
+ y2 - third, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y2,
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ case BSRight:
+ drawLineForBoxSide(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0),
+ x1 + third, y2 - max((adjbw2 * 2 + 1) / 3, 0),
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ drawLineForBoxSide(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0),
+ x2, y2 - max((-adjbw2 * 2 + 1) / 3, 0),
+ s, c, SOLID, adjbw1bigthird, adjbw2bigthird);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case RIDGE:
+ case GROOVE:
+ {
+ EBorderStyle s1;
+ EBorderStyle s2;
+ if (style == GROOVE) {
+ s1 = INSET;
+ s2 = OUTSET;
+ } else {
+ s1 = OUTSET;
+ s2 = INSET;
+ }
+
+ int adjbw1bighalf = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 2;
+ int adjbw2bighalf = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 2;
+
+ switch (s) {
+ case BSTop:
+ drawLineForBoxSide(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2,
+ s, c, s1, adjbw1bighalf, adjbw2bighalf);
+ drawLineForBoxSide(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2,
+ s, c, s2, adjbw1 / 2, adjbw2 / 2);
+ break;
+ case BSLeft:
+ drawLineForBoxSide(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2,
+ s, c, s1, adjbw1bighalf, adjbw2bighalf);
+ drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2,
+ s, c, s2, adjbw1 / 2, adjbw2 / 2);
+ break;
+ case BSBottom:
+ drawLineForBoxSide(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2,
+ s, c, s2, adjbw1bighalf, adjbw2bighalf);
+ drawLineForBoxSide(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2,
+ s, c, s1, adjbw1 / 2, adjbw2 / 2);
+ break;
+ case BSRight:
+ drawLineForBoxSide(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2,
+ s, c, s2, adjbw1bighalf, adjbw2bighalf);
+ drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2,
+ s, c, s1, adjbw1 / 2, adjbw2 / 2);
+ break;
+ }
+ break;
+ }
+ case INSET:
+ if (s == BSTop || s == BSLeft)
+ c = c.dark();
+ // fall through
+ case OUTSET:
+ if (style == OUTSET && (s == BSBottom || s == BSRight))
+ c = c.dark();
+ // fall through
+ case SOLID: {
+ graphicsContext->setStrokeStyle(NoStroke);
+ graphicsContext->setFillColor(c, m_style->colorSpace());
+ ASSERT(x2 >= x1);
+ ASSERT(y2 >= y1);
+ if (!adjbw1 && !adjbw2) {
+ graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1));
+ return;
+ }
+ FloatPoint quad[4];
+ switch (s) {
+ case BSTop:
+ quad[0] = FloatPoint(x1 + max(-adjbw1, 0), y1);
+ quad[1] = FloatPoint(x1 + max(adjbw1, 0), y2);
+ quad[2] = FloatPoint(x2 - max(adjbw2, 0), y2);
+ quad[3] = FloatPoint(x2 - max(-adjbw2, 0), y1);
+ break;
+ case BSBottom:
+ quad[0] = FloatPoint(x1 + max(adjbw1, 0), y1);
+ quad[1] = FloatPoint(x1 + max(-adjbw1, 0), y2);
+ quad[2] = FloatPoint(x2 - max(-adjbw2, 0), y2);
+ quad[3] = FloatPoint(x2 - max(adjbw2, 0), y1);
+ break;
+ case BSLeft:
+ quad[0] = FloatPoint(x1, y1 + max(-adjbw1, 0));
+ quad[1] = FloatPoint(x1, y2 - max(-adjbw2, 0));
+ quad[2] = FloatPoint(x2, y2 - max(adjbw2, 0));
+ quad[3] = FloatPoint(x2, y1 + max(adjbw1, 0));
+ break;
+ case BSRight:
+ quad[0] = FloatPoint(x1, y1 + max(adjbw1, 0));
+ quad[1] = FloatPoint(x1, y2 - max(adjbw2, 0));
+ quad[2] = FloatPoint(x2, y2 - max(-adjbw2, 0));
+ quad[3] = FloatPoint(x2, y1 + max(-adjbw1, 0));
+ break;
+ }
+ graphicsContext->drawConvexPolygon(4, quad);
+ break;
+ }
+ }
+}
+
+IntRect RenderObject::borderInnerRect(const IntRect& borderRect, unsigned short topWidth, unsigned short bottomWidth, unsigned short leftWidth, unsigned short rightWidth) const
+{
+ return IntRect(
+ borderRect.x() + leftWidth,
+ borderRect.y() + topWidth,
+ borderRect.width() - leftWidth - rightWidth,
+ borderRect.height() - topWidth - bottomWidth);
+}
+
+#if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING)
+void RenderObject::drawBoxSideFromPath(GraphicsContext* graphicsContext, IntRect borderRect, Path borderPath,
+ float thickness, float drawThickness, BoxSide s, const RenderStyle* style,
+ Color c, EBorderStyle borderStyle)
+{
+ if (thickness <= 0)
+ return;
+
+ if (borderStyle == DOUBLE && thickness < 3)
+ borderStyle = SOLID;
+
+ switch (borderStyle) {
+ case BNONE:
+ case BHIDDEN:
+ return;
+ case DOTTED:
+ case DASHED: {
+ graphicsContext->setStrokeColor(c, style->colorSpace());
+
+ // The stroke is doubled here because the provided path is the
+ // outside edge of the border so half the stroke is clipped off.
+ // The extra multiplier is so that the clipping mask can antialias
+ // the edges to prevent jaggies.
+ graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f);
+ graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
+
+ // If the number of dashes that fit in the path is odd and non-integral then we
+ // will have an awkwardly-sized dash at the end of the path. To try to avoid that
+ // here, we simply make the whitespace dashes ever so slightly bigger.
+ // FIXME: This could be even better if we tried to manipulate the dash offset
+ // and possibly the whiteSpaceWidth to get the corners dash-symmetrical.
+ float patWidth = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f);
+ float whiteSpaceWidth = patWidth;
+ float numberOfDashes = borderPath.length() / patWidth;
+ bool evenNumberOfFullDashes = !((int)numberOfDashes % 2);
+ bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes);
+ if (!evenNumberOfFullDashes && !integralNumberOfDashes) {
+ float numberOfWhitespaceDashes = numberOfDashes / 2;
+ whiteSpaceWidth += (patWidth / numberOfWhitespaceDashes);
+ }
+
+ DashArray lineDash;
+ lineDash.append(patWidth);
+ lineDash.append(whiteSpaceWidth);
+ graphicsContext->setLineDash(lineDash, patWidth);
+ graphicsContext->strokePath(borderPath);
+ return;
+ }
+ case DOUBLE: {
+ int outerBorderTopWidth = style->borderTopWidth() / 3;
+ int outerBorderRightWidth = style->borderRightWidth() / 3;
+ int outerBorderBottomWidth = style->borderBottomWidth() / 3;
+ int outerBorderLeftWidth = style->borderLeftWidth() / 3;
+
+ int innerBorderTopWidth = style->borderTopWidth() * 2 / 3;
+ int innerBorderRightWidth = style->borderRightWidth() * 2 / 3;
+ int innerBorderBottomWidth = style->borderBottomWidth() * 2 / 3;
+ int innerBorderLeftWidth = style->borderLeftWidth() * 2 / 3;
+
+ // We need certain integer rounding results
+ if (style->borderTopWidth() % 3 == 2)
+ outerBorderTopWidth += 1;
+ if (style->borderRightWidth() % 3 == 2)
+ outerBorderRightWidth += 1;
+ if (style->borderBottomWidth() % 3 == 2)
+ outerBorderBottomWidth += 1;
+ if (style->borderLeftWidth() % 3 == 2)
+ outerBorderLeftWidth += 1;
+
+ if (style->borderTopWidth() % 3 == 1)
+ innerBorderTopWidth += 1;
+ if (style->borderRightWidth() % 3 == 1)
+ innerBorderRightWidth += 1;
+ if (style->borderBottomWidth() % 3 == 1)
+ innerBorderBottomWidth += 1;
+ if (style->borderLeftWidth() % 3 == 1)
+ innerBorderLeftWidth += 1;
+
+ // Get the inner border rects for both the outer border line and the inner border line
+ IntRect outerBorderInnerRect = borderInnerRect(borderRect, outerBorderTopWidth, outerBorderBottomWidth,
+ outerBorderLeftWidth, outerBorderRightWidth);
+ IntRect innerBorderInnerRect = borderInnerRect(borderRect, innerBorderTopWidth, innerBorderBottomWidth,
+ innerBorderLeftWidth, innerBorderRightWidth);
+
+ // Get the inner radii for the outer border line
+ IntSize outerBorderTopLeftInnerRadius, outerBorderTopRightInnerRadius, outerBorderBottomLeftInnerRadius,
+ outerBorderBottomRightInnerRadius;
+ style->getInnerBorderRadiiForRectWithBorderWidths(outerBorderInnerRect, outerBorderTopWidth, outerBorderBottomWidth,
+ outerBorderLeftWidth, outerBorderRightWidth, outerBorderTopLeftInnerRadius, outerBorderTopRightInnerRadius,
+ outerBorderBottomLeftInnerRadius, outerBorderBottomRightInnerRadius);
+
+ // Get the inner radii for the inner border line
+ IntSize innerBorderTopLeftInnerRadius, innerBorderTopRightInnerRadius, innerBorderBottomLeftInnerRadius,
+ innerBorderBottomRightInnerRadius;
+ style->getInnerBorderRadiiForRectWithBorderWidths(innerBorderInnerRect, innerBorderTopWidth, innerBorderBottomWidth,
+ innerBorderLeftWidth, innerBorderRightWidth, innerBorderTopLeftInnerRadius, innerBorderTopRightInnerRadius,
+ innerBorderBottomLeftInnerRadius, innerBorderBottomRightInnerRadius);
+
+ // Draw inner border line
+ graphicsContext->save();
+ graphicsContext->addRoundedRectClip(innerBorderInnerRect, innerBorderTopLeftInnerRadius,
+ innerBorderTopRightInnerRadius, innerBorderBottomLeftInnerRadius, innerBorderBottomRightInnerRadius);
+ drawBoxSideFromPath(graphicsContext, borderRect, borderPath, thickness, drawThickness, s, style, c, SOLID);
+ graphicsContext->restore();
+
+ // Draw outer border line
+ graphicsContext->save();
+ graphicsContext->clipOutRoundedRect(outerBorderInnerRect, outerBorderTopLeftInnerRadius,
+ outerBorderTopRightInnerRadius, outerBorderBottomLeftInnerRadius, outerBorderBottomRightInnerRadius);
+ drawBoxSideFromPath(graphicsContext, borderRect, borderPath, thickness, drawThickness, s, style, c, SOLID);
+ graphicsContext->restore();
+
+ return;
+ }
+ case RIDGE:
+ case GROOVE:
+ {
+ EBorderStyle s1;
+ EBorderStyle s2;
+ if (borderStyle == GROOVE) {
+ s1 = INSET;
+ s2 = OUTSET;
+ } else {
+ s1 = OUTSET;
+ s2 = INSET;
+ }
+
+ IntRect halfBorderRect = borderInnerRect(borderRect, style->borderLeftWidth() / 2, style->borderBottomWidth() / 2,
+ style->borderLeftWidth() / 2, style->borderRightWidth() / 2);
+
+ IntSize topLeftHalfRadius, topRightHalfRadius, bottomLeftHalfRadius, bottomRightHalfRadius;
+ style->getInnerBorderRadiiForRectWithBorderWidths(halfBorderRect, style->borderLeftWidth() / 2,
+ style->borderBottomWidth() / 2, style->borderLeftWidth() / 2, style->borderRightWidth() / 2,
+ topLeftHalfRadius, topRightHalfRadius, bottomLeftHalfRadius, bottomRightHalfRadius);
+
+ // Paint full border
+ drawBoxSideFromPath(graphicsContext, borderRect, borderPath, thickness, drawThickness, s, style, c, s1);
+
+ // Paint inner only
+ graphicsContext->save();
+ graphicsContext->addRoundedRectClip(halfBorderRect, topLeftHalfRadius, topRightHalfRadius,
+ bottomLeftHalfRadius, bottomRightHalfRadius);
+ drawBoxSideFromPath(graphicsContext, borderRect, borderPath, thickness, drawThickness, s, style, c, s2);
+ graphicsContext->restore();
+
+ return;
+ }
+ case INSET:
+ if (s == BSTop || s == BSLeft)
+ c = c.dark();
+ break;
+ case OUTSET:
+ if (s == BSBottom || s == BSRight)
+ c = c.dark();
+ break;
+ default:
+ break;
+ }
+
+ graphicsContext->setStrokeStyle(NoStroke);
+ graphicsContext->setFillColor(c, style->colorSpace());
+ graphicsContext->drawRect(borderRect);
+}
+#else
+void RenderObject::drawArcForBoxSide(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius,
+ int angleStart, int angleSpan, BoxSide s, Color c,
+ EBorderStyle style, bool firstCorner)
+{
+ // FIXME: This function should be removed when all ports implement GraphicsContext::clipConvexPolygon()!!
+ // At that time, everyone can use RenderObject::drawBoxSideFromPath() instead. This should happen soon.
+ if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2))
+ style = SOLID;
+
+ switch (style) {
+ case BNONE:
+ case BHIDDEN:
+ return;
+ case DOTTED:
+ case DASHED:
+ graphicsContext->setStrokeColor(c, m_style->colorSpace());
+ graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke);
+ graphicsContext->setStrokeThickness(thickness);
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan);
+ break;
+ case DOUBLE: {
+ float third = thickness / 3.0f;
+ float innerThird = (thickness + 1.0f) / 6.0f;
+ int shiftForInner = static_cast<int>(innerThird * 2.5f);
+
+ int outerY = y;
+ int outerHeight = radius.height() * 2;
+ int innerX = x + shiftForInner;
+ int innerY = y + shiftForInner;
+ int innerWidth = (radius.width() - shiftForInner) * 2;
+ int innerHeight = (radius.height() - shiftForInner) * 2;
+ if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) {
+ outerHeight += 2;
+ innerHeight += 2;
+ }
+
+ graphicsContext->setStrokeStyle(SolidStroke);
+ graphicsContext->setStrokeColor(c, m_style->colorSpace());
+ graphicsContext->setStrokeThickness(third);
+ graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan);
+ graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird);
+ graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan);
+ break;
+ }
+ case GROOVE:
+ case RIDGE: {
+ Color c2;
+ if ((style == RIDGE && (s == BSTop || s == BSLeft)) ||
+ (style == GROOVE && (s == BSBottom || s == BSRight)))
+ c2 = c.dark();
+ else {
+ c2 = c;
+ c = c.dark();
+ }
+
+ graphicsContext->setStrokeStyle(SolidStroke);
+ graphicsContext->setStrokeColor(c, m_style->colorSpace());
+ graphicsContext->setStrokeThickness(thickness);
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan);
+
+ float halfThickness = (thickness + 1.0f) / 4.0f;
+ int shiftForInner = static_cast<int>(halfThickness * 1.5f);
+ graphicsContext->setStrokeColor(c2, m_style->colorSpace());
+ graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness);
+ graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2,
+ (radius.height() - shiftForInner) * 2), angleStart, angleSpan);
+ break;
+ }
+ case INSET:
+ if (s == BSTop || s == BSLeft)
+ c = c.dark();
+ case OUTSET:
+ if (style == OUTSET && (s == BSBottom || s == BSRight))
+ c = c.dark();
+ case SOLID:
+ graphicsContext->setStrokeStyle(SolidStroke);
+ graphicsContext->setStrokeColor(c, m_style->colorSpace());
+ graphicsContext->setStrokeThickness(thickness);
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan);
+ break;
+ }
+}
+#endif
+
+void RenderObject::paintFocusRing(GraphicsContext* context, int tx, int ty, RenderStyle* style)
+{
+ Vector<IntRect> focusRingRects;
+ addFocusRingRects(focusRingRects, tx, ty);
+ if (style->outlineStyleIsAuto())
+ context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), style->visitedDependentColor(CSSPropertyOutlineColor));
+ else
+ addPDFURLRect(context, unionRect(focusRingRects));
+}
+
+void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+ Node* n = node();
+ if (!n || !n->isLink() || !n->isElementNode())
+ return;
+ const AtomicString& href = static_cast<Element*>(n)->getAttribute(hrefAttr);
+ if (href.isNull())
+ return;
+ context->setURLForRect(n->document()->completeURL(href), rect);
+}
+
+void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty, int w, int h)
+{
+ if (!hasOutline())
+ return;
+
+ RenderStyle* styleToUse = style();
+ int ow = styleToUse->outlineWidth();
+ EBorderStyle os = styleToUse->outlineStyle();
+
+ Color oc = styleToUse->visitedDependentColor(CSSPropertyOutlineColor);
+
+ int offset = styleToUse->outlineOffset();
+
+ 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;
+
+ tx -= offset;
+ ty -= offset;
+ w += 2 * offset;
+ h += 2 * offset;
+
+ if (h < 0 || w < 0)
+ return;
+
+ drawLineForBoxSide(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, BSLeft, oc, os, ow, ow);
+ drawLineForBoxSide(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, BSTop, oc, os, ow, ow);
+ drawLineForBoxSide(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, BSRight, oc, os, ow, ow);
+ drawLineForBoxSide(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, BSBottom, oc, os, ow, ow);
+}
+
+IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms)
+{
+ if (useTransforms) {
+ Vector<FloatQuad> quads;
+ absoluteQuads(quads);
+
+ size_t n = quads.size();
+ if (!n)
+ return IntRect();
+
+ IntRect result = quads[0].enclosingBoundingBox();
+ for (size_t i = 1; i < n; ++i)
+ result.unite(quads[i].enclosingBoundingBox());
+ return result;
+ }
+
+ FloatPoint absPos = localToAbsolute();
+ Vector<IntRect> rects;
+ absoluteRects(rects, absPos.x(), absPos.y());
+
+ size_t n = rects.size();
+ if (!n)
+ return IntRect();
+
+ IntRect result = rects[0];
+ for (size_t i = 1; i < n; ++i)
+ result.unite(rects[i]);
+ return result;
+}
+
+void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads)
+{
+ Vector<IntRect> rects;
+ // FIXME: addFocusRingRects() needs to be passed this transform-unaware
+ // localToAbsolute() offset here because RenderInline::addFocusRingRects()
+ // implicitly assumes that. This doesn't work correctly with transformed
+ // descendants.
+ FloatPoint absolutePoint = localToAbsolute();
+ addFocusRingRects(rects, absolutePoint.x(), absolutePoint.y());
+ size_t count = rects.size();
+ for (size_t i = 0; i < count; ++i) {
+ IntRect rect = rects[i];
+ rect.move(-absolutePoint.x(), -absolutePoint.y());
+ quads.append(localToAbsoluteQuad(FloatQuad(rect)));
+ }
+}
+
+void RenderObject::addAbsoluteRectForLayer(IntRect& result)
+{
+ if (hasLayer())
+ result.unite(absoluteBoundingBoxRect());
+ for (RenderObject* current = firstChild(); current; current = current->nextSibling())
+ current->addAbsoluteRectForLayer(result);
+}
+
+IntRect RenderObject::paintingRootRect(IntRect& topLevelRect)
+{
+ IntRect result = absoluteBoundingBoxRect();
+ topLevelRect = result;
+ for (RenderObject* current = firstChild(); current; current = current->nextSibling())
+ current->addAbsoluteRectForLayer(result);
+ return result;
+}
+
+void RenderObject::paint(PaintInfo& /*paintInfo*/, int /*tx*/, int /*ty*/)
+{
+}
+
+RenderBoxModelObject* RenderObject::containerForRepaint() const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (RenderView* v = view()) {
+ if (v->usesCompositing()) {
+ RenderLayer* compLayer = enclosingLayer()->enclosingCompositingLayer();
+ return compLayer ? compLayer->renderer() : 0;
+ }
+ }
+#endif
+ // Do root-relative repaint.
+ return 0;
+}
+
+void RenderObject::repaintUsingContainer(RenderBoxModelObject* repaintContainer, const IntRect& r, bool immediate)
+{
+ if (!repaintContainer) {
+ view()->repaintViewRectangle(r, immediate);
+ return;
+ }
+
+#if USE(ACCELERATED_COMPOSITING)
+ RenderView* v = view();
+ if (repaintContainer->isRenderView()) {
+ ASSERT(repaintContainer == v);
+ if (!v->hasLayer() || !v->layer()->isComposited() || v->layer()->backing()->paintingGoesToWindow()) {
+ v->repaintViewRectangle(r, immediate);
+ return;
+ }
+ }
+
+ if (v->usesCompositing()) {
+ ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited());
+ repaintContainer->layer()->setBackingNeedsRepaintInRect(r);
+ }
+#else
+ if (repaintContainer->isRenderView())
+ toRenderView(repaintContainer)->repaintViewRectangle(r, immediate);
+#endif
+}
+
+void RenderObject::repaint(bool immediate)
+{
+ // Don't repaint if we're unrooted (note that view() still returns the view when unrooted)
+ RenderView* view;
+ if (!isRooted(&view))
+ return;
+
+ if (view->printing())
+ return; // Don't repaint if we're printing.
+
+ RenderBoxModelObject* repaintContainer = containerForRepaint();
+ repaintUsingContainer(repaintContainer ? repaintContainer : view, clippedOverflowRectForRepaint(repaintContainer), immediate);
+}
+
+void RenderObject::repaintRectangle(const IntRect& r, bool immediate)
+{
+ // Don't repaint if we're unrooted (note that view() still returns the view when unrooted)
+ RenderView* view;
+ if (!isRooted(&view))
+ return;
+
+ if (view->printing())
+ return; // Don't repaint if we're printing.
+
+ IntRect dirtyRect(r);
+
+ // FIXME: layoutDelta needs to be applied in parts before/after transforms and
+ // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
+ dirtyRect.move(view->layoutDelta());
+
+ RenderBoxModelObject* repaintContainer = containerForRepaint();
+ computeRectForRepaint(repaintContainer, dirtyRect);
+ repaintUsingContainer(repaintContainer ? repaintContainer : view, dirtyRect, immediate);
+}
+
+bool RenderObject::repaintAfterLayoutIfNeeded(RenderBoxModelObject* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox, const IntRect* newBoundsPtr, const IntRect* newOutlineBoxRectPtr)
+{
+ RenderView* v = view();
+ if (v->printing())
+ return false; // Don't repaint if we're printing.
+
+ // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048
+ // ASSERT(!newBoundsPtr || *newBoundsPtr == clippedOverflowRectForRepaint(repaintContainer));
+ IntRect newBounds = newBoundsPtr ? *newBoundsPtr : clippedOverflowRectForRepaint(repaintContainer);
+ IntRect newOutlineBox;
+
+ bool fullRepaint = selfNeedsLayout();
+ // Presumably a background or a border exists if border-fit:lines was specified.
+ if (!fullRepaint && style()->borderFit() == BorderFitLines)
+ fullRepaint = true;
+ if (!fullRepaint) {
+ // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048
+ // ASSERT(!newOutlineBoxRectPtr || *newOutlineBoxRectPtr == outlineBoundsForRepaint(repaintContainer));
+ newOutlineBox = newOutlineBoxRectPtr ? *newOutlineBoxRectPtr : outlineBoundsForRepaint(repaintContainer);
+ if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox)))
+ fullRepaint = true;
+ }
+
+ if (!repaintContainer)
+ repaintContainer = v;
+
+ if (fullRepaint) {
+ repaintUsingContainer(repaintContainer, oldBounds);
+ if (newBounds != oldBounds)
+ repaintUsingContainer(repaintContainer, newBounds);
+ return true;
+ }
+
+ if (newBounds == oldBounds && newOutlineBox == oldOutlineBox)
+ return false;
+
+ int deltaLeft = newBounds.x() - oldBounds.x();
+ if (deltaLeft > 0)
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height()));
+ else if (deltaLeft < 0)
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height()));
+
+ int deltaRight = newBounds.right() - oldBounds.right();
+ if (deltaRight > 0)
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height()));
+ else if (deltaRight < 0)
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height()));
+
+ int deltaTop = newBounds.y() - oldBounds.y();
+ if (deltaTop > 0)
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop));
+ else if (deltaTop < 0)
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop));
+
+ int deltaBottom = newBounds.bottom() - oldBounds.bottom();
+ if (deltaBottom > 0)
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom));
+ else if (deltaBottom < 0)
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom));
+
+ if (newOutlineBox == oldOutlineBox)
+ return false;
+
+ // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly
+ // two rectangles (but typically only one).
+ RenderStyle* outlineStyle = outlineStyleForRepaint();
+ int ow = outlineStyle->outlineSize();
+ int width = abs(newOutlineBox.width() - oldOutlineBox.width());
+ if (width) {
+ int shadowLeft;
+ int shadowRight;
+ style()->getBoxShadowHorizontalExtent(shadowLeft, shadowRight);
+
+ int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0;
+ int boxWidth = isBox() ? toRenderBox(this)->width() : 0;
+ int borderWidth = max(-outlineStyle->outlineOffset(), max(borderRight, max(style()->borderTopRightRadius().width().calcValue(boxWidth), style()->borderBottomRightRadius().width().calcValue(boxWidth)))) + max(ow, shadowRight);
+ IntRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - borderWidth,
+ newOutlineBox.y(),
+ width + borderWidth,
+ max(newOutlineBox.height(), oldOutlineBox.height()));
+ int right = min(newBounds.right(), oldBounds.right());
+ if (rightRect.x() < right) {
+ rightRect.setWidth(min(rightRect.width(), right - rightRect.x()));
+ repaintUsingContainer(repaintContainer, rightRect);
+ }
+ }
+ int height = abs(newOutlineBox.height() - oldOutlineBox.height());
+ if (height) {
+ int shadowTop;
+ int shadowBottom;
+ style()->getBoxShadowVerticalExtent(shadowTop, shadowBottom);
+
+ int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0;
+ int boxHeight = isBox() ? toRenderBox(this)->height() : 0;
+ int borderHeight = max(-outlineStyle->outlineOffset(), max(borderBottom, max(style()->borderBottomLeftRadius().height().calcValue(boxHeight), style()->borderBottomRightRadius().height().calcValue(boxHeight)))) + max(ow, shadowBottom);
+ IntRect bottomRect(newOutlineBox.x(),
+ min(newOutlineBox.bottom(), oldOutlineBox.bottom()) - borderHeight,
+ max(newOutlineBox.width(), oldOutlineBox.width()),
+ height + borderHeight);
+ int bottom = min(newBounds.bottom(), oldBounds.bottom());
+ if (bottomRect.y() < bottom) {
+ bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y()));
+ repaintUsingContainer(repaintContainer, bottomRect);
+ }
+ }
+ return false;
+}
+
+void RenderObject::repaintDuringLayoutIfMoved(const IntRect&)
+{
+}
+
+void RenderObject::repaintOverhangingFloats(bool)
+{
+}
+
+bool RenderObject::checkForRepaintDuringLayout() const
+{
+ // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=20885> It is probably safe to also require
+ // m_everHadLayout. Currently, only RenderBlock::layoutBlock() adds this condition. See also
+ // <https://bugs.webkit.org/show_bug.cgi?id=15129>.
+ return !document()->view()->needsFullRepaint() && !hasLayer();
+}
+
+IntRect RenderObject::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth)
+{
+ IntRect r(clippedOverflowRectForRepaint(repaintContainer));
+ r.inflate(outlineWidth);
+ return r;
+}
+
+IntRect RenderObject::clippedOverflowRectForRepaint(RenderBoxModelObject*)
+{
+ ASSERT_NOT_REACHED();
+ return IntRect();
+}
+
+void RenderObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
+{
+ if (repaintContainer == this)
+ return;
+
+ if (RenderObject* o = parent()) {
+ if (o->isBlockFlow()) {
+ RenderBlock* cb = toRenderBlock(o);
+ if (cb->hasColumns())
+ cb->adjustRectForColumns(rect);
+ }
+
+ if (o->hasOverflowClip()) {
+ // 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.
+ RenderBox* boxParent = toRenderBox(o);
+
+ IntRect repaintRect(rect);
+ repaintRect.move(-boxParent->layer()->scrolledContentOffset()); // For overflow:auto/scroll/hidden.
+
+ IntRect boxRect(0, 0, boxParent->layer()->width(), boxParent->layer()->height());
+ rect = intersection(repaintRect, boxRect);
+ if (rect.isEmpty())
+ return;
+ }
+
+ o->computeRectForRepaint(repaintContainer, rect, fixed);
+ }
+}
+
+void RenderObject::dirtyLinesFromChangedChild(RenderObject*)
+{
+}
+
+#ifndef NDEBUG
+
+void RenderObject::showTreeForThis() const
+{
+ if (node())
+ node()->showTreeForThis();
+}
+
+void RenderObject::showRenderObject() const
+{
+ showRenderObject(0);
+}
+
+void RenderObject::showRenderObject(int printedCharacters) const
+{
+ // As this function is intended to be used when debugging, the
+ // this pointer may be 0.
+ if (!this) {
+ fputs("(null)\n", stderr);
+ return;
+ }
+
+ printedCharacters += fprintf(stderr, "%s %p", renderName(), this);
+
+ if (node()) {
+ if (printedCharacters)
+ for (; printedCharacters < 39; printedCharacters++)
+ fputc(' ', stderr);
+ fputc('\t', stderr);
+ node()->showNode();
+ } else
+ fputc('\n', stderr);
+}
+
+void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const
+{
+ int printedCharacters = 0;
+ if (markedObject1 == this && markedLabel1)
+ printedCharacters += fprintf(stderr, "%s", markedLabel1);
+ if (markedObject2 == this && markedLabel2)
+ printedCharacters += fprintf(stderr, "%s", markedLabel2);
+ for (; printedCharacters < depth * 2; printedCharacters++)
+ fputc(' ', stderr);
+
+ showRenderObject(printedCharacters);
+ if (!this)
+ return;
+
+ for (const RenderObject* child = firstChild(); child; child = child->nextSibling())
+ child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1);
+}
+
+#endif // NDEBUG
+
+Color RenderObject::selectionBackgroundColor() const
+{
+ Color color;
+ if (style()->userSelect() != SELECT_NONE) {
+ RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION);
+ if (pseudoStyle && pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).isValid())
+ color = pseudoStyle->visitedDependentColor(CSSPropertyBackgroundColor).blendWithWhite();
+ else
+ color = frame()->selection()->isFocusedAndActive() ?
+ theme()->activeSelectionBackgroundColor() :
+ theme()->inactiveSelectionBackgroundColor();
+ }
+
+ return color;
+}
+
+Color RenderObject::selectionColor(int colorProperty) const
+{
+ Color color;
+ // If the element is unselectable, or we are only painting the selection,
+ // don't override the foreground color with the selection foreground color.
+ if (style()->userSelect() == SELECT_NONE
+ || (frame()->view()->paintBehavior() & PaintBehaviorSelectionOnly))
+ return color;
+
+ if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(SELECTION)) {
+ color = pseudoStyle->visitedDependentColor(colorProperty);
+ if (!color.isValid())
+ color = pseudoStyle->visitedDependentColor(CSSPropertyColor);
+ } else
+ color = frame()->selection()->isFocusedAndActive() ?
+ theme()->activeSelectionForegroundColor() :
+ theme()->inactiveSelectionForegroundColor();
+
+ return color;
+}
+
+Color RenderObject::selectionForegroundColor() const
+{
+ return selectionColor(CSSPropertyWebkitTextFillColor);
+}
+
+Color RenderObject::selectionEmphasisMarkColor() const
+{
+ return selectionColor(CSSPropertyWebkitTextEmphasisColor);
+}
+
+#if ENABLE(DRAG_SUPPORT)
+Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const
+{
+ if (!dhtmlOK && !uaOK)
+ return 0;
+
+ for (const RenderObject* curr = this; curr; curr = curr->parent()) {
+ Node* elt = curr->node();
+ if (elt && elt->nodeType() == Node::TEXT_NODE) {
+ // Since there's no way for the author to address the -webkit-user-drag style for a text node,
+ // we use our own judgement.
+ if (uaOK && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) {
+ dhtmlWillDrag = false;
+ return curr->node();
+ }
+ if (elt->canStartSelection())
+ // In this case we have a click in the unselected portion of text. If this text is
+ // selectable, we want to start the selection process instead of looking for a parent
+ // to try to drag.
+ return 0;
+ } else {
+ EUserDrag dragMode = curr->style()->userDrag();
+ if (dhtmlOK && dragMode == DRAG_ELEMENT) {
+ dhtmlWillDrag = true;
+ return curr->node();
+ }
+ if (uaOK && dragMode == DRAG_AUTO
+ && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) {
+ dhtmlWillDrag = false;
+ return curr->node();
+ }
+ }
+ }
+ return 0;
+}
+#endif // ENABLE(DRAG_SUPPORT)
+
+void RenderObject::selectionStartEnd(int& spos, int& epos) const
+{
+ view()->selectionStartEnd(spos, epos);
+}
+
+void RenderObject::handleDynamicFloatPositionChange()
+{
+ // We have gone from not affecting the inline status of the parent flow to suddenly
+ // having an impact. See if there is a mismatch between the parent flow's
+ // childrenInline() state and our state.
+ setInline(style()->isDisplayInlineType());
+ if (isInline() != parent()->childrenInline()) {
+ if (!isInline())
+ toRenderBoxModelObject(parent())->childBecameNonInline(this);
+ else {
+ // An anonymous block must be made to wrap this inline.
+ RenderBlock* block = toRenderBlock(parent())->createAnonymousBlock();
+ RenderObjectChildList* childlist = parent()->virtualChildren();
+ childlist->insertChildNode(parent(), block, this);
+ block->children()->appendChildNode(block, childlist->removeChildNode(parent(), this));
+ }
+ }
+}
+
+void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style)
+{
+ if (!isText() && style)
+ setStyle(animation()->updateAnimations(this, style.get()));
+ else
+ setStyle(style);
+}
+
+StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsigned contextSensitiveProperties) const
+{
+#if USE(ACCELERATED_COMPOSITING)
+ // If transform changed, and we are not composited, need to do a layout.
+ if (contextSensitiveProperties & ContextSensitivePropertyTransform) {
+ // Text nodes share style with their parents but transforms don't apply to them,
+ // hence the !isText() check.
+ // FIXME: when transforms are taken into account for overflow, we will need to do a layout.
+ if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited()))
+ diff = StyleDifferenceLayout;
+ else if (diff < StyleDifferenceRecompositeLayer)
+ diff = StyleDifferenceRecompositeLayer;
+ }
+
+ // If opacity changed, and we are not composited, need to repaint (also
+ // ignoring text nodes)
+ if (contextSensitiveProperties & ContextSensitivePropertyOpacity) {
+ if (!isText() && (!hasLayer() || !toRenderBoxModelObject(this)->layer()->isComposited()))
+ diff = StyleDifferenceRepaintLayer;
+ else if (diff < StyleDifferenceRecompositeLayer)
+ diff = StyleDifferenceRecompositeLayer;
+ }
+
+ // The answer to requiresLayer() for plugins and iframes can change outside of the style system,
+ // since it depends on whether we decide to composite these elements. When the layer status of
+ // one of these elements changes, we need to force a layout.
+ if (diff == StyleDifferenceEqual && style() && isBoxModelObject()) {
+ if (hasLayer() != toRenderBoxModelObject(this)->requiresLayer())
+ diff = StyleDifferenceLayout;
+ }
+#else
+ UNUSED_PARAM(contextSensitiveProperties);
+#endif
+
+ // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint.
+ if (diff == StyleDifferenceRepaintLayer && !hasLayer())
+ diff = StyleDifferenceRepaint;
+
+ return diff;
+}
+
+void RenderObject::setStyle(PassRefPtr<RenderStyle> style)
+{
+ if (m_style == style) {
+#if USE(ACCELERATED_COMPOSITING)
+ // We need to run through adjustStyleDifference() for iframes and plugins, so
+ // style sharing is disabled for them. That should ensure that we never hit this code path.
+ ASSERT(!isRenderIFrame() && !isEmbeddedObject() &&!isApplet());
+#endif
+ return;
+ }
+
+ StyleDifference diff = StyleDifferenceEqual;
+ unsigned contextSensitiveProperties = ContextSensitivePropertyNone;
+ if (m_style)
+ diff = m_style->diff(style.get(), contextSensitiveProperties);
+
+ diff = adjustStyleDifference(diff, contextSensitiveProperties);
+
+ styleWillChange(diff, style.get());
+
+ RefPtr<RenderStyle> oldStyle = m_style.release();
+ m_style = style;
+
+ updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0);
+ updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0);
+
+ updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0);
+ updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0);
+
+ // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen
+ // during styleDidChange (it's used by clippedOverflowRectForRepaint()).
+ if (m_style->outlineWidth() > 0 && m_style->outlineSize() > maximalOutlineSize(PaintPhaseOutline))
+ toRenderView(document()->renderer())->setMaximalOutlineSize(m_style->outlineSize());
+
+ styleDidChange(diff, oldStyle.get());
+
+ if (!m_parent || isText())
+ return;
+
+ // Now that the layer (if any) has been updated, we need to adjust the diff again,
+ // check whether we should layout now, and decide if we need to repaint.
+ StyleDifference updatedDiff = adjustStyleDifference(diff, contextSensitiveProperties);
+
+ if (diff <= StyleDifferenceLayoutPositionedMovementOnly) {
+ if (updatedDiff == StyleDifferenceLayout)
+ setNeedsLayoutAndPrefWidthsRecalc();
+ else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly)
+ setNeedsPositionedMovementLayout();
+ }
+
+ if (updatedDiff == StyleDifferenceRepaintLayer || updatedDiff == StyleDifferenceRepaint) {
+ // Do a repaint with the new style now, e.g., for example if we go from
+ // not having an outline to having an outline.
+ repaint();
+ }
+}
+
+void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style)
+{
+ m_style = style;
+}
+
+void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (m_style) {
+ // If our z-index changes value or our visibility changes,
+ // we need to dirty our stacking context's z-order list.
+ if (newStyle) {
+ bool visibilityChanged = m_style->visibility() != newStyle->visibility()
+ || m_style->zIndex() != newStyle->zIndex()
+ || m_style->hasAutoZIndex() != newStyle->hasAutoZIndex();
+#if ENABLE(DASHBOARD_SUPPORT)
+ if (visibilityChanged)
+ document()->setDashboardRegionsDirty(true);
+#endif
+ if (visibilityChanged && AXObjectCache::accessibilityEnabled())
+ document()->axObjectCache()->childrenChanged(this);
+
+ // Keep layer hierarchy visibility bits up to date if visibility changes.
+ if (m_style->visibility() != newStyle->visibility()) {
+ if (RenderLayer* l = enclosingLayer()) {
+ if (newStyle->visibility() == VISIBLE)
+ l->setHasVisibleContent(true);
+ else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) {
+ l->dirtyVisibleContentStatus();
+ if (diff > StyleDifferenceRepaintLayer)
+ repaint();
+ }
+ }
+ }
+ }
+
+ if (m_parent && (diff == StyleDifferenceRepaint || newStyle->outlineSize() < m_style->outlineSize()))
+ repaint();
+ if (isFloating() && (m_style->floating() != newStyle->floating()))
+ // For changes in float styles, we need to conceivably remove ourselves
+ // from the floating objects list.
+ toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists();
+ else if (isPositioned() && (m_style->position() != newStyle->position()))
+ // For changes in positioning styles, we need to conceivably remove ourselves
+ // from the positioned objects list.
+ toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists();
+
+ s_affectsParentBlock = isFloatingOrPositioned() &&
+ (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)
+ && parent() && (parent()->isBlockFlow() || parent()->isRenderInline());
+
+ // reset style flags
+ if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) {
+ m_floating = false;
+ m_positioned = false;
+ m_relPositioned = false;
+ }
+ m_paintBackground = false;
+ m_hasOverflowClip = false;
+ m_hasTransform = false;
+ m_hasReflection = false;
+ } else
+ s_affectsParentBlock = false;
+
+ if (view()->frameView()) {
+ bool shouldBlitOnFixedBackgroundImage = false;
+#if ENABLE(FAST_MOBILE_SCROLLING)
+ // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays
+ // when scrolling a page with a fixed background image. As an optimization, assuming there are
+ // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we
+ // ignore the CSS property "background-attachment: fixed".
+ shouldBlitOnFixedBackgroundImage = true;
+#endif
+
+ bool newStyleSlowScroll = newStyle && !shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage();
+ bool oldStyleSlowScroll = m_style && !shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage();
+ if (oldStyleSlowScroll != newStyleSlowScroll) {
+ if (oldStyleSlowScroll)
+ view()->frameView()->removeSlowRepaintObject();
+ if (newStyleSlowScroll)
+ view()->frameView()->addSlowRepaintObject();
+ }
+ }
+}
+
+void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ if (s_affectsParentBlock)
+ handleDynamicFloatPositionChange();
+
+ if (!m_parent)
+ return;
+
+ if (diff == StyleDifferenceLayout) {
+ RenderCounter::rendererStyleChanged(this, oldStyle, m_style.get());
+
+ // If the object already needs layout, then setNeedsLayout won't do
+ // any work. But if the containing block has changed, then we may need
+ // to mark the new containing blocks for layout. The change that can
+ // directly affect the containing block of this object is a change to
+ // the position style.
+ if (m_needsLayout && oldStyle->position() != m_style->position())
+ markContainingBlocksForLayout();
+
+ setNeedsLayoutAndPrefWidthsRecalc();
+ } else if (diff == StyleDifferenceLayoutPositionedMovementOnly)
+ setNeedsPositionedMovementLayout();
+
+ // Don't check for repaint here; we need to wait until the layer has been
+ // updated by subclasses before we know if we have to repaint (in setStyle()).
+}
+
+void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers)
+{
+ // Optimize the common case
+ if (oldLayers && !oldLayers->next() && newLayers && !newLayers->next() && (oldLayers->image() == newLayers->image()))
+ return;
+
+ // Go through the new layers and addClients first, to avoid removing all clients of an image.
+ for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) {
+ if (currNew->image())
+ currNew->image()->addClient(this);
+ }
+
+ for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) {
+ if (currOld->image())
+ currOld->image()->removeClient(this);
+ }
+}
+
+void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage)
+{
+ if (oldImage != newImage) {
+ if (oldImage)
+ oldImage->removeClient(this);
+ if (newImage)
+ newImage->addClient(this);
+ }
+}
+
+IntRect RenderObject::viewRect() const
+{
+ return view()->viewRect();
+}
+
+FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const
+{
+ TransformState transformState(TransformState::ApplyTransformDirection, localPoint);
+ mapLocalToContainer(0, fixed, useTransforms, transformState);
+ transformState.flatten();
+
+ return transformState.lastPlanarPoint();
+}
+
+FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const
+{
+ TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint);
+ mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
+ transformState.flatten();
+
+ return transformState.lastPlanarPoint();
+}
+
+void RenderObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
+{
+ if (repaintContainer == this)
+ return;
+
+ RenderObject* o = parent();
+ if (!o)
+ return;
+
+ IntSize columnOffset;
+ o->adjustForColumns(columnOffset, roundedIntPoint(transformState.mappedPoint()));
+ if (!columnOffset.isZero())
+ transformState.move(columnOffset);
+
+ if (o->hasOverflowClip())
+ transformState.move(-toRenderBox(o)->layer()->scrolledContentOffset());
+
+ o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
+}
+
+void RenderObject::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
+{
+ RenderObject* o = parent();
+ if (o) {
+ o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
+ if (o->hasOverflowClip())
+ transformState.move(toRenderBox(o)->layer()->scrolledContentOffset());
+ }
+}
+
+bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const
+{
+#if ENABLE(3D_RENDERING)
+ // hasTransform() indicates whether the object has transform, transform-style or perspective. We just care about transform,
+ // so check the layer's transform directly.
+ return (hasLayer() && toRenderBoxModelObject(this)->layer()->transform()) || (containerObject && containerObject->style()->hasPerspective());
+#else
+ UNUSED_PARAM(containerObject);
+ return hasTransform();
+#endif
+}
+
+void RenderObject::getTransformFromContainer(const RenderObject* containerObject, const IntSize& offsetInContainer, TransformationMatrix& transform) const
+{
+ transform.makeIdentity();
+ transform.translate(offsetInContainer.width(), offsetInContainer.height());
+ RenderLayer* layer;
+ if (hasLayer() && (layer = toRenderBoxModelObject(this)->layer()) && layer->transform())
+ transform.multLeft(layer->currentTransform());
+
+#if ENABLE(3D_RENDERING)
+ if (containerObject && containerObject->hasLayer() && containerObject->style()->hasPerspective()) {
+ // Perpsective on the container affects us, so we have to factor it in here.
+ ASSERT(containerObject->hasLayer());
+ FloatPoint perspectiveOrigin = toRenderBoxModelObject(containerObject)->layer()->perspectiveOrigin();
+
+ TransformationMatrix perspectiveMatrix;
+ perspectiveMatrix.applyPerspective(containerObject->style()->perspective());
+
+ transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0);
+ transform.multiply(perspectiveMatrix);
+ transform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0);
+ }
+#else
+ UNUSED_PARAM(containerObject);
+#endif
+}
+
+FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, RenderBoxModelObject* repaintContainer, bool fixed) const
+{
+ // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(),
+ // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks.
+ TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), &localQuad);
+ mapLocalToContainer(repaintContainer, fixed, true, transformState);
+ transformState.flatten();
+
+ return transformState.lastPlanarQuad();
+}
+
+IntSize RenderObject::offsetFromContainer(RenderObject* o, const IntPoint& point) const
+{
+ ASSERT(o == container());
+
+ IntSize offset;
+
+ o->adjustForColumns(offset, point);
+
+ if (o->hasOverflowClip())
+ offset -= toRenderBox(o)->layer()->scrolledContentOffset();
+
+ return offset;
+}
+
+IntSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const
+{
+ IntSize offset;
+ IntPoint referencePoint;
+ const RenderObject* currContainer = this;
+ do {
+ RenderObject* nextContainer = currContainer->container();
+ ASSERT(nextContainer); // This means we reached the top without finding container.
+ if (!nextContainer)
+ break;
+ ASSERT(!currContainer->hasTransform());
+ IntSize currentOffset = currContainer->offsetFromContainer(nextContainer, referencePoint);
+ offset += currentOffset;
+ referencePoint.move(currentOffset);
+ currContainer = nextContainer;
+ } while (currContainer != container);
+
+ return offset;
+}
+
+IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine)
+{
+ if (extraWidthToEndOfLine)
+ *extraWidthToEndOfLine = 0;
+
+ return IntRect();
+}
+
+RenderView* RenderObject::view() const
+{
+ return toRenderView(document()->renderer());
+}
+
+bool RenderObject::isRooted(RenderView** view)
+{
+ RenderObject* o = this;
+ while (o->parent())
+ o = o->parent();
+
+ if (!o->isRenderView())
+ return false;
+
+ if (view)
+ *view = toRenderView(o);
+
+ return true;
+}
+
+bool RenderObject::hasOutlineAnnotation() const
+{
+ return node() && node()->isLink() && document()->printing();
+}
+
+RenderObject* RenderObject::container(RenderBoxModelObject* repaintContainer, bool* repaintContainerSkipped) const
+{
+ if (repaintContainerSkipped)
+ *repaintContainerSkipped = false;
+
+ // This method is extremely similar to containingBlock(), but with a few notable
+ // exceptions.
+ // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when
+ // the object is not part of the primary document subtree yet.
+ // (2) For normal flow elements, it just returns the parent.
+ // (3) For absolute positioned elements, it will return a relative positioned inline.
+ // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle
+ // the layout of the positioned object. This does mean that computePositionedLogicalWidth and
+ // computePositionedLogicalHeight have to use container().
+ RenderObject* o = parent();
+
+ if (isText())
+ return o;
+
+ EPosition pos = m_style->position();
+ if (pos == FixedPosition) {
+ // container() can be called on an object that is not in the
+ // tree yet. We don't call view() since it will assert if it
+ // can't get back to the canvas. Instead we just walk as high up
+ // as we can. If we're in the tree, we'll get the root. If we
+ // aren't we'll get the root of our little subtree (most likely
+ // we'll just return 0).
+ // FIXME: The definition of view() has changed to not crawl up the render tree. It might
+ // be safe now to use it.
+ while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) {
+ if (repaintContainerSkipped && o == repaintContainer)
+ *repaintContainerSkipped = true;
+ o = o->parent();
+ }
+ } else if (pos == AbsolutePosition) {
+ // Same goes here. We technically just want our containing block, but
+ // we may not have one if we're part of an uninstalled subtree. We'll
+ // climb as high as we can though.
+ while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) {
+ if (repaintContainerSkipped && o == repaintContainer)
+ *repaintContainerSkipped = true;
+ o = o->parent();
+ }
+ }
+
+ return o;
+}
+
+bool RenderObject::isSelectionBorder() const
+{
+ SelectionState st = selectionState();
+ return st == SelectionStart || st == SelectionEnd || st == SelectionBoth;
+}
+
+void RenderObject::destroy()
+{
+ // Destroy any leftover anonymous children.
+ RenderObjectChildList* children = virtualChildren();
+ if (children)
+ children->destroyLeftoverChildren();
+
+ // If this renderer is being autoscrolled, stop the autoscroll timer
+
+ // FIXME: RenderObject::destroy should not get called with a renderer whose document
+ // has a null frame, so we assert this. However, we don't want release builds to crash which is why we
+ // check that the frame is not null.
+ ASSERT(frame());
+ if (frame() && frame()->eventHandler()->autoscrollRenderer() == this)
+ frame()->eventHandler()->stopAutoscrollTimer(true);
+
+ if (m_hasCounterNodeMap)
+ RenderCounter::destroyCounterNodes(this);
+
+ if (AXObjectCache::accessibilityEnabled()) {
+ document()->axObjectCache()->childrenChanged(this->parent());
+ document()->axObjectCache()->remove(this);
+ }
+ animation()->cancelAnimations(this);
+
+ // By default no ref-counting. RenderWidget::destroy() doesn't call
+ // this function because it needs to do ref-counting. If anything
+ // in this function changes, be sure to fix RenderWidget::destroy() as well.
+
+ remove();
+
+ // FIXME: Would like to do this in RenderBoxModelObject, but the timing is so complicated that this can't easily
+ // be moved into RenderBoxModelObject::destroy.
+ if (hasLayer()) {
+ setHasLayer(false);
+ toRenderBoxModelObject(this)->destroyLayer();
+ }
+ arenaDelete(renderArena(), this);
+}
+
+void RenderObject::arenaDelete(RenderArena* arena, void* base)
+{
+ if (m_style) {
+ for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) {
+ if (StyleImage* backgroundImage = bgLayer->image())
+ backgroundImage->removeClient(this);
+ }
+
+ for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
+ if (StyleImage* maskImage = maskLayer->image())
+ maskImage->removeClient(this);
+ }
+
+ if (StyleImage* borderImage = m_style->borderImage().image())
+ borderImage->removeClient(this);
+
+ if (StyleImage* maskBoxImage = m_style->maskBoxImage().image())
+ maskBoxImage->removeClient(this);
+ }
+
+#ifndef NDEBUG
+ void* savedBase = baseOfRenderObjectBeingDeleted;
+ baseOfRenderObjectBeingDeleted = base;
+#endif
+ delete this;
+#ifndef NDEBUG
+ baseOfRenderObjectBeingDeleted = savedBase;
+#endif
+
+ // Recover the size left there for us by operator delete and free the memory.
+ arena->free(*(size_t*)base, base);
+}
+
+VisiblePosition RenderObject::positionForCoordinates(int x, int y)
+{
+ return positionForPoint(IntPoint(x, y));
+}
+
+VisiblePosition RenderObject::positionForPoint(const IntPoint&)
+{
+ return createVisiblePosition(caretMinOffset(), DOWNSTREAM);
+}
+
+void RenderObject::updateDragState(bool dragOn)
+{
+ bool valueChanged = (dragOn != m_isDragging);
+ m_isDragging = dragOn;
+ if (valueChanged && style()->affectedByDragRules() && node())
+ node()->setNeedsStyleRecalc();
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->updateDragState(dragOn);
+}
+
+bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter)
+{
+ bool inside = false;
+ if (hitTestFilter != HitTestSelf) {
+ // First test the foreground layer (lines and inlines).
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestForeground);
+
+ // Test floats next.
+ if (!inside)
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestFloat);
+
+ // Finally test to see if the mouse is in the background (within a child block's background).
+ if (!inside)
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestChildBlockBackgrounds);
+ }
+
+ // See if the mouse is inside us but not any of our descendants
+ if (hitTestFilter != HitTestDescendants && !inside)
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestBlockBackground);
+
+ return inside;
+}
+
+void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& point)
+{
+ if (result.innerNode())
+ return;
+
+ Node* n = node();
+ if (n) {
+ result.setInnerNode(n);
+ if (!result.innerNonSharedNode())
+ result.setInnerNonSharedNode(n);
+ result.setLocalPoint(point);
+ }
+}
+
+bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, int /*y*/, int /*tx*/, int /*ty*/, HitTestAction)
+{
+ return false;
+}
+
+void RenderObject::scheduleRelayout()
+{
+ if (isRenderView()) {
+ FrameView* view = toRenderView(this)->frameView();
+ if (view)
+ view->scheduleRelayout();
+ } else if (parent()) {
+ FrameView* v = view() ? view()->frameView() : 0;
+ if (v)
+ v->scheduleRelayoutOfSubtree(this);
+ }
+}
+
+void RenderObject::layout()
+{
+ ASSERT(needsLayout());
+ RenderObject* child = firstChild();
+ while (child) {
+ child->layoutIfNeeded();
+ ASSERT(!child->needsLayout());
+ child = child->nextSibling();
+ }
+ setNeedsLayout(false);
+}
+
+PassRefPtr<RenderStyle> RenderObject::uncachedFirstLineStyle(RenderStyle* style) const
+{
+ if (!document()->usesFirstLineRules())
+ return 0;
+
+ ASSERT(!isText());
+
+ RefPtr<RenderStyle> result;
+
+ if (isBlockFlow()) {
+ if (RenderBlock* firstLineBlock = this->firstLineBlock())
+ result = firstLineBlock->getUncachedPseudoStyle(FIRST_LINE, style, firstLineBlock == this ? style : 0);
+ } else if (!isAnonymous() && isRenderInline()) {
+ RenderStyle* parentStyle = parent()->firstLineStyle();
+ if (parentStyle != parent()->style())
+ result = getUncachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle, style);
+ }
+
+ return result.release();
+}
+
+RenderStyle* RenderObject::firstLineStyleSlowCase() const
+{
+ ASSERT(document()->usesFirstLineRules());
+
+ RenderStyle* style = m_style.get();
+ const RenderObject* renderer = isText() ? parent() : this;
+ if (renderer->isBlockFlow()) {
+ if (RenderBlock* firstLineBlock = renderer->firstLineBlock())
+ style = firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style);
+ } else if (!renderer->isAnonymous() && renderer->isRenderInline()) {
+ RenderStyle* parentStyle = renderer->parent()->firstLineStyle();
+ if (parentStyle != renderer->parent()->style()) {
+ // A first-line style is in effect. Cache a first-line style for ourselves.
+ renderer->style()->setHasPseudoStyle(FIRST_LINE_INHERITED);
+ style = renderer->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle);
+ }
+ }
+
+ return style;
+}
+
+RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const
+{
+ if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo))
+ return 0;
+
+ RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo);
+ if (cachedStyle)
+ return cachedStyle;
+
+ RefPtr<RenderStyle> result = getUncachedPseudoStyle(pseudo, parentStyle);
+ if (result)
+ return style()->addCachedPseudoStyle(result.release());
+ return 0;
+}
+
+PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle, RenderStyle* ownStyle) const
+{
+ if (pseudo < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style()->hasPseudoStyle(pseudo))
+ return 0;
+
+ if (!parentStyle) {
+ ASSERT(!ownStyle);
+ parentStyle = style();
+ }
+
+ Node* n = node();
+ while (n && !n->isElementNode())
+ n = n->parentNode();
+ if (!n)
+ return 0;
+
+ RefPtr<RenderStyle> result;
+ if (pseudo == FIRST_LINE_INHERITED) {
+ result = document()->styleSelector()->styleForElement(static_cast<Element*>(n), parentStyle, false);
+ result->setStyleType(FIRST_LINE_INHERITED);
+ } else
+ result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(n), parentStyle);
+ return result.release();
+}
+
+static Color decorationColor(RenderObject* renderer)
+{
+ Color result;
+ if (renderer->style()->textStrokeWidth() > 0) {
+ // Prefer stroke color if possible but not if it's fully transparent.
+ result = renderer->style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
+ if (result.alpha())
+ return result;
+ }
+
+ result = renderer->style()->visitedDependentColor(CSSPropertyWebkitTextFillColor);
+ return result;
+}
+
+void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline,
+ Color& linethrough, bool quirksMode)
+{
+ RenderObject* curr = this;
+ do {
+ int currDecs = curr->style()->textDecoration();
+ if (currDecs) {
+ if (currDecs & UNDERLINE) {
+ decorations &= ~UNDERLINE;
+ underline = decorationColor(curr);
+ }
+ if (currDecs & OVERLINE) {
+ decorations &= ~OVERLINE;
+ overline = decorationColor(curr);
+ }
+ if (currDecs & LINE_THROUGH) {
+ decorations &= ~LINE_THROUGH;
+ linethrough = decorationColor(curr);
+ }
+ }
+ curr = curr->parent();
+ if (curr && curr->isAnonymousBlock() && toRenderBlock(curr)->continuation())
+ curr = toRenderBlock(curr)->continuation();
+ } while (curr && decorations && (!quirksMode || !curr->node() ||
+ (!curr->node()->hasTagName(aTag) && !curr->node()->hasTagName(fontTag))));
+
+ // If we bailed out, use the element we bailed out at (typically a <font> or <a> element).
+ if (decorations && curr) {
+ if (decorations & UNDERLINE)
+ underline = decorationColor(curr);
+ if (decorations & OVERLINE)
+ overline = decorationColor(curr);
+ if (decorations & LINE_THROUGH)
+ linethrough = decorationColor(curr);
+ }
+}
+
+#if ENABLE(DASHBOARD_SUPPORT)
+void RenderObject::addDashboardRegions(Vector<DashboardRegionValue>& regions)
+{
+ // Convert the style regions to absolute coordinates.
+ if (style()->visibility() != VISIBLE || !isBox())
+ return;
+
+ RenderBox* box = toRenderBox(this);
+
+ const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions();
+ unsigned i, count = styleRegions.size();
+ for (i = 0; i < count; i++) {
+ StyleDashboardRegion styleRegion = styleRegions[i];
+
+ int w = box->width();
+ int h = box->height();
+
+ DashboardRegionValue region;
+ region.label = styleRegion.label;
+ region.bounds = IntRect(styleRegion.offset.left().value(),
+ 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;
+
+ region.clip = region.bounds;
+ computeAbsoluteRepaintRect(region.clip);
+ if (region.clip.height() < 0) {
+ region.clip.setHeight(0);
+ region.clip.setWidth(0);
+ }
+
+ FloatPoint absPos = localToAbsolute();
+ region.bounds.setX(absPos.x() + styleRegion.offset.left().value());
+ region.bounds.setY(absPos.y() + styleRegion.offset.top().value());
+
+ if (frame()) {
+ float pageScaleFactor = frame()->page()->chrome()->scaleFactor();
+ if (pageScaleFactor != 1.0f) {
+ region.bounds.scale(pageScaleFactor);
+ region.clip.scale(pageScaleFactor);
+ }
+ }
+
+ regions.append(region);
+ }
+}
+
+void RenderObject::collectDashboardRegions(Vector<DashboardRegionValue>& regions)
+{
+ // RenderTexts don't have their own style, they just use their parent's style,
+ // so we don't want to include them.
+ if (isText())
+ return;
+
+ addDashboardRegions(regions);
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
+ curr->collectDashboardRegions(regions);
+}
+#endif
+
+bool RenderObject::willRenderImage(CachedImage*)
+{
+ // Without visibility we won't render (and therefore don't care about animation).
+ if (style()->visibility() != VISIBLE)
+ return false;
+
+ // If we're not in a window (i.e., we're dormant from being put in the b/f cache or in a background tab)
+ // then we don't want to render either.
+ return !document()->inPageCache() && !document()->view()->isOffscreen();
+}
+
+int RenderObject::maximalOutlineSize(PaintPhase p) const
+{
+ if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines)
+ return 0;
+ return toRenderView(document()->renderer())->maximalOutlineSize();
+}
+
+int RenderObject::caretMinOffset() const
+{
+ return 0;
+}
+
+int RenderObject::caretMaxOffset() const
+{
+ if (isReplaced())
+ return node() ? max(1U, node()->childNodeCount()) : 1;
+ if (isHR())
+ return 1;
+ return 0;
+}
+
+unsigned RenderObject::caretMaxRenderedOffset() const
+{
+ return 0;
+}
+
+int RenderObject::previousOffset(int current) const
+{
+ return current - 1;
+}
+
+int RenderObject::previousOffsetForBackwardDeletion(int current) const
+{
+ return current - 1;
+}
+
+int RenderObject::nextOffset(int current) const
+{
+ return current + 1;
+}
+
+void RenderObject::adjustRectForOutlineAndShadow(IntRect& rect) const
+{
+ int outlineSize = outlineStyleForRepaint()->outlineSize();
+ if (const ShadowData* boxShadow = style()->boxShadow()) {
+ boxShadow->adjustRectForShadow(rect, outlineSize);
+ return;
+ }
+
+ rect.inflate(outlineSize);
+}
+
+AnimationController* RenderObject::animation() const
+{
+ return frame()->animation();
+}
+
+void RenderObject::imageChanged(CachedImage* image, const IntRect* rect)
+{
+ imageChanged(static_cast<WrappedImagePtr>(image), rect);
+}
+
+RenderBoxModelObject* RenderObject::offsetParent() const
+{
+ // If any of the following holds true return null and stop this algorithm:
+ // A is the root element.
+ // A is the HTML body element.
+ // The computed value of the position property for element A is fixed.
+ if (isRoot() || isBody() || (isPositioned() && style()->position() == FixedPosition))
+ return 0;
+
+ // If A is an area HTML element which has a map HTML element somewhere in the ancestor
+ // chain return the nearest ancestor map HTML element and stop this algorithm.
+ // FIXME: Implement!
+
+ // Return the nearest ancestor element of A for which at least one of the following is
+ // true and stop this algorithm if such an ancestor is found:
+ // * The computed value of the position property is not static.
+ // * It is the HTML body element.
+ // * The computed value of the position property of A is static and the ancestor
+ // is one of the following HTML elements: td, th, or table.
+ // * Our own extension: if there is a difference in the effective zoom
+ bool skipTables = isPositioned() || isRelPositioned();
+ float currZoom = style()->effectiveZoom();
+ RenderObject* curr = parent();
+ while (curr && (!curr->node() ||
+ (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) {
+ Node* element = curr->node();
+ if (!skipTables && element) {
+ bool isTableElement = element->hasTagName(tableTag) ||
+ element->hasTagName(tdTag) ||
+ element->hasTagName(thTag);
+
+#if ENABLE(WML)
+ if (!isTableElement && element->isWMLElement())
+ isTableElement = element->hasTagName(WMLNames::tableTag) ||
+ element->hasTagName(WMLNames::tdTag);
+#endif
+
+ if (isTableElement)
+ break;
+ }
+
+ float newZoom = curr->style()->effectiveZoom();
+ if (currZoom != newZoom)
+ break;
+ currZoom = newZoom;
+ curr = curr->parent();
+ }
+ return curr && curr->isBoxModelObject() ? toRenderBoxModelObject(curr) : 0;
+}
+
+VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity)
+{
+ // If this is a non-anonymous renderer in an editable area, then it's simple.
+ if (Node* node = this->node()) {
+ if (!node->isContentEditable()) {
+ // If it can be found, we prefer a visually equivalent position that is editable.
+ Position position(node, offset);
+ Position candidate = position.downstream(CanCrossEditingBoundary);
+ if (candidate.node()->isContentEditable())
+ return VisiblePosition(candidate, affinity);
+ candidate = position.upstream(CanCrossEditingBoundary);
+ if (candidate.node()->isContentEditable())
+ return VisiblePosition(candidate, affinity);
+ }
+ return VisiblePosition(node, offset, affinity);
+ }
+
+ // We don't want to cross the boundary between editable and non-editable
+ // regions of the document, but that is either impossible or at least
+ // extremely unlikely in any normal case because we stop as soon as we
+ // find a single non-anonymous renderer.
+
+ // Find a nearby non-anonymous renderer.
+ RenderObject* child = this;
+ while (RenderObject* parent = child->parent()) {
+ // Find non-anonymous content after.
+ RenderObject* renderer = child;
+ while ((renderer = renderer->nextInPreOrder(parent))) {
+ if (Node* node = renderer->node())
+ return VisiblePosition(node, 0, DOWNSTREAM);
+ }
+
+ // Find non-anonymous content before.
+ renderer = child;
+ while ((renderer = renderer->previousInPreOrder())) {
+ if (renderer == parent)
+ break;
+ if (Node* node = renderer->node())
+ return VisiblePosition(lastDeepEditingPositionForNode(node), DOWNSTREAM);
+ }
+
+ // Use the parent itself unless it too is anonymous.
+ if (Node* node = parent->node())
+ return VisiblePosition(node, 0, DOWNSTREAM);
+
+ // Repeat at the next level up.
+ child = parent;
+ }
+
+ // Everything was anonymous. Give up.
+ return VisiblePosition();
+}
+
+VisiblePosition RenderObject::createVisiblePosition(const Position& position)
+{
+ if (position.isNotNull())
+ return VisiblePosition(position);
+
+ ASSERT(!node());
+ return createVisiblePosition(0, DOWNSTREAM);
+}
+
+#if ENABLE(SVG)
+RenderSVGResourceContainer* RenderObject::toRenderSVGResourceContainer()
+{
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void RenderObject::setNeedsBoundariesUpdate()
+{
+ if (RenderObject* renderer = parent())
+ renderer->setNeedsBoundariesUpdate();
+}
+
+FloatRect RenderObject::objectBoundingBox() const
+{
+ ASSERT_NOT_REACHED();
+ return FloatRect();
+}
+
+FloatRect RenderObject::strokeBoundingBox() const
+{
+ ASSERT_NOT_REACHED();
+ return FloatRect();
+}
+
+// Returns the smallest rectangle enclosing all of the painted content
+// respecting clipping, masking, filters, opacity, stroke-width and markers
+FloatRect RenderObject::repaintRectInLocalCoordinates() const
+{
+ ASSERT_NOT_REACHED();
+ return FloatRect();
+}
+
+AffineTransform RenderObject::localTransform() const
+{
+ static const AffineTransform identity;
+ return identity;
+}
+
+const AffineTransform& RenderObject::localToParentTransform() const
+{
+ static const AffineTransform identity;
+ return identity;
+}
+
+bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction)
+{
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+#endif // ENABLE(SVG)
+
+} // namespace WebCore
+
+#ifndef NDEBUG
+
+void showTree(const WebCore::RenderObject* ro)
+{
+ if (ro)
+ ro->showTreeForThis();
+}
+
+void showRenderTree(const WebCore::RenderObject* object1)
+{
+ showRenderTree(object1, 0);
+}
+
+void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2)
+{
+ if (object1) {
+ const WebCore::RenderObject* root = object1;
+ while (root->parent())
+ root = root->parent();
+ root->showRenderTreeAndMark(object1, "*", object2, "-", 0);
+ }
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderObject.h b/Source/WebCore/rendering/RenderObject.h
new file mode 100644
index 0000000..2f443ef
--- /dev/null
+++ b/Source/WebCore/rendering/RenderObject.h
@@ -0,0 +1,1050 @@
+/*
+ * 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 RenderObject_h
+#define RenderObject_h
+
+#include "AffineTransform.h"
+#include "CachedResourceClient.h"
+#include "CSSPrimitiveValue.h"
+#include "Document.h"
+#include "Element.h"
+#include "FloatQuad.h"
+#include "PaintInfo.h"
+#include "RenderObjectChildList.h"
+#include "RenderStyle.h"
+#include "TextAffinity.h"
+#include "TransformationMatrix.h"
+#include <wtf/UnusedParam.h>
+
+#if PLATFORM(CG) || PLATFORM(CAIRO) || PLATFORM(QT)
+#define HAVE_PATH_BASED_BORDER_RADIUS_DRAWING 1
+#endif
+
+namespace WebCore {
+
+class AnimationController;
+class HitTestResult;
+class InlineBox;
+class InlineFlowBox;
+class OverlapTestRequestClient;
+class Path;
+class Position;
+class RenderBoxModelObject;
+class RenderInline;
+class RenderBlock;
+class RenderFlow;
+class RenderLayer;
+class RenderTheme;
+class TransformState;
+class VisiblePosition;
+#if ENABLE(SVG)
+class RenderSVGResourceContainer;
+#endif
+
+enum HitTestFilter {
+ HitTestAll,
+ HitTestSelf,
+ HitTestDescendants
+};
+
+enum HitTestAction {
+ HitTestBlockBackground,
+ HitTestChildBlockBackground,
+ HitTestChildBlockBackgrounds,
+ HitTestFloat,
+ HitTestForeground
+};
+
+// Sides used when drawing borders and outlines. This is in RenderObject rather than RenderBoxModelObject since outlines can
+// be drawn by SVG around bounding boxes.
+enum BoxSide {
+ BSTop,
+ BSBottom,
+ BSLeft,
+ BSRight
+};
+
+const int caretWidth = 1;
+
+#if ENABLE(DASHBOARD_SUPPORT)
+struct DashboardRegionValue {
+ bool operator==(const DashboardRegionValue& o) const
+ {
+ return type == o.type && bounds == o.bounds && clip == o.clip && label == o.label;
+ }
+ bool operator!=(const DashboardRegionValue& o) const
+ {
+ return !(*this == o);
+ }
+
+ String label;
+ IntRect bounds;
+ IntRect clip;
+ int type;
+};
+#endif
+
+// Base class for all rendering tree objects.
+class RenderObject : public CachedResourceClient {
+ friend class RenderBlock;
+ friend class RenderBox;
+ friend class RenderLayer;
+ friend class RenderObjectChildList;
+ friend class RenderSVGContainer;
+public:
+ // Anonymous objects should pass the document as their node, and they will then automatically be
+ // marked as anonymous in the constructor.
+ RenderObject(Node*);
+ virtual ~RenderObject();
+
+ RenderTheme* theme() const;
+
+ virtual const char* renderName() const = 0;
+
+ RenderObject* parent() const { return m_parent; }
+ bool isDescendantOf(const RenderObject*) const;
+
+ RenderObject* previousSibling() const { return m_previous; }
+ RenderObject* nextSibling() const { return m_next; }
+
+ RenderObject* firstChild() const
+ {
+ if (const RenderObjectChildList* children = virtualChildren())
+ return children->firstChild();
+ return 0;
+ }
+ RenderObject* lastChild() const
+ {
+ if (const RenderObjectChildList* children = virtualChildren())
+ return children->lastChild();
+ return 0;
+ }
+ virtual RenderObjectChildList* virtualChildren() { return 0; }
+ virtual const RenderObjectChildList* virtualChildren() const { return 0; }
+
+ RenderObject* nextInPreOrder() const;
+ RenderObject* nextInPreOrder(RenderObject* stayWithin) const;
+ RenderObject* nextInPreOrderAfterChildren() const;
+ RenderObject* nextInPreOrderAfterChildren(RenderObject* stayWithin) const;
+ RenderObject* previousInPreOrder() const;
+ RenderObject* childAt(unsigned) const;
+
+ RenderObject* firstLeafChild() const;
+ RenderObject* lastLeafChild() const;
+
+ // The following six functions are used when the render tree hierarchy changes to make sure layers get
+ // properly added and removed. Since containership can be implemented by any subclass, and since a hierarchy
+ // can contain a mixture of boxes and other object types, these functions need to be in the base class.
+ RenderLayer* enclosingLayer() const;
+ void addLayers(RenderLayer* parentLayer, RenderObject* newObject);
+ void removeLayers(RenderLayer* parentLayer);
+ void moveLayers(RenderLayer* oldParent, RenderLayer* newParent);
+ RenderLayer* findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, bool checkParent = true);
+
+ // Convenience function for getting to the nearest enclosing box of a RenderObject.
+ RenderBox* enclosingBox() const;
+ RenderBoxModelObject* enclosingBoxModelObject() const;
+
+ virtual bool isEmpty() const { return firstChild() == 0; }
+
+#ifndef NDEBUG
+ void setHasAXObject(bool flag) { m_hasAXObject = flag; }
+ bool hasAXObject() const { return m_hasAXObject; }
+ bool isSetNeedsLayoutForbidden() const { return m_setNeedsLayoutForbidden; }
+ void setNeedsLayoutIsForbidden(bool flag) { m_setNeedsLayoutForbidden = flag; }
+#endif
+
+ // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline
+ // children.
+ virtual RenderBlock* firstLineBlock() const;
+
+ // Called when an object that was floating or positioned becomes a normal flow object
+ // again. We have to make sure the render tree updates as needed to accommodate the new
+ // normal flow object.
+ void handleDynamicFloatPositionChange();
+
+ // RenderObject tree manipulation
+ //////////////////////////////////////////
+ virtual bool canHaveChildren() const { return virtualChildren(); }
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { return true; }
+ virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0);
+ virtual void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild = 0) { return addChild(newChild, beforeChild); }
+ virtual void removeChild(RenderObject*);
+ virtual bool createsAnonymousWrapper() const { return false; }
+ //////////////////////////////////////////
+
+protected:
+ //////////////////////////////////////////
+ // Helper functions. Dangerous to use!
+ void setPreviousSibling(RenderObject* previous) { m_previous = previous; }
+ void setNextSibling(RenderObject* next) { m_next = next; }
+ void setParent(RenderObject* parent) { m_parent = parent; }
+ //////////////////////////////////////////
+private:
+ void addAbsoluteRectForLayer(IntRect& result);
+ void setLayerNeedsFullRepaint();
+
+public:
+#ifndef NDEBUG
+ void showTreeForThis() const;
+
+ void showRenderObject() const;
+ // We don't make printedCharacters an optional parameter so that
+ // showRenderObject can be called from gdb easily.
+ void showRenderObject(int printedCharacters) const;
+ void showRenderTreeAndMark(const RenderObject* markedObject1 = 0, const char* markedLabel1 = 0, const RenderObject* markedObject2 = 0, const char* markedLabel2 = 0, int depth = 0) const;
+#endif
+
+ static RenderObject* createObject(Node*, RenderStyle*);
+
+ // 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);
+
+private:
+ // The normal operator new is disallowed on all render objects.
+ void* operator new(size_t) throw();
+
+public:
+ RenderArena* renderArena() const { return document()->renderArena(); }
+
+ virtual bool isApplet() const { return false; }
+ virtual bool isBR() const { return false; }
+ virtual bool isBlockFlow() const { return false; }
+ virtual bool isBoxModelObject() const { return false; }
+ virtual bool isCounter() const { return false; }
+ virtual bool isDetails() const { return false; }
+ virtual bool isDetailsMarker() const { return false; }
+ virtual bool isEmbeddedObject() const { return false; }
+ virtual bool isFieldset() const { return false; }
+ virtual bool isFileUploadControl() const { return false; }
+ virtual bool isFrame() const { return false; }
+ virtual bool isFrameSet() const { return false; }
+ virtual bool isImage() const { return false; }
+ virtual bool isInlineBlockOrInlineTable() const { return false; }
+ virtual bool isListBox() const { return false; }
+ virtual bool isListItem() const { return false; }
+ virtual bool isListMarker() const { return false; }
+ virtual bool isMedia() const { return false; }
+ virtual bool isMenuList() const { return false; }
+#if ENABLE(METER_TAG)
+ virtual bool isMeter() const { return false; }
+#endif
+#if ENABLE(PROGRESS_TAG)
+ virtual bool isProgress() const { return false; }
+#endif
+ virtual bool isRenderBlock() const { return false; }
+ virtual bool isRenderButton() const { return false; }
+ virtual bool isRenderIFrame() const { return false; }
+ virtual bool isRenderImage() const { return false; }
+ virtual bool isRenderInline() const { return false; }
+ virtual bool isRenderPart() const { return false; }
+ virtual bool isRenderView() const { return false; }
+ virtual bool isReplica() const { return false; }
+
+ virtual bool isRuby() const { return false; }
+ virtual bool isRubyBase() const { return false; }
+ virtual bool isRubyRun() const { return false; }
+ virtual bool isRubyText() const { return false; }
+
+ virtual bool isSlider() const { return false; }
+ virtual bool isSummary() const { return false; }
+ virtual bool isTable() const { return false; }
+ virtual bool isTableCell() const { return false; }
+ virtual bool isTableCol() const { return false; }
+ virtual bool isTableRow() const { return false; }
+ virtual bool isTableSection() const { return false; }
+ virtual bool isTextControl() const { return false; }
+ virtual bool isTextArea() const { return false; }
+ virtual bool isTextField() const { return false; }
+ virtual bool isVideo() const { return false; }
+ virtual bool isWidget() const { return false; }
+ virtual bool isCanvas() const { return false; }
+#if ENABLE(FULLSCREEN_API)
+ virtual bool isRenderFullScreen() const { return false; }
+#endif
+
+ bool isRoot() const { return document()->documentElement() == m_node; }
+ bool isBody() const;
+ bool isHR() const;
+ bool isLegend() const;
+
+ bool isHTMLMarquee() const;
+
+ inline bool isBeforeContent() const;
+ inline bool isAfterContent() const;
+ static inline bool isBeforeContent(const RenderObject* obj) { return obj && obj->isBeforeContent(); }
+ static inline bool isAfterContent(const RenderObject* obj) { return obj && obj->isAfterContent(); }
+
+ bool childrenInline() const { return m_childrenInline; }
+ void setChildrenInline(bool b = true) { m_childrenInline = b; }
+ bool hasColumns() const { return m_hasColumns; }
+ void setHasColumns(bool b = true) { m_hasColumns = b; }
+ bool cellWidthChanged() const { return m_cellWidthChanged; }
+ void setCellWidthChanged(bool b = true) { m_cellWidthChanged = b; }
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return false; }
+
+#if ENABLE(MATHML)
+ virtual bool isRenderMathMLBlock() const { return false; }
+#endif // ENABLE(MATHML)
+
+#if ENABLE(SVG)
+ // FIXME: Until all SVG renders can be subclasses of RenderSVGModelObject we have
+ // to add SVG renderer methods to RenderObject with an ASSERT_NOT_REACHED() default implementation.
+ virtual bool isSVGRoot() const { return false; }
+ virtual bool isSVGContainer() const { return false; }
+ virtual bool isSVGViewportContainer() const { return false; }
+ virtual bool isSVGGradientStop() const { return false; }
+ virtual bool isSVGHiddenContainer() const { return false; }
+ virtual bool isSVGPath() const { return false; }
+ virtual bool isSVGText() const { return false; }
+ virtual bool isSVGTextPath() const { return false; }
+ virtual bool isSVGInline() const { return false; }
+ virtual bool isSVGInlineText() const { return false; }
+ virtual bool isSVGImage() const { return false; }
+ virtual bool isSVGForeignObject() const { return false; }
+ virtual bool isSVGResourceContainer() const { return false; }
+ virtual bool isSVGResourceFilterPrimitive() const { return false; }
+ virtual bool isSVGShadowTreeRootContainer() const { return false; }
+
+ virtual RenderSVGResourceContainer* toRenderSVGResourceContainer();
+
+ // FIXME: Those belong into a SVG specific base-class for all renderers (see above)
+ // Unfortunately we don't have such a class yet, because it's not possible for all renderers
+ // to inherit from RenderSVGObject -> RenderObject (some need RenderBlock inheritance for instance)
+ virtual void setNeedsTransformUpdate() { }
+ virtual void setNeedsBoundariesUpdate();
+
+ // Per SVG 1.1 objectBoundingBox ignores clipping, masking, filter effects, opacity and stroke-width.
+ // This is used for all computation of objectBoundingBox relative units and by SVGLocateable::getBBox().
+ // NOTE: Markers are not specifically ignored here by SVG 1.1 spec, but we ignore them
+ // since stroke-width is ignored (and marker size can depend on stroke-width).
+ // objectBoundingBox is returned local coordinates.
+ // The name objectBoundingBox is taken from the SVG 1.1 spec.
+ virtual FloatRect objectBoundingBox() const;
+ virtual FloatRect strokeBoundingBox() const;
+
+ // Returns the smallest rectangle enclosing all of the painted content
+ // respecting clipping, masking, filters, opacity, stroke-width and markers
+ virtual FloatRect repaintRectInLocalCoordinates() const;
+
+ // This only returns the transform="" value from the element
+ // most callsites want localToParentTransform() instead.
+ virtual AffineTransform localTransform() const;
+
+ // Returns the full transform mapping from local coordinates to local coords for the parent SVG renderer
+ // This includes any viewport transforms and x/y offsets as well as the transform="" value off the element.
+ virtual const AffineTransform& localToParentTransform() const;
+
+ // SVG uses FloatPoint precise hit testing, and passes the point in parent
+ // coordinates instead of in repaint container coordinates. Eventually the
+ // rest of the rendering tree will move to a similar model.
+ virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+#endif
+
+ bool isAnonymous() const { return m_isAnonymous; }
+ void setIsAnonymous(bool b) { m_isAnonymous = b; }
+ bool isAnonymousBlock() const
+ {
+ return m_isAnonymous && style()->display() == BLOCK && style()->styleType() == NOPSEUDO && !isListMarker();
+ }
+ bool isAnonymousColumnsBlock() const { return style()->specifiesColumns() && isAnonymousBlock(); }
+ bool isAnonymousColumnSpanBlock() const { return style()->columnSpan() && isAnonymousBlock(); }
+ bool isElementContinuation() const { return node() && node()->renderer() != this; }
+ bool isInlineElementContinuation() const { return isElementContinuation() && isInline(); }
+ bool isBlockElementContinuation() const { return isElementContinuation() && !isInline(); }
+ virtual RenderBoxModelObject* virtualContinuation() const { return 0; }
+
+ bool isFloating() const { return m_floating; }
+ bool isPositioned() const { return m_positioned; } // absolute or fixed positioning
+ bool isRelPositioned() const { return m_relPositioned; } // relative positioning
+ bool isText() const { return m_isText; }
+ bool isBox() const { return m_isBox; }
+ bool isInline() const { return m_inline; } // inline object
+ bool isRunIn() const { return style()->display() == RUN_IN; } // run-in object
+ bool isDragging() const { return m_isDragging; }
+ bool isReplaced() const { return m_replaced; } // a "replaced" element (see CSS)
+
+ bool hasLayer() const { return m_hasLayer; }
+
+ bool hasBoxDecorations() const { return m_paintBackground; }
+ bool mustRepaintBackgroundOrBorder() const;
+ bool hasBackground() const { return style()->hasBackground(); }
+ bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout || m_needsPositionedMovementLayout; }
+ bool selfNeedsLayout() const { return m_needsLayout; }
+ bool needsPositionedMovementLayout() const { return m_needsPositionedMovementLayout; }
+ bool needsPositionedMovementLayoutOnly() const { return m_needsPositionedMovementLayout && !m_needsLayout && !m_normalChildNeedsLayout && !m_posChildNeedsLayout; }
+ bool posChildNeedsLayout() const { return m_posChildNeedsLayout; }
+ bool normalChildNeedsLayout() const { return m_normalChildNeedsLayout; }
+
+ bool preferredLogicalWidthsDirty() const { return m_preferredLogicalWidthsDirty; }
+
+ bool isSelectionBorder() const;
+
+ bool hasClip() const { return isPositioned() && style()->hasClip(); }
+ bool hasOverflowClip() const { return m_hasOverflowClip; }
+
+ bool hasTransform() const { return m_hasTransform; }
+ bool hasMask() const { return style() && style()->hasMask(); }
+
+ void drawLineForBoxSide(GraphicsContext*, int x1, int y1, int x2, int y2, BoxSide,
+ Color, EBorderStyle, int adjbw1, int adjbw2);
+#if HAVE(PATH_BASED_BORDER_RADIUS_DRAWING)
+ void drawBoxSideFromPath(GraphicsContext*, IntRect, Path,
+ float thickness, float drawThickness, BoxSide, const RenderStyle*,
+ Color, EBorderStyle);
+#else
+ // FIXME: This function should be removed when all ports implement GraphicsContext::clipConvexPolygon()!!
+ // At that time, everyone can use RenderObject::drawBoxSideFromPath() instead. This should happen soon.
+ void drawArcForBoxSide(GraphicsContext*, int x, int y, float thickness, IntSize radius, int angleStart,
+ int angleSpan, BoxSide, Color, EBorderStyle, bool firstCorner);
+#endif
+
+ IntRect borderInnerRect(const IntRect&, unsigned short topWidth, unsigned short bottomWidth,
+ unsigned short leftWidth, unsigned short rightWidth) const;
+
+ // The pseudo element style can be cached or uncached. Use the cached method if the pseudo element doesn't respect
+ // any pseudo classes (and therefore has no concept of changing state).
+ RenderStyle* getCachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0) const;
+ PassRefPtr<RenderStyle> getUncachedPseudoStyle(PseudoId, RenderStyle* parentStyle = 0, RenderStyle* ownStyle = 0) const;
+
+ virtual void updateDragState(bool dragOn);
+
+ RenderView* view() const;
+
+ // Returns true if this renderer is rooted, and optionally returns the hosting view (the root of the hierarchy).
+ bool isRooted(RenderView** = 0);
+
+ Node* node() const { return m_isAnonymous ? 0 : m_node; }
+ void setNode(Node* node) { m_node = node; }
+
+ Document* document() const { return m_node->document(); }
+ Frame* frame() const { return document()->frame(); }
+
+ bool hasOutlineAnnotation() const;
+ bool hasOutline() const { return style()->hasOutline() || hasOutlineAnnotation(); }
+
+ // Returns the object containing this one. Can be different from parent for positioned elements.
+ // If repaintContainer and repaintContainerSkipped are not null, on return *repaintContainerSkipped
+ // is true if the renderer returned is an ancestor of repaintContainer.
+ RenderObject* container(RenderBoxModelObject* repaintContainer = 0, bool* repaintContainerSkipped = 0) const;
+
+ virtual RenderObject* hoverAncestor() const { return parent(); }
+
+ // IE Extension that can be called on any RenderObject. See the implementation for the details.
+ RenderBoxModelObject* offsetParent() const;
+
+ void markContainingBlocksForLayout(bool scheduleRelayout = true, RenderObject* newRoot = 0);
+ void setNeedsLayout(bool b, bool markParents = true);
+ void setChildNeedsLayout(bool b, bool markParents = true);
+ void setNeedsPositionedMovementLayout();
+ void setPreferredLogicalWidthsDirty(bool, bool markParents = true);
+ void invalidateContainerPreferredLogicalWidths();
+
+ void setNeedsLayoutAndPrefWidthsRecalc()
+ {
+ setNeedsLayout(true);
+ setPreferredLogicalWidthsDirty(true);
+ }
+
+ void setPositioned(bool b = true) { m_positioned = b; }
+ void setRelPositioned(bool b = true) { m_relPositioned = b; }
+ void setFloating(bool b = true) { m_floating = b; }
+ void setInline(bool b = true) { m_inline = b; }
+ void setHasBoxDecorations(bool b = true) { m_paintBackground = b; }
+ void setIsText() { m_isText = true; }
+ void setIsBox() { m_isBox = true; }
+ void setReplaced(bool b = true) { m_replaced = b; }
+ void setHasOverflowClip(bool b = true) { m_hasOverflowClip = b; }
+ void setHasLayer(bool b = true) { m_hasLayer = b; }
+ void setHasTransform(bool b = true) { m_hasTransform = b; }
+ void setHasReflection(bool b = true) { m_hasReflection = b; }
+
+ void scheduleRelayout();
+
+ void updateFillImages(const FillLayer*, const FillLayer*);
+ void updateImage(StyleImage*, StyleImage*);
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ // Recursive function that computes the size and position of this object and all its descendants.
+ virtual void layout();
+
+ /* This function performs a layout only if one is needed. */
+ void layoutIfNeeded() { if (needsLayout()) layout(); }
+
+ // used for element state updates that cannot be fixed with a
+ // repaint and do not need a relayout
+ virtual void updateFromElement() { }
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ virtual void addDashboardRegions(Vector<DashboardRegionValue>&);
+ void collectDashboardRegions(Vector<DashboardRegionValue>&);
+#endif
+
+ bool hitTest(const HitTestRequest&, HitTestResult&, const IntPoint&, int tx, int ty, HitTestFilter = HitTestAll);
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+ virtual void updateHitTestResult(HitTestResult&, const IntPoint&);
+
+ VisiblePosition positionForCoordinates(int x, int y);
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+ VisiblePosition createVisiblePosition(int offset, EAffinity);
+ VisiblePosition createVisiblePosition(const Position&);
+
+ virtual void dirtyLinesFromChangedChild(RenderObject*);
+
+ // Called to update a style that is allowed to trigger animations.
+ // FIXME: Right now this will typically be called only when updating happens from the DOM on explicit elements.
+ // We don't yet handle generated content animation such as first-letter or before/after (we'll worry about this later).
+ void setAnimatableStyle(PassRefPtr<RenderStyle>);
+
+ // Set the style of the object and update the state of the object accordingly.
+ virtual void setStyle(PassRefPtr<RenderStyle>);
+
+ // Updates only the local style ptr of the object. Does not update the state of the object,
+ // and so only should be called when the style is known not to have changed (or from setStyle).
+ void setStyleInternal(PassRefPtr<RenderStyle>);
+
+ // returns the containing block level element for this element.
+ RenderBlock* containingBlock() const;
+
+ // Convert the given local point to absolute coordinates
+ // FIXME: Temporary. If useTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware.
+ FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const;
+ FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const;
+
+ // Convert a local quad to absolute coordinates, taking transforms into account.
+ FloatQuad localToAbsoluteQuad(const FloatQuad& quad, bool fixed = false) const
+ {
+ return localToContainerQuad(quad, 0, fixed);
+ }
+ // Convert a local quad into the coordinate system of container, taking transforms into account.
+ FloatQuad localToContainerQuad(const FloatQuad&, RenderBoxModelObject* repaintContainer, bool fixed = false) const;
+
+ // Return the offset from the container() renderer (excluding transforms). In multi-column layout,
+ // different offsets apply at different points, so return the offset that applies to the given point.
+ virtual IntSize offsetFromContainer(RenderObject*, const IntPoint&) const;
+ // Return the offset from an object up the container() chain. Asserts that none of the intermediate objects have transforms.
+ IntSize offsetFromAncestorContainer(RenderObject*) const;
+
+ virtual void absoluteRects(Vector<IntRect>&, int, int) { }
+ // FIXME: useTransforms should go away eventually
+ IntRect absoluteBoundingBoxRect(bool useTransforms = false);
+
+ // Build an array of quads in absolute coords for line boxes
+ virtual void absoluteQuads(Vector<FloatQuad>&) { }
+
+ void absoluteFocusRingQuads(Vector<FloatQuad>&);
+
+ // the rect that will be painted if this object is passed as the paintingRoot
+ IntRect paintingRootRect(IntRect& topLevelRect);
+
+ virtual int minPreferredLogicalWidth() const { return 0; }
+ virtual int maxPreferredLogicalWidth() const { return 0; }
+
+ RenderStyle* style() const { return m_style.get(); }
+ RenderStyle* firstLineStyle() const { return document()->usesFirstLineRules() ? firstLineStyleSlowCase() : style(); }
+ RenderStyle* style(bool firstLine) const { return firstLine ? firstLineStyle() : style(); }
+
+ // Used only by Element::pseudoStyleCacheIsInvalid to get a first line style based off of a
+ // given new style, without accessing the cache.
+ PassRefPtr<RenderStyle> uncachedFirstLineStyle(RenderStyle*) const;
+
+ // Anonymous blocks that are part of of a continuation chain will return their inline continuation's outline style instead.
+ // This is typically only relevant when repainting.
+ virtual RenderStyle* outlineStyleForRepaint() const { return style(); }
+
+ void getTextDecorationColors(int decorations, Color& underline, Color& overline,
+ Color& linethrough, bool quirksMode = false);
+
+ // Return the RenderBox in the container chain which is responsible for painting this object, or 0
+ // if painting is root-relative. This is the container that should be passed to the 'forRepaint'
+ // methods.
+ RenderBoxModelObject* containerForRepaint() const;
+ // Actually do the repaint of rect r for this object which has been computed in the coordinate space
+ // of repaintContainer. If repaintContainer is 0, repaint via the view.
+ void repaintUsingContainer(RenderBoxModelObject* repaintContainer, const IntRect& r, bool immediate = false);
+
+ // Repaint the entire object. Called when, e.g., the color of a border changes, or when a border
+ // style changes.
+ void repaint(bool immediate = false);
+
+ // Repaint a specific subrectangle within a given object. The rect |r| is in the object's coordinate space.
+ void repaintRectangle(const IntRect&, bool immediate = false);
+
+ // Repaint only if our old bounds and new bounds are different. The caller may pass in newBounds and newOutlineBox if they are known.
+ bool repaintAfterLayoutIfNeeded(RenderBoxModelObject* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox, const IntRect* newBoundsPtr = 0, const IntRect* newOutlineBoxPtr = 0);
+
+ // Repaint only if the object moved.
+ virtual void repaintDuringLayoutIfMoved(const IntRect& rect);
+
+ // Called to repaint a block's floats.
+ virtual void repaintOverhangingFloats(bool paintAllDescendants = false);
+
+ bool checkForRepaintDuringLayout() const;
+
+ // Returns the rect that should be repainted whenever this object changes. The rect is in the view's
+ // coordinate space. This method deals with outlines and overflow.
+ IntRect absoluteClippedOverflowRect()
+ {
+ return clippedOverflowRectForRepaint(0);
+ }
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual IntRect rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth);
+
+ // Given a rect in the object's coordinate space, compute a rect suitable for repainting
+ // that rect in view coordinates.
+ void computeAbsoluteRepaintRect(IntRect& r, bool fixed = false)
+ {
+ return computeRectForRepaint(0, r, fixed);
+ }
+ // Given a rect in the object's coordinate space, compute a rect suitable for repainting
+ // that rect in the coordinate space of repaintContainer.
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+
+ // If multiple-column layout results in applying an offset to the given point, add the same
+ // offset to the given size.
+ virtual void adjustForColumns(IntSize&, const IntPoint&) const { }
+
+ virtual unsigned int length() const { return 1; }
+
+ bool isFloatingOrPositioned() const { return (isFloating() || isPositioned()); }
+
+ bool isTransparent() const { return style()->opacity() < 1.0f; }
+ float opacity() const { return style()->opacity(); }
+
+ bool hasReflection() const { return m_hasReflection; }
+
+ // Applied as a "slop" to dirty rect checks during the outline painting phase's dirty-rect checks.
+ int maximalOutlineSize(PaintPhase) const;
+
+ void setHasMarkupTruncation(bool b = true) { m_hasMarkupTruncation = b; }
+ bool hasMarkupTruncation() const { return m_hasMarkupTruncation; }
+
+ enum SelectionState {
+ SelectionNone, // The object is not selected.
+ SelectionStart, // The object either contains the start of a selection run or is the start of a run
+ SelectionInside, // The object is fully encompassed by a selection run
+ SelectionEnd, // The object either contains the end of a selection run or is the end of a run
+ SelectionBoth // The object contains an entire run or is the sole selected object in that run
+ };
+
+ // The current selection state for an object. For blocks, the state refers to the state of the leaf
+ // descendants (as described above in the SelectionState enum declaration).
+ SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState);; }
+
+ // Sets the selection state for an object.
+ virtual void setSelectionState(SelectionState state) { m_selectionState = state; }
+
+ // A single rectangle that encompasses all of the selected objects within this object. Used to determine the tightest
+ // possible bounding box for the selection.
+ IntRect selectionRect(bool clipToVisibleContent = true) { return selectionRectForRepaint(0, clipToVisibleContent); }
+ virtual IntRect selectionRectForRepaint(RenderBoxModelObject* /*repaintContainer*/, bool /*clipToVisibleContent*/ = true) { return IntRect(); }
+
+ // Whether or not an object can be part of the leaf elements of the selection.
+ virtual bool canBeSelectionLeaf() const { return false; }
+
+ // Whether or not a block has selected children.
+ bool hasSelectedChildren() const { return m_selectionState != SelectionNone; }
+
+ // Obtains the selection colors that should be used when painting a selection.
+ Color selectionBackgroundColor() const;
+ Color selectionForegroundColor() const;
+ Color selectionEmphasisMarkColor() const;
+
+ // Whether or not a given block needs to paint selection gaps.
+ virtual bool shouldPaintSelectionGaps() const { return false; }
+
+#if ENABLE(DRAG_SUPPORT)
+ Node* draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const;
+#endif
+
+ /**
+ * Returns the local coordinates of the caret within this render object.
+ * @param caretOffset zero-based offset determining position within the render object.
+ * @param extraWidthToEndOfLine optional out arg to give extra width to end of line -
+ * useful for character range rect computations
+ */
+ virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
+
+ bool isMarginBeforeQuirk() const { return m_marginBeforeQuirk; }
+ bool isMarginAfterQuirk() const { return m_marginAfterQuirk; }
+ void setMarginBeforeQuirk(bool b = true) { m_marginBeforeQuirk = b; }
+ void setMarginAfterQuirk(bool b = true) { m_marginAfterQuirk = b; }
+
+ // When performing a global document tear-down, the renderer of the document is cleared. We use this
+ // as a hook to detect the case of document destruction and don't waste time doing unnecessary work.
+ bool documentBeingDestroyed() const;
+
+ virtual void destroy();
+
+ // Virtual function helpers for CSS3 Flexible Box Layout
+ virtual bool isFlexibleBox() const { return false; }
+ virtual bool isFlexingChildren() const { return false; }
+ virtual bool isStretchingChildren() const { return false; }
+
+ virtual int caretMinOffset() const;
+ virtual int caretMaxOffset() const;
+ virtual unsigned caretMaxRenderedOffset() const;
+
+ virtual int previousOffset(int current) const;
+ virtual int previousOffsetForBackwardDeletion(int current) const;
+ virtual int nextOffset(int current) const;
+
+ virtual void imageChanged(CachedImage*, const IntRect* = 0);
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0) { }
+ virtual bool willRenderImage(CachedImage*);
+
+ void selectionStartEnd(int& spos, int& epos) const;
+
+ bool hasOverrideSize() const { return m_hasOverrideSize; }
+ void setHasOverrideSize(bool b) { m_hasOverrideSize = b; }
+
+ void remove() { if (parent()) parent()->removeChild(this); }
+
+ AnimationController* animation() const;
+
+ bool visibleToHitTesting() const { return style()->visibility() == VISIBLE && style()->pointerEvents() != PE_NONE; }
+
+ // Map points and quads through elements, potentially via 3d transforms. You should never need to call these directly; use
+ // localToAbsolute/absoluteToLocal methods instead.
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+ virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const;
+
+ bool shouldUseTransformFromContainer(const RenderObject* container) const;
+ void getTransformFromContainer(const RenderObject* container, const IntSize& offsetInContainer, TransformationMatrix&) const;
+
+ virtual void addFocusRingRects(Vector<IntRect>&, int /*tx*/, int /*ty*/) { };
+
+ IntRect absoluteOutlineBounds() const
+ {
+ return outlineBoundsForRepaint(0);
+ }
+
+protected:
+ // Overrides should call the superclass at the end
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ // Overrides should call the superclass at the start
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ void paintFocusRing(GraphicsContext*, int tx, int ty, RenderStyle*);
+ void paintOutline(GraphicsContext*, int tx, int ty, int w, int h);
+ void addPDFURLRect(GraphicsContext*, const IntRect&);
+
+ virtual IntRect viewRect() const;
+
+ void adjustRectForOutlineAndShadow(IntRect&) const;
+
+ void arenaDelete(RenderArena*, void* objectBase);
+
+ virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* /*repaintContainer*/, IntPoint* /*cachedOffsetToRepaintContainer*/ = 0) const { return IntRect(); }
+
+ class LayoutRepainter {
+ public:
+ LayoutRepainter(RenderObject& object, bool checkForRepaint, const IntRect* oldBounds = 0)
+ : m_object(object)
+ , m_repaintContainer(0)
+ , m_checkForRepaint(checkForRepaint)
+ {
+ if (m_checkForRepaint) {
+ m_repaintContainer = m_object.containerForRepaint();
+ m_oldBounds = oldBounds ? *oldBounds : m_object.clippedOverflowRectForRepaint(m_repaintContainer);
+ m_oldOutlineBox = m_object.outlineBoundsForRepaint(m_repaintContainer);
+ }
+ }
+
+ // Return true if it repainted.
+ bool repaintAfterLayout()
+ {
+ return m_checkForRepaint ? m_object.repaintAfterLayoutIfNeeded(m_repaintContainer, m_oldBounds, m_oldOutlineBox) : false;
+ }
+
+ bool checkForRepaint() const { return m_checkForRepaint; }
+
+ private:
+ RenderObject& m_object;
+ RenderBoxModelObject* m_repaintContainer;
+ IntRect m_oldBounds;
+ IntRect m_oldOutlineBox;
+ bool m_checkForRepaint;
+ };
+
+private:
+ RenderStyle* firstLineStyleSlowCase() const;
+ StyleDifference adjustStyleDifference(StyleDifference, unsigned contextSensitiveProperties) const;
+
+ Color selectionColor(int colorProperty) const;
+
+ RefPtr<RenderStyle> m_style;
+
+ Node* m_node;
+
+ RenderObject* m_parent;
+ RenderObject* m_previous;
+ RenderObject* m_next;
+
+#ifndef NDEBUG
+ bool m_hasAXObject;
+ bool m_setNeedsLayoutForbidden : 1;
+#endif
+
+ // 32 bits have been used here. THERE ARE NO FREE BITS AVAILABLE.
+ bool m_needsLayout : 1;
+ bool m_needsPositionedMovementLayout :1;
+ bool m_normalChildNeedsLayout : 1;
+ bool m_posChildNeedsLayout : 1;
+ bool m_preferredLogicalWidthsDirty : 1;
+ bool m_floating : 1;
+
+ bool m_positioned : 1;
+ bool m_relPositioned : 1;
+ bool m_paintBackground : 1; // if the box has something to paint in the
+ // background painting phase (background, border, etc)
+
+ bool m_isAnonymous : 1;
+ bool m_isText : 1;
+ bool m_isBox : 1;
+ bool m_inline : 1;
+ bool m_replaced : 1;
+ bool m_isDragging : 1;
+
+ bool m_hasLayer : 1;
+ bool m_hasOverflowClip : 1; // Set in the case of overflow:auto/scroll/hidden
+ bool m_hasTransform : 1;
+ bool m_hasReflection : 1;
+
+ bool m_hasOverrideSize : 1;
+
+public:
+ bool m_hasCounterNodeMap : 1;
+ bool m_everHadLayout : 1;
+
+private:
+ // These bitfields are moved here from subclasses to pack them together
+ // from RenderBlock
+ bool m_childrenInline : 1;
+ bool m_marginBeforeQuirk : 1;
+ bool m_marginAfterQuirk : 1;
+ bool m_hasMarkupTruncation : 1;
+ unsigned m_selectionState : 3; // SelectionState
+ bool m_hasColumns : 1;
+
+ // from RenderTableCell
+ bool m_cellWidthChanged : 1;
+
+private:
+ // Store state between styleWillChange and styleDidChange
+ static bool s_affectsParentBlock;
+};
+
+inline bool RenderObject::documentBeingDestroyed() const
+{
+ return !document()->renderer();
+}
+
+inline bool RenderObject::isBeforeContent() const
+{
+ if (style()->styleType() != BEFORE)
+ return false;
+ // Text nodes don't have their own styles, so ignore the style on a text node.
+ if (isText() && !isBR())
+ return false;
+ return true;
+}
+
+inline bool RenderObject::isAfterContent() const
+{
+ if (style()->styleType() != AFTER)
+ return false;
+ // Text nodes don't have their own styles, so ignore the style on a text node.
+ if (isText() && !isBR())
+ return false;
+ return true;
+}
+
+inline void RenderObject::setNeedsLayout(bool b, bool markParents)
+{
+ bool alreadyNeededLayout = m_needsLayout;
+ m_needsLayout = b;
+ if (b) {
+ ASSERT(!isSetNeedsLayoutForbidden());
+ if (!alreadyNeededLayout) {
+ if (markParents)
+ markContainingBlocksForLayout();
+ if (hasLayer())
+ setLayerNeedsFullRepaint();
+ }
+ } else {
+ m_everHadLayout = true;
+ m_posChildNeedsLayout = false;
+ m_normalChildNeedsLayout = false;
+ m_needsPositionedMovementLayout = false;
+ }
+}
+
+inline void RenderObject::setChildNeedsLayout(bool b, bool markParents)
+{
+ bool alreadyNeededLayout = m_normalChildNeedsLayout;
+ m_normalChildNeedsLayout = b;
+ if (b) {
+ ASSERT(!isSetNeedsLayoutForbidden());
+ if (!alreadyNeededLayout && markParents)
+ markContainingBlocksForLayout();
+ } else {
+ m_posChildNeedsLayout = false;
+ m_normalChildNeedsLayout = false;
+ m_needsPositionedMovementLayout = false;
+ }
+}
+
+inline void RenderObject::setNeedsPositionedMovementLayout()
+{
+ bool alreadyNeededLayout = needsLayout();
+ m_needsPositionedMovementLayout = true;
+ if (!alreadyNeededLayout) {
+ markContainingBlocksForLayout();
+ if (hasLayer())
+ setLayerNeedsFullRepaint();
+ }
+}
+
+inline bool objectIsRelayoutBoundary(const RenderObject *obj)
+{
+ // FIXME: In future it may be possible to broaden this condition in order to improve performance.
+ // Table cells are excluded because even when their CSS height is fixed, their height()
+ // may depend on their contents.
+ return obj->isTextControl()
+ || (obj->hasOverflowClip() && !obj->style()->width().isIntrinsicOrAuto() && !obj->style()->height().isIntrinsicOrAuto() && !obj->style()->height().isPercent() && !obj->isTableCell())
+#if ENABLE(SVG)
+ || obj->isSVGRoot()
+#endif
+ ;
+}
+
+inline void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot)
+{
+ ASSERT(!scheduleRelayout || !newRoot);
+
+ RenderObject* o = container();
+ RenderObject* last = this;
+
+ while (o) {
+ // Don't mark the outermost object of an unrooted subtree. That object will be
+ // marked when the subtree is added to the document.
+ RenderObject* container = o->container();
+ if (!container && !o->isRenderView())
+ return;
+ if (!last->isText() && (last->style()->position() == FixedPosition || last->style()->position() == AbsolutePosition)) {
+ if ((last->style()->top().isAuto() && last->style()->bottom().isAuto()) || last->style()->top().isStatic()) {
+ RenderObject* parent = last->parent();
+ if (!parent->normalChildNeedsLayout()) {
+ parent->setChildNeedsLayout(true, false);
+ if (parent != newRoot)
+ parent->markContainingBlocksForLayout(scheduleRelayout, newRoot);
+ }
+ }
+ if (o->m_posChildNeedsLayout)
+ return;
+ o->m_posChildNeedsLayout = true;
+ ASSERT(!o->isSetNeedsLayoutForbidden());
+ } else {
+ if (o->m_normalChildNeedsLayout)
+ return;
+ o->m_normalChildNeedsLayout = true;
+ ASSERT(!o->isSetNeedsLayoutForbidden());
+ }
+
+ if (o == newRoot)
+ return;
+
+ last = o;
+ if (scheduleRelayout && objectIsRelayoutBoundary(last))
+ break;
+ o = container;
+ }
+
+ if (scheduleRelayout)
+ last->scheduleRelayout();
+}
+
+inline void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering)
+{
+#if !ENABLE(3D_RENDERING)
+ UNUSED_PARAM(has3DRendering);
+ matrix.makeAffine();
+#else
+ if (!has3DRendering)
+ matrix.makeAffine();
+#endif
+}
+
+inline int adjustForAbsoluteZoom(int value, RenderObject* renderer)
+{
+ return adjustForAbsoluteZoom(value, renderer->style());
+}
+
+inline FloatPoint adjustFloatPointForAbsoluteZoom(const FloatPoint& point, RenderObject* renderer)
+{
+ // The result here is in floats, so we don't need the truncation hack from the integer version above.
+ float zoomFactor = renderer->style()->effectiveZoom();
+ if (zoomFactor == 1)
+ return point;
+ return FloatPoint(point.x() / zoomFactor, point.y() / zoomFactor);
+}
+
+inline void adjustFloatQuadForAbsoluteZoom(FloatQuad& quad, RenderObject* renderer)
+{
+ quad.setP1(adjustFloatPointForAbsoluteZoom(quad.p1(), renderer));
+ quad.setP2(adjustFloatPointForAbsoluteZoom(quad.p2(), renderer));
+ quad.setP3(adjustFloatPointForAbsoluteZoom(quad.p3(), renderer));
+ quad.setP4(adjustFloatPointForAbsoluteZoom(quad.p4(), renderer));
+}
+
+inline void adjustFloatRectForAbsoluteZoom(FloatRect& rect, RenderObject* renderer)
+{
+ RenderStyle* style = renderer->style();
+ rect.setX(adjustFloatForAbsoluteZoom(rect.x(), style));
+ rect.setY(adjustFloatForAbsoluteZoom(rect.y(), style));
+ rect.setWidth(adjustFloatForAbsoluteZoom(rect.width(), style));
+ rect.setHeight(adjustFloatForAbsoluteZoom(rect.height(), style));
+}
+
+} // namespace WebCore
+
+#ifndef NDEBUG
+// Outside the WebCore namespace for ease of invocation from gdb.
+void showTree(const WebCore::RenderObject*);
+void showRenderTree(const WebCore::RenderObject* object1);
+// We don't make object2 an optional parameter so that showRenderTree
+// can be called from gdb easily.
+void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2);
+#endif
+
+#endif // RenderObject_h
diff --git a/Source/WebCore/rendering/RenderObjectChildList.cpp b/Source/WebCore/rendering/RenderObjectChildList.cpp
new file mode 100644
index 0000000..c7c8e44
--- /dev/null
+++ b/Source/WebCore/rendering/RenderObjectChildList.cpp
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2010. 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 "RenderObjectChildList.h"
+
+#include "AXObjectCache.h"
+#include "RenderBlock.h"
+#include "RenderCounter.h"
+#include "RenderImage.h"
+#include "RenderImageResourceStyleImage.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderListItem.h"
+#include "RenderStyle.h"
+#include "RenderTextFragment.h"
+#include "RenderView.h"
+
+namespace WebCore {
+
+void RenderObjectChildList::destroyLeftoverChildren()
+{
+ while (firstChild()) {
+ if (firstChild()->isListMarker() || (firstChild()->style()->styleType() == FIRST_LETTER && !firstChild()->isText()))
+ firstChild()->remove(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment.
+ else if (firstChild()->isRunIn() && firstChild()->node()) {
+ firstChild()->node()->setRenderer(0);
+ firstChild()->node()->setNeedsStyleRecalc();
+ firstChild()->destroy();
+ } else {
+ // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields.
+ if (firstChild()->node())
+ firstChild()->node()->setRenderer(0);
+ firstChild()->destroy();
+ }
+ }
+}
+
+RenderObject* RenderObjectChildList::removeChildNode(RenderObject* owner, RenderObject* oldChild, bool fullRemove)
+{
+ ASSERT(oldChild->parent() == owner);
+
+ // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or
+ // that a positioned child got yanked). We also repaint, so that the area exposed when the child
+ // disappears gets repainted properly.
+ if (!owner->documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) {
+ oldChild->setNeedsLayoutAndPrefWidthsRecalc();
+ if (oldChild->isBody())
+ owner->view()->repaint();
+ else
+ oldChild->repaint();
+ }
+
+ // If we have a line box wrapper, delete it.
+ if (oldChild->isBox())
+ toRenderBox(oldChild)->deleteLineBoxWrapper();
+
+ if (!owner->documentBeingDestroyed() && fullRemove) {
+ // if we remove visible child from an invisible parent, we don't know the layer visibility any more
+ RenderLayer* layer = 0;
+ if (owner->style()->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) {
+ layer = owner->enclosingLayer();
+ layer->dirtyVisibleContentStatus();
+ }
+
+ // Keep our layer hierarchy updated.
+ if (oldChild->firstChild() || oldChild->hasLayer()) {
+ if (!layer)
+ layer = owner->enclosingLayer();
+ oldChild->removeLayers(layer);
+ }
+
+ if (oldChild->isListItem())
+ toRenderListItem(oldChild)->updateListMarkerNumbers();
+
+ if (oldChild->isPositioned() && owner->childrenInline())
+ owner->dirtyLinesFromChangedChild(oldChild);
+
+#if ENABLE(SVG)
+ // Update cached boundaries in SVG renderers, if a child is removed.
+ owner->setNeedsBoundariesUpdate();
+#endif
+ }
+
+ // If oldChild is the start or end of the selection, then clear the selection to
+ // avoid problems of invalid pointers.
+ // FIXME: The SelectionController should be responsible for this when it
+ // is notified of DOM mutations.
+ if (!owner->documentBeingDestroyed() && oldChild->isSelectionBorder())
+ owner->view()->clearSelection();
+
+ // remove the child
+ if (oldChild->previousSibling())
+ oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
+ if (oldChild->nextSibling())
+ oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
+
+ if (firstChild() == oldChild)
+ setFirstChild(oldChild->nextSibling());
+ if (lastChild() == oldChild)
+ setLastChild(oldChild->previousSibling());
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ if (AXObjectCache::accessibilityEnabled())
+ owner->document()->axObjectCache()->childrenChanged(owner);
+
+ return oldChild;
+}
+
+void RenderObjectChildList::appendChildNode(RenderObject* owner, RenderObject* newChild, bool fullAppend)
+{
+ ASSERT(newChild->parent() == 0);
+ ASSERT(!owner->isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell()));
+
+ newChild->setParent(owner);
+ RenderObject* lChild = lastChild();
+
+ if (lChild) {
+ newChild->setPreviousSibling(lChild);
+ lChild->setNextSibling(newChild);
+ } else
+ setFirstChild(newChild);
+
+ setLastChild(newChild);
+
+ if (fullAppend) {
+ // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
+ // and don't have a layer attached to ourselves.
+ RenderLayer* layer = 0;
+ if (newChild->firstChild() || newChild->hasLayer()) {
+ layer = owner->enclosingLayer();
+ newChild->addLayers(layer, newChild);
+ }
+
+ // if the new child is visible but this object was not, tell the layer it has some visible content
+ // that needs to be drawn and layer visibility optimization can't be used
+ if (owner->style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) {
+ if (!layer)
+ layer = owner->enclosingLayer();
+ if (layer)
+ layer->setHasVisibleContent(true);
+ }
+
+ if (newChild->isListItem())
+ toRenderListItem(newChild)->updateListMarkerNumbers();
+
+ if (!newChild->isFloatingOrPositioned() && owner->childrenInline())
+ owner->dirtyLinesFromChangedChild(newChild);
+ }
+
+ newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy.
+ if (!owner->normalChildNeedsLayout())
+ owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
+
+ if (AXObjectCache::accessibilityEnabled())
+ owner->document()->axObjectCache()->childrenChanged(owner);
+}
+
+void RenderObjectChildList::insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* beforeChild, bool fullInsert)
+{
+ if (!beforeChild) {
+ appendChildNode(owner, child, fullInsert);
+ return;
+ }
+
+ ASSERT(!child->parent());
+ while (beforeChild->parent() != owner && beforeChild->parent()->isAnonymousBlock())
+ beforeChild = beforeChild->parent();
+ ASSERT(beforeChild->parent() == owner);
+
+ ASSERT(!owner->isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell()));
+
+ if (beforeChild == firstChild())
+ setFirstChild(child);
+
+ RenderObject* prev = beforeChild->previousSibling();
+ child->setNextSibling(beforeChild);
+ beforeChild->setPreviousSibling(child);
+ if (prev)
+ prev->setNextSibling(child);
+ child->setPreviousSibling(prev);
+
+ child->setParent(owner);
+
+ if (fullInsert) {
+ // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children
+ // and don't have a layer attached to ourselves.
+ RenderLayer* layer = 0;
+ if (child->firstChild() || child->hasLayer()) {
+ layer = owner->enclosingLayer();
+ child->addLayers(layer, child);
+ }
+
+ // if the new child is visible but this object was not, tell the layer it has some visible content
+ // that needs to be drawn and layer visibility optimization can't be used
+ if (owner->style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) {
+ if (!layer)
+ layer = owner->enclosingLayer();
+ if (layer)
+ layer->setHasVisibleContent(true);
+ }
+
+ if (child->isListItem())
+ toRenderListItem(child)->updateListMarkerNumbers();
+
+ if (!child->isFloating() && owner->childrenInline())
+ owner->dirtyLinesFromChangedChild(child);
+ }
+
+ child->setNeedsLayoutAndPrefWidthsRecalc();
+ if (!owner->normalChildNeedsLayout())
+ owner->setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child.
+
+ if (AXObjectCache::accessibilityEnabled())
+ owner->document()->axObjectCache()->childrenChanged(owner);
+}
+
+static RenderObject* beforeAfterContainer(RenderObject* container, PseudoId type)
+{
+ if (type == BEFORE) {
+ // An anonymous (generated) inline run-in that has PseudoId BEFORE must come from a grandparent.
+ // Therefore we should skip these generated run-ins when checking our immediate children.
+ // If we don't find our :before child immediately, then we should check if we own a
+ // generated inline run-in in the next level of children.
+ RenderObject* first = container;
+ do {
+ // Skip list markers and generated run-ins
+ first = first->firstChild();
+ while (first && (first->isListMarker() || (first->isRenderInline() && first->isRunIn() && first->isAnonymous())))
+ first = first->nextSibling();
+ } while (first && first->isAnonymous() && first->style()->styleType() == NOPSEUDO);
+
+ if (!first)
+ return 0;
+
+ if (first->style()->styleType() == type)
+ return first;
+
+ // Check for a possible generated run-in, using run-in positioning rules.
+ // Skip inlines and floating / positioned blocks, and place as the first child.
+ first = container->firstChild();
+ if (!first->isRenderBlock())
+ return 0;
+ while (first && first->isFloatingOrPositioned())
+ first = first->nextSibling();
+ if (first) {
+ first = first->firstChild();
+ // We still need to skip any list markers that could exist before the run-in.
+ while (first && first->isListMarker())
+ first = first->nextSibling();
+ if (first && first->style()->styleType() == type && first->isRenderInline() && first->isRunIn() && first->isAnonymous())
+ return first;
+ }
+ return 0;
+ }
+
+ if (type == AFTER) {
+ RenderObject* last = container;
+ do {
+ last = last->lastChild();
+ } while (last && last->isAnonymous() && last->style()->styleType() == NOPSEUDO && !last->isListMarker());
+ if (last && last->style()->styleType() != type)
+ return 0;
+ return last;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static RenderObject* findBeforeAfterParent(RenderObject* object)
+{
+ // Only table parts need to search for the :before or :after parent
+ if (!(object->isTable() || object->isTableSection() || object->isTableRow()))
+ return object;
+
+ RenderObject* beforeAfterParent = object;
+ while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage()))
+ beforeAfterParent = beforeAfterParent->firstChild();
+ return beforeAfterParent;
+}
+
+static void invalidateCountersInContainer(RenderObject* container, const AtomicString& identifier)
+{
+ if (!container)
+ return;
+ container = findBeforeAfterParent(container);
+ if (!container)
+ return;
+ // Sometimes the counter is attached directly on the container.
+ if (container->isCounter()) {
+ toRenderCounter(container)->invalidate(identifier);
+ return;
+ }
+ for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) {
+ if (content->isCounter())
+ toRenderCounter(content)->invalidate(identifier);
+ }
+}
+
+void RenderObjectChildList::invalidateCounters(RenderObject* owner, const AtomicString& identifier)
+{
+ ASSERT(!owner->documentBeingDestroyed());
+ invalidateCountersInContainer(beforeAfterContainer(owner, BEFORE), identifier);
+ invalidateCountersInContainer(beforeAfterContainer(owner, AFTER), identifier);
+}
+
+void RenderObjectChildList::updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject)
+{
+ // Double check that the document did in fact use generated content rules. Otherwise we should not have been called.
+ ASSERT(owner->document()->usesBeforeAfterRules());
+
+ // In CSS2, before/after pseudo-content cannot nest. Check this first.
+ if (owner->style()->styleType() == BEFORE || owner->style()->styleType() == AFTER)
+ return;
+
+ if (!styledObject)
+ styledObject = owner;
+
+ RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type);
+ RenderObject* child = beforeAfterContainer(owner, type);
+
+ // Whether or not we currently have generated content attached.
+ bool oldContentPresent = child;
+
+ // Whether or not we now want generated content.
+ bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE;
+
+ // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate
+ // :after content and not :before content.
+ if (newContentWanted && type == BEFORE && owner->isElementContinuation())
+ newContentWanted = false;
+
+ // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object,
+ // then we don't generate the :after content.
+ if (newContentWanted && type == AFTER && owner->virtualContinuation())
+ newContentWanted = false;
+
+ // If we don't want generated content any longer, or if we have generated content, but it's no longer
+ // identical to the new content data we want to build render objects for, then we nuke all
+ // of the old generated content.
+ if (oldContentPresent && (!newContentWanted || Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) {
+ // Nuke the child.
+ if (child->style()->styleType() == type) {
+ oldContentPresent = false;
+ child->destroy();
+ child = (type == BEFORE) ? owner->virtualChildren()->firstChild() : owner->virtualChildren()->lastChild();
+ }
+ }
+
+ // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we
+ // have no generated content and can now return.
+ if (!newContentWanted)
+ return;
+
+ if (owner->isRenderInline() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE &&
+ !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition))
+ // According to the CSS2 spec (the end of section 12.1), the only allowed
+ // display values for the pseudo style are NONE and INLINE for inline flows.
+ // FIXME: CSS2.1 lifted this restriction, but block display types will crash.
+ // For now we at least relax the restriction to allow all inline types like inline-block
+ // and inline-table.
+ pseudoElementStyle->setDisplay(INLINE);
+
+ if (oldContentPresent) {
+ if (child && child->style()->styleType() == type) {
+ // We have generated content present still. We want to walk this content and update our
+ // style information with the new pseudo-element style.
+ child->setStyle(pseudoElementStyle);
+
+ RenderObject* beforeAfterParent = findBeforeAfterParent(child);
+ if (!beforeAfterParent)
+ return;
+
+ // Note that if we ever support additional types of generated content (which should be way off
+ // in the future), this code will need to be patched.
+ for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) {
+ if (genChild->isText())
+ // Generated text content is a child whose style also needs to be set to the pseudo-element style.
+ genChild->setStyle(pseudoElementStyle);
+ else if (genChild->isImage()) {
+ // Images get an empty style that inherits from the pseudo.
+ RefPtr<RenderStyle> style = RenderStyle::create();
+ style->inheritFrom(pseudoElementStyle);
+ genChild->setStyle(style.release());
+ } else {
+ // RenderListItem may insert a list marker here. We do not need to care about this case.
+ // Otherwise, genChild must be a first-letter container. updateFirstLetter() will take care of it.
+ ASSERT(genChild->isListMarker() || genChild->style()->styleType() == FIRST_LETTER);
+ }
+ }
+ }
+ return; // We've updated the generated content. That's all we needed to do.
+ }
+
+ RenderObject* insertBefore = (type == BEFORE) ? owner->virtualChildren()->firstChild() : 0;
+
+ // Generated content consists of a single container that houses multiple children (specified
+ // by the content property). This generated content container gets the pseudo-element style set on it.
+ RenderObject* generatedContentContainer = 0;
+
+ // Walk our list of generated content and create render objects for each.
+ for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->next()) {
+ RenderObject* renderer = 0;
+ switch (content->type()) {
+ case CONTENT_NONE:
+ break;
+ case CONTENT_TEXT:
+ renderer = new (owner->renderArena()) RenderTextFragment(owner->document() /* anonymous object */, content->text());
+ renderer->setStyle(pseudoElementStyle);
+ break;
+ case CONTENT_OBJECT: {
+ RenderImage* image = new (owner->renderArena()) RenderImage(owner->document()); // anonymous object
+ RefPtr<RenderStyle> style = RenderStyle::create();
+ style->inheritFrom(pseudoElementStyle);
+ image->setStyle(style.release());
+ if (StyleImage* styleImage = content->image())
+ image->setImageResource(RenderImageResourceStyleImage::create(styleImage));
+ else
+ image->setImageResource(RenderImageResource::create());
+ renderer = image;
+ break;
+ }
+ case CONTENT_COUNTER:
+ renderer = new (owner->renderArena()) RenderCounter(owner->document(), *content->counter());
+ renderer->setStyle(pseudoElementStyle);
+ break;
+ }
+
+ if (renderer) {
+ if (!generatedContentContainer) {
+ // Make a generated box that might be any display type now that we are able to drill down into children
+ // to find the original content properly.
+ generatedContentContainer = RenderObject::createObject(owner->document(), pseudoElementStyle);
+ generatedContentContainer->setStyle(pseudoElementStyle);
+ owner->addChild(generatedContentContainer, insertBefore);
+ }
+ if (generatedContentContainer->isChildAllowed(renderer, pseudoElementStyle))
+ generatedContentContainer->addChild(renderer);
+ else
+ renderer->destroy();
+ }
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderObjectChildList.h b/Source/WebCore/rendering/RenderObjectChildList.h
new file mode 100644
index 0000000..8b80f37
--- /dev/null
+++ b/Source/WebCore/rendering/RenderObjectChildList.h
@@ -0,0 +1,68 @@
+/*
+ * 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 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 RenderObjectChildList_h
+#define RenderObjectChildList_h
+
+#include "RenderStyleConstants.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class RenderObject;
+
+class RenderObjectChildList {
+public:
+ RenderObjectChildList()
+ : m_firstChild(0)
+ , m_lastChild(0)
+ {
+ }
+
+ RenderObject* firstChild() const { return m_firstChild; }
+ RenderObject* lastChild() const { return m_lastChild; }
+
+ // FIXME: Temporary while RenderBox still exists. Eventually this will just happen during insert/append/remove methods on the child list, and nobody
+ // will need to manipulate firstChild or lastChild directly.
+ void setFirstChild(RenderObject* child) { m_firstChild = child; }
+ void setLastChild(RenderObject* child) { m_lastChild = child; }
+
+ void destroyLeftoverChildren();
+
+ RenderObject* removeChildNode(RenderObject* owner, RenderObject*, bool fullRemove = true);
+ void appendChildNode(RenderObject* owner, RenderObject*, bool fullAppend = true);
+ void insertChildNode(RenderObject* owner, RenderObject* child, RenderObject* before, bool fullInsert = true);
+
+ void updateBeforeAfterContent(RenderObject* owner, PseudoId type, RenderObject* styledObject = 0);
+ void invalidateCounters(RenderObject* owner, const AtomicString& identifier);
+
+private:
+ RenderObject* m_firstChild;
+ RenderObject* m_lastChild;
+};
+
+} // namespace WebCore
+
+#endif // RenderObjectChildList_h
diff --git a/Source/WebCore/rendering/RenderOverflow.h b/Source/WebCore/rendering/RenderOverflow.h
new file mode 100644
index 0000000..7dc2bcb
--- /dev/null
+++ b/Source/WebCore/rendering/RenderOverflow.h
@@ -0,0 +1,163 @@
+/*
+ * 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 RenderOverflow_h
+#define RenderOverflow_h
+
+#include "IntRect.h"
+
+namespace WebCore
+{
+// RenderOverflow is a class for tracking content that spills out of a box. This class is used by RenderBox and
+// InlineFlowBox.
+//
+// There are two types of overflow: layout overflow (which is expected to be reachable via scrolling mechanisms) and
+// visual overflow (which is not expected to be reachable via scrolling mechanisms).
+//
+// Layout overflow examples include other boxes that spill out of our box, For example, in the inline case a tall image
+// could spill out of a line box.
+
+// Examples of visual overflow are shadows, text stroke (and eventually outline and border-image).
+
+// This object is allocated only when some of these fields have non-default values in the owning box.
+class RenderOverflow : public Noncopyable {
+public:
+ RenderOverflow(const IntRect& layoutRect, const IntRect& visualRect)
+ : m_topLayoutOverflow(layoutRect.y())
+ , m_bottomLayoutOverflow(layoutRect.bottom())
+ , m_leftLayoutOverflow(layoutRect.x())
+ , m_rightLayoutOverflow(layoutRect.right())
+ , m_topVisualOverflow(visualRect.y())
+ , m_bottomVisualOverflow(visualRect.bottom())
+ , m_leftVisualOverflow(visualRect.x())
+ , m_rightVisualOverflow(visualRect.right())
+ {
+ }
+
+ int topLayoutOverflow() const { return m_topLayoutOverflow; }
+ int bottomLayoutOverflow() const { return m_bottomLayoutOverflow; }
+ int leftLayoutOverflow() const { return m_leftLayoutOverflow; }
+ int rightLayoutOverflow() const { return m_rightLayoutOverflow; }
+ IntRect layoutOverflowRect() const;
+
+ int topVisualOverflow() const { return m_topVisualOverflow; }
+ int bottomVisualOverflow() const { return m_bottomVisualOverflow; }
+ int leftVisualOverflow() const { return m_leftVisualOverflow; }
+ int rightVisualOverflow() const { return m_rightVisualOverflow; }
+ IntRect visualOverflowRect() const;
+
+ void setTopLayoutOverflow(int overflow) { m_topLayoutOverflow = overflow; }
+ void setBottomLayoutOverflow(int overflow) { m_bottomLayoutOverflow = overflow; }
+ void setLeftLayoutOverflow(int overflow) { m_leftLayoutOverflow = overflow; }
+ void setRightLayoutOverflow(int overflow) { m_rightLayoutOverflow = overflow; }
+
+ void setTopVisualOverflow(int overflow) { m_topVisualOverflow = overflow; }
+ void setBottomVisualOverflow(int overflow) { m_bottomVisualOverflow = overflow; }
+ void setLeftVisualOverflow(int overflow) { m_leftVisualOverflow = overflow; }
+ void setRightVisualOverflow(int overflow) { m_rightVisualOverflow = overflow; }
+
+ void move(int dx, int dy);
+
+ void addLayoutOverflow(const IntRect&);
+ void addVisualOverflow(const IntRect&);
+
+ void setLayoutOverflow(const IntRect&);
+ void setVisualOverflow(const IntRect&);
+
+ void resetLayoutOverflow(const IntRect& defaultRect);
+
+private:
+ int m_topLayoutOverflow;
+ int m_bottomLayoutOverflow;
+ int m_leftLayoutOverflow;
+ int m_rightLayoutOverflow;
+
+ int m_topVisualOverflow;
+ int m_bottomVisualOverflow;
+ int m_leftVisualOverflow;
+ int m_rightVisualOverflow;
+};
+
+inline IntRect RenderOverflow::layoutOverflowRect() const
+{
+ return IntRect(m_leftLayoutOverflow, m_topLayoutOverflow, m_rightLayoutOverflow - m_leftLayoutOverflow, m_bottomLayoutOverflow - m_topLayoutOverflow);
+}
+
+inline IntRect RenderOverflow::visualOverflowRect() const
+{
+ return IntRect(m_leftVisualOverflow, m_topVisualOverflow, m_rightVisualOverflow - m_leftVisualOverflow, m_bottomVisualOverflow - m_topVisualOverflow);
+}
+
+inline void RenderOverflow::move(int dx, int dy)
+{
+ m_topLayoutOverflow += dy;
+ m_bottomLayoutOverflow += dy;
+ m_leftLayoutOverflow += dx;
+ m_rightLayoutOverflow += dx;
+
+ m_topVisualOverflow += dy;
+ m_bottomVisualOverflow += dy;
+ m_leftVisualOverflow += dx;
+ m_rightVisualOverflow += dx;
+}
+
+inline void RenderOverflow::addLayoutOverflow(const IntRect& rect)
+{
+ m_topLayoutOverflow = std::min(rect.y(), m_topLayoutOverflow);
+ m_bottomLayoutOverflow = std::max(rect.bottom(), m_bottomLayoutOverflow);
+ m_leftLayoutOverflow = std::min(rect.x(), m_leftLayoutOverflow);
+ m_rightLayoutOverflow = std::max(rect.right(), m_rightLayoutOverflow);
+}
+
+inline void RenderOverflow::addVisualOverflow(const IntRect& rect)
+{
+ m_topVisualOverflow = std::min(rect.y(), m_topVisualOverflow);
+ m_bottomVisualOverflow = std::max(rect.bottom(), m_bottomVisualOverflow);
+ m_leftVisualOverflow = std::min(rect.x(), m_leftVisualOverflow);
+ m_rightVisualOverflow = std::max(rect.right(), m_rightVisualOverflow);
+}
+
+inline void RenderOverflow::setLayoutOverflow(const IntRect& rect)
+{
+ m_topLayoutOverflow = rect.y();
+ m_bottomLayoutOverflow = rect.bottom();
+ m_leftLayoutOverflow = rect.x();
+ m_rightLayoutOverflow = rect.right();
+}
+
+inline void RenderOverflow::setVisualOverflow(const IntRect& rect)
+{
+ m_topVisualOverflow = rect.y();
+ m_bottomVisualOverflow = rect.bottom();
+ m_leftVisualOverflow = rect.x();
+ m_rightVisualOverflow = rect.right();
+}
+
+inline void RenderOverflow::resetLayoutOverflow(const IntRect& rect)
+{
+ m_topLayoutOverflow = rect.y();
+ m_bottomLayoutOverflow = rect.bottom();
+ m_leftLayoutOverflow = rect.x();
+ m_rightLayoutOverflow = rect.right();
+}
+
+} // namespace WebCore
+
+#endif // RenderOverflow_h
diff --git a/Source/WebCore/rendering/RenderPart.cpp b/Source/WebCore/rendering/RenderPart.cpp
new file mode 100644
index 0000000..3262961
--- /dev/null
+++ b/Source/WebCore/rendering/RenderPart.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Simon Hausmann <hausmann@kde.org>
+ * (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 "RenderPart.h"
+
+#include "RenderView.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "HTMLFrameElementBase.h"
+
+namespace WebCore {
+
+RenderPart::RenderPart(Element* node)
+ : RenderWidget(node)
+{
+ setInline(false);
+}
+
+RenderPart::~RenderPart()
+{
+ clearWidget();
+}
+
+void RenderPart::setWidget(PassRefPtr<Widget> widget)
+{
+ if (widget == this->widget())
+ return;
+
+ RenderWidget::setWidget(widget);
+
+ // make sure the scrollbars are set correctly for restore
+ // ### find better fix
+ viewCleared();
+}
+
+void RenderPart::viewCleared()
+{
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderPart.h b/Source/WebCore/rendering/RenderPart.h
new file mode 100644
index 0000000..18f2346
--- /dev/null
+++ b/Source/WebCore/rendering/RenderPart.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Simon Hausmann <hausmann@kde.org>
+ * 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 RenderPart_h
+#define RenderPart_h
+
+#include "RenderWidget.h"
+
+namespace WebCore {
+
+// Renderer for frames via RenderFrameBase, and plug-ins via RenderEmbeddedObject.
+class RenderPart : public RenderWidget {
+public:
+ RenderPart(Element*);
+ virtual ~RenderPart();
+
+ virtual void setWidget(PassRefPtr<Widget>);
+ virtual void viewCleared();
+
+private:
+ virtual bool isRenderPart() const { return true; }
+ virtual const char* renderName() const { return "RenderPart"; }
+};
+
+inline RenderPart* toRenderPart(RenderObject* object)
+{
+ ASSERT(!object || object->isRenderPart());
+ return static_cast<RenderPart*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderPart(const RenderPart*);
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderProgress.cpp b/Source/WebCore/rendering/RenderProgress.cpp
new file mode 100644
index 0000000..84de6fb
--- /dev/null
+++ b/Source/WebCore/rendering/RenderProgress.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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)
+
+#include "RenderProgress.h"
+
+#include "HTMLNames.h"
+#include "HTMLProgressElement.h"
+#include "RenderTheme.h"
+#include "ShadowElement.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/RefPtr.h>
+
+using namespace std;
+
+namespace WebCore {
+
+RenderProgress::RenderProgress(HTMLProgressElement* element)
+ : RenderIndicator(element)
+ , m_position(-1)
+ , m_animationStartTime(0)
+ , m_animationRepeatInterval(0)
+ , m_animationDuration(0)
+ , m_animating(false)
+ , m_animationTimer(this, &RenderProgress::animationTimerFired)
+{
+}
+
+RenderProgress::~RenderProgress()
+{
+ if (m_valuePart)
+ m_valuePart->detach();
+}
+
+void RenderProgress::updateFromElement()
+{
+ if (!m_valuePart) {
+ m_valuePart = ShadowBlockElement::createForPart(static_cast<HTMLElement*>(node()), PROGRESS_BAR_VALUE);
+ if (m_valuePart->renderer())
+ addChild(m_valuePart->renderer());
+ }
+
+ if (shouldHaveParts())
+ style()->setAppearance(NoControlPart);
+ else if (m_valuePart->renderer())
+ m_valuePart->renderer()->style()->setVisibility(HIDDEN);
+
+ HTMLProgressElement* element = progressElement();
+ if (m_position == element->position())
+ return;
+ m_position = element->position();
+
+ updateAnimationState();
+ RenderIndicator::updateFromElement();
+}
+
+double RenderProgress::animationProgress() const
+{
+ return m_animating ? (fmod((currentTime() - m_animationStartTime), m_animationDuration) / m_animationDuration) : 0;
+}
+
+bool RenderProgress::isDeterminate() const
+{
+ return 0 <= position();
+}
+
+void RenderProgress::animationTimerFired(Timer<RenderProgress>*)
+{
+ repaint();
+}
+
+void RenderProgress::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.phase == PaintPhaseBlockBackground) {
+ if (!m_animationTimer.isActive() && m_animating)
+ m_animationTimer.startOneShot(m_animationRepeatInterval);
+ }
+
+ RenderIndicator::paint(paintInfo, tx, ty);
+}
+
+void RenderProgress::layoutParts()
+{
+ m_valuePart->layoutAsPart(valuePartRect());
+ updateAnimationState();
+}
+
+bool RenderProgress::shouldHaveParts() const
+{
+ if (!style()->hasAppearance())
+ return true;
+ if (ShadowBlockElement::partShouldHaveStyle(this, PROGRESS_BAR_VALUE))
+ return true;
+ return false;
+}
+
+void RenderProgress::updateAnimationState()
+{
+ m_animationDuration = theme()->animationDurationForProgressBar(this);
+ m_animationRepeatInterval = theme()->animationRepeatIntervalForProgressBar(this);
+
+ bool animating = style()->hasAppearance() && m_animationDuration > 0;
+ if (animating == m_animating)
+ return;
+
+ m_animating = animating;
+ if (m_animating) {
+ m_animationStartTime = currentTime();
+ m_animationTimer.startOneShot(m_animationRepeatInterval);
+ } else
+ m_animationTimer.stop();
+}
+
+IntRect RenderProgress::valuePartRect() const
+{
+ IntRect rect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), lround((width() - borderLeft() - paddingLeft() - borderRight() - paddingRight()) * position()), height() - borderTop() - paddingTop() - borderBottom() - paddingBottom());
+ if (!style()->isLeftToRightDirection())
+ rect.setX(width() - borderRight() - paddingRight() - rect.width());
+ return rect;
+}
+
+HTMLProgressElement* RenderProgress::progressElement() const
+{
+ return static_cast<HTMLProgressElement*>(node());
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/RenderProgress.h b/Source/WebCore/rendering/RenderProgress.h
new file mode 100644
index 0000000..9ed5741
--- /dev/null
+++ b/Source/WebCore/rendering/RenderProgress.h
@@ -0,0 +1,83 @@
+/*
+ * 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 RenderProgress_h
+#define RenderProgress_h
+
+#if ENABLE(PROGRESS_TAG)
+#include "RenderBlock.h"
+#include "RenderIndicator.h"
+
+namespace WebCore {
+
+class HTMLProgressElement;
+class ShadowBlockElement;
+
+class RenderProgress : public RenderIndicator {
+public:
+ RenderProgress(HTMLProgressElement*);
+ virtual ~RenderProgress();
+
+ double position() const { return m_position; }
+ double animationProgress() const;
+ double animationStartTime() const { return m_animationStartTime; }
+
+ bool isDeterminate() const;
+
+ HTMLProgressElement* progressElement() const;
+
+private:
+ virtual const char* renderName() const { return "RenderProgress"; }
+ virtual bool isProgress() const { return true; }
+ virtual void updateFromElement();
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual void layoutParts();
+
+ IntRect valuePartRect() const;
+ bool shouldHaveParts() const;
+
+ void animationTimerFired(Timer<RenderProgress>*);
+ void updateAnimationState();
+
+ double m_position;
+ double m_animationStartTime;
+ double m_animationRepeatInterval;
+ double m_animationDuration;
+ bool m_animating;
+ Timer<RenderProgress> m_animationTimer;
+ RefPtr<ShadowBlockElement> m_valuePart;
+};
+
+inline RenderProgress* toRenderProgress(RenderObject* object)
+{
+ ASSERT(!object || object->isProgress());
+ return static_cast<RenderProgress*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderProgress(const RenderProgress*);
+
+} // namespace WebCore
+
+#endif
+
+#endif // RenderProgress_h
+
diff --git a/Source/WebCore/rendering/RenderReplaced.cpp b/Source/WebCore/rendering/RenderReplaced.cpp
new file mode 100644
index 0000000..974a8d0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderReplaced.cpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 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 "RenderReplaced.h"
+
+#include "GraphicsContext.h"
+#include "RenderBlock.h"
+#include "RenderLayer.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "VisiblePosition.h"
+
+using namespace std;
+
+namespace WebCore {
+
+const int cDefaultWidth = 300;
+const int cDefaultHeight = 150;
+
+RenderReplaced::RenderReplaced(Node* node)
+ : RenderBox(node)
+ , m_intrinsicSize(cDefaultWidth, cDefaultHeight)
+ , m_hasIntrinsicSize(false)
+{
+ setReplaced(true);
+}
+
+RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize)
+ : RenderBox(node)
+ , m_intrinsicSize(intrinsicSize)
+ , m_hasIntrinsicSize(true)
+{
+ setReplaced(true);
+}
+
+RenderReplaced::~RenderReplaced()
+{
+}
+
+void RenderReplaced::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBox::styleDidChange(diff, oldStyle);
+
+ bool hadStyle = (oldStyle != 0);
+ float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom();
+ if (style() && style()->effectiveZoom() != oldZoom)
+ intrinsicSizeChanged();
+}
+
+void RenderReplaced::layout()
+{
+ ASSERT(needsLayout());
+
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
+
+ setHeight(minimumReplacedHeight());
+
+ computeLogicalWidth();
+ computeLogicalHeight();
+
+ m_overflow.clear();
+ addShadowOverflow();
+ updateLayerTransform();
+
+ repainter.repaintAfterLayout();
+ setNeedsLayout(false);
+}
+
+void RenderReplaced::intrinsicSizeChanged()
+{
+ int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom());
+ int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom());
+ m_intrinsicSize = IntSize(scaledWidth, scaledHeight);
+ setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (!shouldPaint(paintInfo, tx, ty))
+ return;
+
+ tx += x();
+ ty += y();
+
+ if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
+ paintBoxDecorations(paintInfo, tx, ty);
+
+ if (paintInfo.phase == PaintPhaseMask) {
+ paintMask(paintInfo, tx, ty);
+ return;
+ }
+
+ if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
+ paintOutline(paintInfo.context, tx, ty, width(), height());
+
+ if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
+ return;
+
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return;
+
+ bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();
+ if (paintInfo.phase == PaintPhaseSelection) {
+ if (selectionState() == SelectionNone)
+ return;
+ drawSelectionTint = false;
+ }
+
+ bool completelyClippedOut = false;
+ if (style()->hasBorderRadius()) {
+ IntRect borderRect = IntRect(tx, ty, width(), height());
+
+ if (borderRect.isEmpty())
+ completelyClippedOut = true;
+ else {
+ // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
+ paintInfo.context->save();
+
+ IntSize topLeft, topRight, bottomLeft, bottomRight;
+ style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
+
+ paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
+ }
+ }
+
+ if (!completelyClippedOut) {
+ paintReplaced(paintInfo, tx, ty);
+
+ if (style()->hasBorderRadius())
+ paintInfo.context->restore();
+ }
+
+ // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
+ // surrounding content.
+ if (drawSelectionTint) {
+ IntRect selectionPaintingRect = localSelectionRect();
+ selectionPaintingRect.move(tx, ty);
+ paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
+ }
+}
+
+bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty)
+{
+ if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline
+ && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask)
+ return false;
+
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return false;
+
+ // if we're invisible or haven't received a layout yet, then just bail.
+ if (style()->visibility() != VISIBLE)
+ return false;
+
+ int currentTX = tx + x();
+ int currentTY = ty + y();
+
+ // Early exit if the element touches the edges.
+ int top = currentTY + topVisualOverflow();
+ int bottom = currentTY + bottomVisualOverflow();
+ if (isSelected() && m_inlineBoxWrapper) {
+ int selTop = ty + m_inlineBoxWrapper->root()->selectionTop();
+ int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
+ top = min(selTop, top);
+ bottom = max(selBottom, bottom);
+ }
+
+ int os = 2 * maximalOutlineSize(paintInfo.phase);
+ if (currentTX + leftVisualOverflow() >= paintInfo.rect.right() + os || currentTX + rightVisualOverflow() <= paintInfo.rect.x() - os)
+ return false;
+ if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os)
+ return false;
+
+ return true;
+}
+
+static inline bool lengthIsSpecified(Length length)
+{
+ LengthType lengthType = length.type();
+ return lengthType == Fixed || lengthType == Percent;
+}
+
+int RenderReplaced::computeReplacedLogicalWidth(bool includeMaxWidth) const
+{
+ int logicalWidth;
+ if (lengthIsSpecified(style()->width()))
+ logicalWidth = computeReplacedLogicalWidthUsing(style()->logicalWidth());
+ else if (m_hasIntrinsicSize)
+ logicalWidth = calcAspectRatioLogicalWidth();
+ else
+ logicalWidth = intrinsicLogicalWidth();
+
+ int minLogicalWidth = computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
+ int maxLogicalWidth = !includeMaxWidth || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
+
+ return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
+}
+
+int RenderReplaced::computeReplacedLogicalHeight() const
+{
+ int logicalHeight;
+ if (lengthIsSpecified(style()->logicalHeight()))
+ logicalHeight = computeReplacedLogicalHeightUsing(style()->logicalHeight());
+ else if (m_hasIntrinsicSize)
+ logicalHeight = calcAspectRatioLogicalHeight();
+ else
+ logicalHeight = intrinsicLogicalHeight();
+
+ int minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
+ int maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
+
+ return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
+}
+
+int RenderReplaced::calcAspectRatioLogicalWidth() const
+{
+ int intrinsicWidth = intrinsicLogicalWidth();
+ int intrinsicHeight = intrinsicLogicalHeight();
+ if (!intrinsicHeight)
+ return 0;
+ return RenderBox::computeReplacedLogicalHeight() * intrinsicWidth / intrinsicHeight;
+}
+
+int RenderReplaced::calcAspectRatioLogicalHeight() const
+{
+ int intrinsicWidth = intrinsicLogicalWidth();
+ int intrinsicHeight = intrinsicLogicalHeight();
+ if (!intrinsicWidth)
+ return 0;
+ return RenderBox::computeReplacedLogicalWidth() * intrinsicHeight / intrinsicWidth;
+}
+
+void RenderReplaced::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ int borderAndPadding = borderAndPaddingWidth();
+ m_maxPreferredLogicalWidth = computeReplacedLogicalWidth(false) + borderAndPadding;
+
+ if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
+ m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
+
+ if (style()->width().isPercent() || style()->height().isPercent()
+ || style()->maxWidth().isPercent() || style()->maxHeight().isPercent()
+ || style()->minWidth().isPercent() || style()->minHeight().isPercent())
+ m_minPreferredLogicalWidth = 0;
+ else
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
+
+ setPreferredLogicalWidthsDirty(false);
+}
+
+unsigned RenderReplaced::caretMaxRenderedOffset() const
+{
+ return 1;
+}
+
+VisiblePosition RenderReplaced::positionForPoint(const IntPoint& point)
+{
+ InlineBox* box = inlineBoxWrapper();
+ if (!box)
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ // FIXME: This code is buggy if the replaced element is relative positioned.
+
+ RootInlineBox* root = box->root();
+
+ int top = root->selectionTop();
+ int bottom = root->selectionBottom();
+
+ int blockDirectionPosition = box->isHorizontal() ? point.y() + y() : point.x() + x();
+ int lineDirectionPosition = box->isHorizontal() ? point.x() + x() : point.y() + y();
+
+ if (blockDirectionPosition < top)
+ return createVisiblePosition(caretMinOffset(), DOWNSTREAM); // coordinates are above
+
+ if (blockDirectionPosition >= bottom)
+ return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); // coordinates are below
+
+ if (node()) {
+ if (lineDirectionPosition <= box->logicalLeft() + (box->logicalWidth() / 2))
+ return createVisiblePosition(0, DOWNSTREAM);
+ return createVisiblePosition(1, DOWNSTREAM);
+ }
+
+ return RenderBox::positionForPoint(point);
+}
+
+IntRect RenderReplaced::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
+{
+ ASSERT(!needsLayout());
+
+ if (!isSelected())
+ return IntRect();
+
+ IntRect rect = localSelectionRect();
+ if (clipToVisibleContent)
+ computeRectForRepaint(repaintContainer, rect);
+ else
+ rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
+
+ return rect;
+}
+
+IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const
+{
+ if (checkWhetherSelected && !isSelected())
+ return IntRect();
+
+ if (!m_inlineBoxWrapper)
+ // We're a block-level replaced element. Just return our own dimensions.
+ return IntRect(0, 0, width(), height());
+
+ RootInlineBox* root = m_inlineBoxWrapper->root();
+ int newLogicalTop = root->block()->style()->isFlippedBlocksWritingMode() ? m_inlineBoxWrapper->logicalBottom() - root->selectionBottom() : root->selectionTop() - m_inlineBoxWrapper->logicalTop();
+ if (root->block()->style()->isHorizontalWritingMode())
+ return IntRect(0, newLogicalTop, width(), root->selectionHeight());
+ return IntRect(newLogicalTop, 0, root->selectionHeight(), height());
+}
+
+void RenderReplaced::setSelectionState(SelectionState s)
+{
+ RenderBox::setSelectionState(s); // The selection state for our containing block hierarchy is updated by the base class call.
+ if (m_inlineBoxWrapper) {
+ RootInlineBox* line = m_inlineBoxWrapper->root();
+ if (line)
+ line->setHasSelectedChildren(isSelected());
+ }
+}
+
+bool RenderReplaced::isSelected() const
+{
+ SelectionState s = selectionState();
+ if (s == SelectionNone)
+ return false;
+ if (s == SelectionInside)
+ return true;
+
+ int selectionStart, selectionEnd;
+ selectionStartEnd(selectionStart, selectionEnd);
+ if (s == SelectionStart)
+ return selectionStart == 0;
+
+ int end = node()->hasChildNodes() ? node()->childNodeCount() : 1;
+ if (s == SelectionEnd)
+ return selectionEnd == end;
+ if (s == SelectionBoth)
+ return selectionStart == 0 && selectionEnd == end;
+
+ ASSERT(0);
+ return false;
+}
+
+IntSize RenderReplaced::intrinsicSize() const
+{
+ return m_intrinsicSize;
+}
+
+void RenderReplaced::setIntrinsicSize(const IntSize& size)
+{
+ ASSERT(m_hasIntrinsicSize);
+ m_intrinsicSize = size;
+}
+
+IntRect RenderReplaced::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
+ return IntRect();
+
+ // The selectionRect can project outside of the overflowRect, so take their union
+ // for repainting to avoid selection painting glitches.
+ IntRect r = unionRect(localSelectionRect(false), 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);
+ if (v)
+ r.inflate(style()->outlineSize());
+ }
+ computeRectForRepaint(repaintContainer, r);
+ return r;
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderReplaced.h b/Source/WebCore/rendering/RenderReplaced.h
new file mode 100644
index 0000000..d6ebba6
--- /dev/null
+++ b/Source/WebCore/rendering/RenderReplaced.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 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 RenderReplaced_h
+#define RenderReplaced_h
+
+#include "RenderBox.h"
+
+namespace WebCore {
+
+class RenderReplaced : public RenderBox {
+public:
+ RenderReplaced(Node*);
+ RenderReplaced(Node*, const IntSize& intrinsicSize);
+ virtual ~RenderReplaced();
+
+protected:
+ virtual void layout();
+
+ virtual IntSize intrinsicSize() const;
+
+ virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const;
+ virtual int computeReplacedLogicalHeight() const;
+ virtual int minimumReplacedHeight() const { return 0; }
+
+ virtual void setSelectionState(SelectionState);
+
+ bool isSelected() const;
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ void setIntrinsicSize(const IntSize&);
+ virtual void intrinsicSizeChanged();
+ void setHasIntrinsicSize() { m_hasIntrinsicSize = true; }
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ bool shouldPaint(PaintInfo&, int& tx, int& ty);
+ IntRect localSelectionRect(bool checkWhetherSelected = true) const; // This is in local coordinates, but it's a physical rect (so the top left corner is physical top left).
+
+private:
+ virtual const char* renderName() const { return "RenderReplaced"; }
+
+ virtual bool canHaveChildren() const { return false; }
+
+ virtual void computePreferredLogicalWidths();
+
+ int calcAspectRatioLogicalWidth() const;
+ int calcAspectRatioLogicalHeight() const;
+
+ virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { }
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+
+ virtual unsigned caretMaxRenderedOffset() const;
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+
+ virtual bool canBeSelectionLeaf() const { return true; }
+
+ virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true);
+
+ IntSize m_intrinsicSize;
+ bool m_hasIntrinsicSize;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderReplica.cpp b/Source/WebCore/rendering/RenderReplica.cpp
new file mode 100644
index 0000000..c099d9d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderReplica.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "RenderReplica.h"
+
+#include "RenderLayer.h"
+
+namespace WebCore {
+
+RenderReplica::RenderReplica(Node* n)
+: RenderBox(n)
+{
+ // This is a hack. Replicas are synthetic, and don't pick up the attributes of the
+ // renderers being replicated, so they always report that they are inline, non-replaced.
+ // However, we need transforms to be applied to replicas for reflections, so have to pass
+ // the if (!isInline() || isReplaced()) check before setHasTransform().
+ setReplaced(true);
+}
+
+RenderReplica::~RenderReplica()
+{}
+
+void RenderReplica::layout()
+{
+ setFrameRect(parentBox()->borderBoxRect());
+ updateLayerTransform();
+ setNeedsLayout(false);
+}
+
+void RenderReplica::computePreferredLogicalWidths()
+{
+ m_minPreferredLogicalWidth = parentBox()->width();
+ m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth;
+ setPreferredLogicalWidthsDirty(false);
+}
+
+void RenderReplica::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseMask)
+ return;
+
+ tx += x();
+ ty += y();
+
+ if (paintInfo.phase == PaintPhaseForeground)
+ // Turn around and paint the parent layer. Use temporary clipRects, so that the layer doesn't end up caching clip rects
+ // computing using the wrong rootLayer
+ layer()->parent()->paintLayer(layer()->transform() ? layer()->parent() : layer()->enclosingTransformedAncestor(),
+ paintInfo.context, paintInfo.rect,
+ PaintBehaviorNormal, 0, 0,
+ RenderLayer::PaintLayerHaveTransparency | RenderLayer::PaintLayerAppliedTransform | RenderLayer::PaintLayerTemporaryClipRects | RenderLayer::PaintLayerPaintingReflection);
+ else if (paintInfo.phase == PaintPhaseMask)
+ paintMask(paintInfo, tx, ty);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderReplica.h b/Source/WebCore/rendering/RenderReplica.h
new file mode 100644
index 0000000..a7b03f6
--- /dev/null
+++ b/Source/WebCore/rendering/RenderReplica.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef RenderReplica_h
+#define RenderReplica_h
+
+#include "RenderBox.h"
+
+namespace WebCore {
+
+class RenderReplica : public RenderBox {
+public:
+ RenderReplica(Node*);
+ virtual ~RenderReplica();
+
+ virtual const char* renderName() const { return "RenderReplica"; }
+
+ virtual bool requiresLayer() const { return true; }
+
+ virtual void layout();
+ virtual void computePreferredLogicalWidths();
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+private:
+ virtual bool isReplica() const { return true; }
+
+};
+
+} // namespace WebCore
+
+#endif // RenderReplica_h
diff --git a/Source/WebCore/rendering/RenderRuby.cpp b/Source/WebCore/rendering/RenderRuby.cpp
new file mode 100644
index 0000000..1c5cfaf
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRuby.cpp
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2009 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 "RenderRuby.h"
+
+#include "RenderRubyRun.h"
+
+namespace WebCore {
+
+//=== generic helper functions to avoid excessive code duplication ===
+
+static RenderRubyRun* lastRubyRun(const RenderObject* ruby)
+{
+ RenderObject* child = ruby->lastChild();
+ if (child && ruby->isAfterContent(child))
+ child = child->previousSibling();
+ ASSERT(!child || child->isRubyRun() || child->isBeforeContent());
+ return child && child->isRubyRun() ? static_cast<RenderRubyRun*>(child) : 0;
+}
+
+static inline RenderRubyRun* findRubyRunParent(RenderObject* child)
+{
+ while (child && !child->isRubyRun())
+ child = child->parent();
+ return static_cast<RenderRubyRun*>(child);
+}
+
+//=== ruby as inline object ===
+
+RenderRubyAsInline::RenderRubyAsInline(Node* node)
+ : RenderInline(node)
+{
+}
+
+RenderRubyAsInline::~RenderRubyAsInline()
+{
+}
+
+bool RenderRubyAsInline::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isRubyText()
+ || child->isRubyRun()
+ || child->isInline();
+}
+
+void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ // Insert :before and :after content outside of ruby runs.
+ if (child->isBeforeContent() || child->isAfterContent()) {
+ RenderInline::addChild(child, beforeChild);
+ return;
+ }
+
+ // If the child is a ruby run, just add it normally.
+ if (child->isRubyRun()) {
+ RenderInline::addChild(child, beforeChild);
+ return;
+ }
+
+ if (beforeChild && !isAfterContent(beforeChild)) {
+ // insert child into run
+ ASSERT(!beforeChild->isRubyRun());
+ RenderObject* run = beforeChild;
+ while (run && !run->isRubyRun())
+ run = run->parent();
+ if (run) {
+ run->addChild(child, beforeChild);
+ return;
+ }
+ ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
+ // Emergency fallback: fall through and just append.
+ }
+
+ // If the new child would be appended, try to add the child to the previous run
+ // if possible, or create a new run otherwise.
+ // (The RenderRubyRun object will handle the details)
+ RenderRubyRun* lastRun = lastRubyRun(this);
+ if (!lastRun || lastRun->hasRubyText()) {
+ lastRun = RenderRubyRun::staticCreateRubyRun(this);
+ RenderInline::addChild(lastRun);
+ }
+ lastRun->addChild(child);
+}
+
+void RenderRubyAsInline::removeChild(RenderObject* child)
+{
+ // If the child's parent is *this (a ruby run or :before or :after content),
+ // just use the normal remove method.
+ if (child->isRubyRun() || child->isBeforeContent() || child->isAfterContent()) {
+ RenderInline::removeChild(child);
+ return;
+ }
+
+ // Otherwise find the containing run and remove it from there.
+ ASSERT(child->parent() != this);
+ RenderRubyRun* run = findRubyRunParent(child);
+ ASSERT(run);
+ run->removeChild(child);
+}
+
+
+//=== ruby as block object ===
+
+RenderRubyAsBlock::RenderRubyAsBlock(Node* node)
+ : RenderBlock(node)
+{
+}
+
+RenderRubyAsBlock::~RenderRubyAsBlock()
+{
+}
+
+bool RenderRubyAsBlock::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isRubyText()
+ || child->isRubyRun()
+ || child->isInline();
+}
+
+void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ // Insert :before and :after content outside of ruby runs.
+ if (child->isBeforeContent() || child->isAfterContent()) {
+ RenderBlock::addChild(child, beforeChild);
+ return;
+ }
+
+ // If the child is a ruby run, just add it normally.
+ if (child->isRubyRun()) {
+ RenderBlock::addChild(child, beforeChild);
+ return;
+ }
+
+ if (beforeChild && !isAfterContent(beforeChild)) {
+ // insert child into run
+ ASSERT(!beforeChild->isRubyRun());
+ RenderObject* run = beforeChild;
+ while (run && !run->isRubyRun())
+ run = run->parent();
+ if (run) {
+ run->addChild(child, beforeChild);
+ return;
+ }
+ ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
+ // Emergency fallback: fall through and just append.
+ }
+
+ // If the new child would be appended, try to add the child to the previous run
+ // if possible, or create a new run otherwise.
+ // (The RenderRubyRun object will handle the details)
+ RenderRubyRun* lastRun = lastRubyRun(this);
+ if (!lastRun || lastRun->hasRubyText()) {
+ lastRun = RenderRubyRun::staticCreateRubyRun(this);
+ RenderBlock::addChild(lastRun);
+ }
+ lastRun->addChild(child);
+}
+
+void RenderRubyAsBlock::removeChild(RenderObject* child)
+{
+ // If the child's parent is *this (a ruby run or :before or :after content),
+ // just use the normal remove method.
+ if (child->isRubyRun() || child->isBeforeContent() || child->isAfterContent()) {
+ RenderBlock::removeChild(child);
+ return;
+ }
+
+ // Otherwise find the containing run and remove it from there.
+ ASSERT(child->parent() != this);
+ RenderRubyRun* run = findRubyRunParent(child);
+ ASSERT(run);
+ run->removeChild(child);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderRuby.h b/Source/WebCore/rendering/RenderRuby.h
new file mode 100644
index 0000000..49a84d8
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRuby.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 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 RenderRuby_h
+#define RenderRuby_h
+
+#include "RenderBlock.h"
+#include "RenderInline.h"
+
+namespace WebCore {
+
+// Following the HTML 5 spec, the box object model for a <ruby> element allows several runs of ruby
+// bases with their respective ruby texts looks as follows:
+//
+// 1 RenderRuby object, corresponding to the whole <ruby> HTML element
+// 1+ RenderRubyRun (anonymous)
+// 0 or 1 RenderRubyText - shuffled to the front in order to re-use existing block layouting
+// 0-n inline object(s)
+// 0 or 1 RenderRubyBase - contains the inline objects that make up the ruby base
+// 1-n inline object(s)
+//
+// Note: <rp> elements are defined as having 'display:none' and thus normally are not assigned a renderer.
+
+// <ruby> when used as 'display:inline'
+class RenderRubyAsInline : public RenderInline {
+public:
+ RenderRubyAsInline(Node*);
+ virtual ~RenderRubyAsInline();
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+ virtual void removeChild(RenderObject* child);
+
+private:
+ virtual bool isRuby() const { return true; }
+ virtual const char* renderName() const { return "RenderRuby (inline)"; }
+ virtual bool createsAnonymousWrapper() const { return true; }
+ virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); }
+};
+
+// <ruby> when used as 'display:block' or 'display:inline-block'
+class RenderRubyAsBlock : public RenderBlock {
+public:
+ RenderRubyAsBlock(Node*);
+ virtual ~RenderRubyAsBlock();
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+ virtual void removeChild(RenderObject* child);
+
+private:
+ virtual bool isRuby() const { return true; }
+ virtual const char* renderName() const { return "RenderRuby (block)"; }
+ virtual bool createsAnonymousWrapper() const { return true; }
+ virtual void removeLeftoverAnonymousBlock(RenderBlock*) { ASSERT_NOT_REACHED(); }
+};
+
+} // namespace WebCore
+
+#endif // RenderRuby_h
diff --git a/Source/WebCore/rendering/RenderRubyBase.cpp b/Source/WebCore/rendering/RenderRubyBase.cpp
new file mode 100644
index 0000000..83399a1
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyBase.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2009 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 "RenderRubyBase.h"
+
+namespace WebCore {
+
+RenderRubyBase::RenderRubyBase(Node* node)
+ : RenderBlock(node)
+{
+ setInline(false);
+}
+
+RenderRubyBase::~RenderRubyBase()
+{
+}
+
+bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isInline();
+}
+
+bool RenderRubyBase::hasOnlyWrappedInlineChildren(RenderObject* beforeChild) const
+{
+ // Tests whether all children in the base before beforeChild are either floated/positioned,
+ // or inline objects wrapped in anonymous blocks.
+ // Note that beforeChild may be 0, in which case all children are looked at.
+ for (RenderObject* child = firstChild(); child != beforeChild; child = child->nextSibling()) {
+ if (!child->isFloatingOrPositioned() && !(child->isAnonymousBlock() && child->childrenInline()))
+ return false;
+ }
+ return true;
+}
+
+void RenderRubyBase::moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
+{
+ // This function removes all children that are before (!) beforeChild
+ // and appends them to toBase.
+ ASSERT(toBase);
+
+ // First make sure that beforeChild (if set) is indeed a direct child of this.
+ // Inline children might be wrapped in an anonymous block if there's a continuation.
+ // Theoretically, in ruby bases, this can happen with only the first such a child,
+ // so it should be OK to just climb the tree.
+ while (fromBeforeChild && fromBeforeChild->parent() != this)
+ fromBeforeChild = fromBeforeChild->parent();
+
+ if (childrenInline())
+ moveInlineChildren(toBase, fromBeforeChild);
+ else
+ moveBlockChildren(toBase, fromBeforeChild);
+
+ setNeedsLayoutAndPrefWidthsRecalc();
+ toBase->setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+void RenderRubyBase::moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
+{
+ RenderBlock* toBlock;
+
+ if (toBase->childrenInline()) {
+ // The standard and easy case: move the children into the target base
+ toBlock = toBase;
+ } else {
+ // We need to wrap the inline objects into an anonymous block.
+ // If toBase has a suitable block, we re-use it, otherwise create a new one.
+ RenderObject* lastChild = toBase->lastChild();
+ if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline())
+ toBlock = toRenderBlock(lastChild);
+ else {
+ toBlock = toBase->createAnonymousBlock();
+ toBase->children()->appendChildNode(toBase, toBlock);
+ }
+ }
+ // Move our inline children into the target block we determined above.
+ moveChildrenTo(toBlock, firstChild(), fromBeforeChild);
+}
+
+void RenderRubyBase::moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
+{
+ if (toBase->childrenInline()) {
+ // First check whether we move only wrapped inline objects.
+ if (hasOnlyWrappedInlineChildren(fromBeforeChild)) {
+ // The reason why the base is in block flow must be after beforeChild.
+ // We therefore can extract the inline objects and move them to toBase.
+ for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) {
+ if (child->isAnonymousBlock()) {
+ RenderBlock* anonBlock = toRenderBlock(child);
+ ASSERT(anonBlock->childrenInline());
+ ASSERT(!anonBlock->inlineElementContinuation());
+ anonBlock->moveAllChildrenTo(toBase, toBase->children());
+ anonBlock->deleteLineBoxTree();
+ anonBlock->destroy();
+ } else {
+ ASSERT(child->isFloatingOrPositioned());
+ moveChildTo(toBase, child);
+ }
+ }
+ } else {
+ // Moving block children -> have to set toBase as block flow
+ toBase->makeChildrenNonInline();
+ // Move children, potentially collapsing anonymous block wrappers.
+ mergeBlockChildren(toBase, fromBeforeChild);
+
+ // Now we need to check if the leftover children are all inline.
+ // If so, make this base inline again.
+ if (hasOnlyWrappedInlineChildren()) {
+ RenderObject* next = 0;
+ for (RenderObject* child = firstChild(); child; child = next) {
+ next = child->nextSibling();
+ if (child->isFloatingOrPositioned())
+ continue;
+ ASSERT(child->isAnonymousBlock());
+
+ RenderBlock* anonBlock = toRenderBlock(child);
+ ASSERT(anonBlock->childrenInline());
+ ASSERT(!anonBlock->inlineElementContinuation());
+ // Move inline children out of anonymous block.
+ anonBlock->moveAllChildrenTo(this, anonBlock);
+ anonBlock->deleteLineBoxTree();
+ anonBlock->destroy();
+ }
+ setChildrenInline(true);
+ }
+ }
+ } else
+ mergeBlockChildren(toBase, fromBeforeChild);
+}
+
+void RenderRubyBase::mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
+{
+ // This function removes all children that are before fromBeforeChild and appends them to toBase.
+ ASSERT(!childrenInline());
+ ASSERT(toBase);
+ ASSERT(!toBase->childrenInline());
+
+ // Quick check whether we have anything to do, to simplify the following code.
+ if (fromBeforeChild != firstChild())
+ return;
+
+ // If an anonymous block would be put next to another such block, then merge those.
+ RenderObject* firstChildHere = firstChild();
+ RenderObject* lastChildThere = toBase->lastChild();
+ if (firstChildHere && firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline()
+ && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) {
+ RenderBlock* anonBlockHere = toRenderBlock(firstChildHere);
+ RenderBlock* anonBlockThere = toRenderBlock(lastChildThere);
+ anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children());
+ anonBlockHere->deleteLineBoxTree();
+ anonBlockHere->destroy();
+ }
+ // Move all remaining children normally.
+ moveChildrenTo(toBase, firstChild(), fromBeforeChild);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderRubyBase.h b/Source/WebCore/rendering/RenderRubyBase.h
new file mode 100644
index 0000000..c029bd5
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyBase.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 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 RenderRubyBase_h
+#define RenderRubyBase_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class RenderRubyBase : public RenderBlock {
+public:
+ RenderRubyBase(Node*);
+ virtual ~RenderRubyBase();
+
+ virtual const char* renderName() const { return "RenderRubyBase (anonymous)"; }
+
+ virtual bool isRubyBase() const { return true; }
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+
+private:
+ bool hasOnlyWrappedInlineChildren(RenderObject* beforeChild = 0) const;
+
+ void moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0);
+ void moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0);
+ void moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0);
+ void mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild = 0);
+
+ // Allow RenderRubyRun to manipulate the children within ruby bases.
+ friend class RenderRubyRun;
+};
+
+} // namespace WebCore
+
+#endif // RenderRubyBase_h
diff --git a/Source/WebCore/rendering/RenderRubyRun.cpp b/Source/WebCore/rendering/RenderRubyRun.cpp
new file mode 100644
index 0000000..c12e543
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyRun.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2009 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 "RenderRubyRun.h"
+
+#include "RenderRubyBase.h"
+#include "RenderRubyText.h"
+#include "RenderView.h"
+
+using namespace std;
+
+namespace WebCore {
+
+RenderRubyRun::RenderRubyRun(Node* node)
+ : RenderBlock(node)
+ , m_beingDestroyed(false)
+{
+ setReplaced(true);
+ setInline(true);
+}
+
+RenderRubyRun::~RenderRubyRun()
+{
+}
+
+void RenderRubyRun::destroy()
+{
+ // Mark if the run is being destroyed to avoid trouble in removeChild().
+ m_beingDestroyed = true;
+ RenderBlock::destroy();
+}
+
+bool RenderRubyRun::hasRubyText() const
+{
+ // The only place where a ruby text can be is in the first position
+ // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
+ return firstChild() && firstChild()->isRubyText();
+}
+
+bool RenderRubyRun::hasRubyBase() const
+{
+ // The only place where a ruby base can be is in the last position
+ // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
+ return lastChild() && lastChild()->isRubyBase();
+}
+
+bool RenderRubyRun::isEmpty() const
+{
+ return !hasRubyText() && !hasRubyBase();
+}
+
+RenderRubyText* RenderRubyRun::rubyText() const
+{
+ RenderObject* child = firstChild();
+ return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
+}
+
+RenderRubyBase* RenderRubyRun::rubyBase() const
+{
+ RenderObject* child = lastChild();
+ return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
+}
+
+RenderRubyBase* RenderRubyRun::rubyBaseSafe()
+{
+ RenderRubyBase* base = rubyBase();
+ if (!base) {
+ base = createRubyBase();
+ RenderBlock::addChild(base);
+ }
+ return base;
+}
+
+RenderBlock* RenderRubyRun::firstLineBlock() const
+{
+ return 0;
+}
+
+void RenderRubyRun::updateFirstLetter()
+{
+}
+
+bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isRubyText() || child->isInline();
+}
+
+void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ ASSERT(child);
+
+ // If child is a ruby text
+ if (child->isRubyText()) {
+ if (!beforeChild) {
+ // RenderRuby has already ascertained that we can add the child here.
+ ASSERT(!hasRubyText());
+ // prepend ruby texts as first child
+ RenderBlock::addChild(child, firstChild());
+ } else if (beforeChild->isRubyText()) {
+ // New text is inserted just before another.
+ // In this case the new text takes the place of the old one, and
+ // the old text goes into a new run that is inserted as next sibling.
+ ASSERT(beforeChild->parent() == this);
+ RenderObject* ruby = parent();
+ ASSERT(ruby->isRuby());
+ RenderBlock* newRun = staticCreateRubyRun(ruby);
+ ruby->addChild(newRun, nextSibling());
+ // Add the new ruby text and move the old one to the new run
+ // Note: Doing it in this order and not using RenderRubyRun's methods,
+ // in order to avoid automatic removal of the ruby run in case there is no
+ // other child besides the old ruby text.
+ RenderBlock::addChild(child, beforeChild);
+ RenderBlock::removeChild(beforeChild);
+ newRun->addChild(beforeChild);
+ } else {
+ if (hasRubyBase()) {
+ // Insertion before a ruby base object.
+ // In this case we need insert a new run before the current one and split the base.
+ RenderObject* ruby = parent();
+ RenderRubyRun* newRun = staticCreateRubyRun(ruby);
+ ruby->addChild(newRun, this);
+ newRun->addChild(child);
+ rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
+ }
+ }
+ } else {
+ // child is not a text -> insert it into the base
+ // (append it instead if beforeChild is the ruby text)
+ if (beforeChild && beforeChild->isRubyText())
+ beforeChild = 0;
+ rubyBaseSafe()->addChild(child, beforeChild);
+ }
+}
+
+void RenderRubyRun::removeChild(RenderObject* child)
+{
+ // If the child is a ruby text, then merge the ruby base with the base of
+ // the right sibling run, if possible.
+ if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) {
+ RenderRubyBase* base = rubyBase();
+ RenderObject* rightNeighbour = nextSibling();
+ if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
+ // Ruby run without a base can happen only at the first run.
+ RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour);
+ if (rightRun->hasRubyBase()) {
+ RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
+ // Collect all children in a single base, then swap the bases.
+ rightBase->moveChildren(base);
+ moveChildTo(rightRun, base);
+ rightRun->moveChildTo(this, rightBase);
+ // The now empty ruby base will be removed below.
+ }
+ }
+ }
+
+ RenderBlock::removeChild(child);
+
+ if (!m_beingDestroyed && !documentBeingDestroyed()) {
+ // Check if our base (if any) is now empty. If so, destroy it.
+ RenderBlock* base = rubyBase();
+ if (base && !base->firstChild()) {
+ RenderBlock::removeChild(base);
+ base->deleteLineBoxTree();
+ base->destroy();
+ }
+
+ // If any of the above leaves the run empty, destroy it as well.
+ if (isEmpty()) {
+ parent()->removeChild(this);
+ deleteLineBoxTree();
+ destroy();
+ }
+ }
+}
+
+RenderRubyBase* RenderRubyRun::createRubyBase() const
+{
+ RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(BLOCK);
+ newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
+ rb->setStyle(newStyle.release());
+ return rb;
+}
+
+RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
+{
+ ASSERT(parentRuby && parentRuby->isRuby());
+ RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(parentRuby->style());
+ newStyle->setDisplay(INLINE_BLOCK);
+ rr->setStyle(newStyle.release());
+ return rr;
+}
+
+RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren)
+{
+ // Don't bother positioning the RenderRubyRun yet.
+ RenderRubyText* rt = rubyText();
+ if (!rt)
+ return 0;
+ if (relayoutChildren)
+ rt->setChildNeedsLayout(true, false);
+ rt->layoutIfNeeded();
+ return rt;
+}
+
+void RenderRubyRun::layout()
+{
+ RenderBlock::layout();
+
+ // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
+ RenderRubyText* rt = rubyText();
+ if (!rt)
+ return;
+
+ int lastLineRubyTextBottom = rt->logicalHeight();
+ int firstLineRubyTextTop = 0;
+ RootInlineBox* rootBox = rt->lastRootBox();
+ if (rootBox) {
+ // In order to align, we have to ignore negative leading.
+ firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
+ lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
+ }
+
+ if (!style()->isFlippedLinesWritingMode()) {
+ int firstLineTop = 0;
+ if (RenderRubyBase* rb = rubyBase()) {
+ RootInlineBox* rootBox = rb->firstRootBox();
+ if (rootBox)
+ firstLineTop = rootBox->logicalTopLayoutOverflow();
+ firstLineTop += rb->logicalTop();
+ }
+
+ rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
+ } else {
+ int lastLineBottom = logicalHeight();
+ if (RenderRubyBase* rb = rubyBase()) {
+ RootInlineBox* rootBox = rb->lastRootBox();
+ if (rootBox)
+ lastLineBottom = rootBox->logicalBottomLayoutOverflow();
+ lastLineBottom += rb->logicalTop();
+ }
+
+ rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
+ }
+
+ // Update our overflow to account for the new RenderRubyText position.
+ m_overflow.clear();
+ computeOverflow(clientLogicalBottom());
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderRubyRun.h b/Source/WebCore/rendering/RenderRubyRun.h
new file mode 100644
index 0000000..d844bff
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyRun.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 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 RenderRubyRun_h
+#define RenderRubyRun_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class RenderRubyBase;
+class RenderRubyText;
+
+// RenderRubyRun are 'inline-block/table' like objects,and wrap a single pairing of a ruby base with its ruby text(s).
+// See RenderRuby.h for further comments on the structure
+
+class RenderRubyRun : public RenderBlock {
+public:
+ RenderRubyRun(Node*);
+ virtual ~RenderRubyRun();
+
+ virtual void destroy();
+
+ bool hasRubyText() const;
+ bool hasRubyBase() const;
+ bool isEmpty() const;
+ RenderRubyText* rubyText() const;
+ RenderRubyBase* rubyBase() const;
+ RenderRubyBase* rubyBaseSafe(); // creates the base if it doesn't already exist
+
+ virtual RenderObject* layoutSpecialExcludedChild(bool relayoutChildren);
+ virtual void layout();
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+ virtual void removeChild(RenderObject* child);
+
+ virtual RenderBlock* firstLineBlock() const;
+ virtual void updateFirstLetter();
+
+ static RenderRubyRun* staticCreateRubyRun(const RenderObject* parentRuby);
+
+protected:
+ RenderRubyBase* createRubyBase() const;
+
+private:
+ virtual bool isRubyRun() const { return true; }
+ virtual const char* renderName() const { return "RenderRubyRun (anonymous)"; }
+ virtual bool createsAnonymousWrapper() const { return true; }
+ virtual void removeLeftoverAnonymousBlock(RenderBlock*) { }
+
+ bool m_beingDestroyed;
+};
+
+} // namespace WebCore
+
+#endif // RenderRubyRun_h
diff --git a/Source/WebCore/rendering/RenderRubyText.cpp b/Source/WebCore/rendering/RenderRubyText.cpp
new file mode 100644
index 0000000..14cf7fc
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyText.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 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 "RenderRubyText.h"
+
+namespace WebCore {
+
+RenderRubyText::RenderRubyText(Node* node)
+ : RenderBlock(node)
+{
+}
+
+RenderRubyText::~RenderRubyText()
+{
+}
+
+bool RenderRubyText::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isInline();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderRubyText.h b/Source/WebCore/rendering/RenderRubyText.h
new file mode 100644
index 0000000..e475914
--- /dev/null
+++ b/Source/WebCore/rendering/RenderRubyText.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 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 RenderRubyText_h
+#define RenderRubyText_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class RenderRubyText : public RenderBlock {
+public:
+ RenderRubyText(Node*);
+ virtual ~RenderRubyText();
+
+ virtual const char* renderName() const { return "RenderRubyText"; }
+
+ virtual bool isRubyText() const { return true; }
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+};
+
+} // namespace WebCore
+
+#endif // RenderRubyText_h
diff --git a/Source/WebCore/rendering/RenderSVGAllInOne.cpp b/Source/WebCore/rendering/RenderSVGAllInOne.cpp
new file mode 100644
index 0000000..7002747
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGAllInOne.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 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.
+ */
+
+// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+
+#include "RenderSVGBlock.cpp"
+#include "RenderSVGContainer.cpp"
+#include "RenderSVGGradientStop.cpp"
+#include "RenderSVGHiddenContainer.cpp"
+#include "RenderSVGImage.cpp"
+#include "RenderSVGModelObject.cpp"
+#include "RenderSVGResource.cpp"
+#include "RenderSVGResourceClipper.cpp"
+#include "RenderSVGResourceContainer.cpp"
+#include "RenderSVGResourceFilter.cpp"
+#include "RenderSVGResourceFilterPrimitive.cpp"
+#include "RenderSVGResourceGradient.cpp"
+#include "RenderSVGResourceLinearGradient.cpp"
+#include "RenderSVGResourceMarker.cpp"
+#include "RenderSVGResourceMasker.cpp"
+#include "RenderSVGResourcePattern.cpp"
+#include "RenderSVGResourceRadialGradient.cpp"
+#include "RenderSVGResourceSolidColor.cpp"
+#include "RenderSVGRoot.cpp"
+#include "RenderSVGShadowTreeRootContainer.cpp"
+#include "RenderSVGTransformableContainer.cpp"
+#include "RenderSVGViewportContainer.cpp"
+#include "SVGImageBufferTools.cpp"
+#include "SVGMarkerLayoutInfo.cpp"
+#include "SVGRenderSupport.cpp"
+#include "SVGRenderTreeAsText.cpp"
+#include "SVGResources.cpp"
+#include "SVGResourcesCache.cpp"
+#include "SVGResourcesCycleSolver.cpp"
+#include "SVGShadowTreeElements.cpp"
+
+// FIXME: As soon as all SVG renderers live in rendering/svg, this file should be moved there as well, removing the need for the svg/ includes below.
+#include "svg/RenderSVGInline.cpp"
+#include "svg/RenderSVGInlineText.cpp"
+#include "svg/RenderSVGTSpan.cpp"
+#include "svg/RenderSVGText.cpp"
+#include "svg/RenderSVGTextPath.cpp"
+#include "svg/SVGInlineFlowBox.cpp"
+#include "svg/SVGInlineTextBox.cpp"
+#include "svg/SVGRootInlineBox.cpp"
+#include "svg/SVGTextChunk.cpp"
+#include "svg/SVGTextChunkBuilder.cpp"
+#include "svg/SVGTextLayoutAttributes.cpp"
+#include "svg/SVGTextLayoutAttributesBuilder.cpp"
+#include "svg/SVGTextLayoutEngine.cpp"
+#include "svg/SVGTextLayoutEngineBaseline.cpp"
+#include "svg/SVGTextLayoutEngineSpacing.cpp"
+#include "svg/SVGTextMetrics.cpp"
+#include "svg/SVGTextQuery.cpp"
diff --git a/Source/WebCore/rendering/RenderSVGBlock.cpp b/Source/WebCore/rendering/RenderSVGBlock.cpp
new file mode 100644
index 0000000..b2d727a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGBlock.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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)
+#include "RenderSVGBlock.h"
+
+#include "RenderSVGResource.h"
+#include "SVGElement.h"
+
+namespace WebCore {
+
+RenderSVGBlock::RenderSVGBlock(SVGElement* node)
+ : RenderBlock(node)
+{
+}
+
+void RenderSVGBlock::setStyle(PassRefPtr<RenderStyle> style)
+{
+ RefPtr<RenderStyle> useStyle = style;
+
+ // SVG text layout code expects us to be a block-level style element.
+ if (useStyle->isDisplayInlineType()) {
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(useStyle.get());
+ newStyle->setDisplay(BLOCK);
+ useStyle = newStyle.release();
+ }
+
+ RenderBlock::setStyle(useStyle.release());
+}
+
+void RenderSVGBlock::updateBoxModelInfoFromStyle()
+{
+ RenderBlock::updateBoxModelInfoFromStyle();
+
+ // RenderSVGlock, used by Render(SVGText|ForeignObject), is not allowed to call setHasOverflowClip(true).
+ // RenderBlock assumes a layer to be present when the overflow clip functionality is requested. Both
+ // Render(SVGText|ForeignObject) return 'false' on 'requiresLayer'. Fine for RenderSVGText.
+ //
+ // If we want to support overflow rules for <foreignObject> we can choose between two solutions:
+ // a) make RenderForeignObject require layers and SVG layer aware
+ // b) reactor overflow logic out of RenderLayer (as suggested by dhyatt), which is a large task
+ //
+ // Until this is resolved, disable overflow support. Opera/FF don't support it as well at the moment (Feb 2010).
+ //
+ // Note: This does NOT affect overflow handling on outer/inner <svg> elements - this is handled
+ // manually by RenderSVGRoot - which owns the documents enclosing root layer and thus works fine.
+ setHasOverflowClip(false);
+}
+
+void RenderSVGBlock::absoluteRects(Vector<IntRect>&, int, int)
+{
+ // This code path should never be taken for SVG, as we're assuming useTransforms=true everywhere, absoluteQuads should be used.
+ ASSERT_NOT_REACHED();
+}
+
+void RenderSVGBlock::destroy()
+{
+ SVGResourcesCache::clientDestroyed(this);
+ RenderBlock::destroy();
+}
+
+void RenderSVGBlock::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (diff == StyleDifferenceLayout)
+ setNeedsBoundariesUpdate();
+ RenderBlock::styleWillChange(diff, newStyle);
+}
+
+void RenderSVGBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+ SVGResourcesCache::clientStyleChanged(this, diff, style());
+}
+
+void RenderSVGBlock::updateFromElement()
+{
+ RenderBlock::updateFromElement();
+ SVGResourcesCache::clientUpdatedFromElement(this, style());
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGBlock.h b/Source/WebCore/rendering/RenderSVGBlock.h
new file mode 100644
index 0000000..a9dd5db
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGBlock.h
@@ -0,0 +1,50 @@
+/*
+ * 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 RenderSVGBlock_h
+#define RenderSVGBlock_h
+
+#if ENABLE(SVG)
+#include "RenderBlock.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+class SVGElement;
+
+class RenderSVGBlock : public RenderBlock {
+public:
+ explicit RenderSVGBlock(SVGElement*);
+
+private:
+ virtual void setStyle(PassRefPtr<RenderStyle>);
+ virtual void updateBoxModelInfoFromStyle();
+
+ virtual void absoluteRects(Vector<IntRect>&, int tx, int ty);
+
+ virtual void destroy();
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void updateFromElement();
+};
+
+}
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGContainer.cpp b/Source/WebCore/rendering/RenderSVGContainer.cpp
new file mode 100644
index 0000000..32e0598
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGContainer.cpp
@@ -0,0 +1,184 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org>
+ 2007 Eric Seidel <eric@webkit.org>
+ Copyright (C) 2009 Google, Inc. All rights reserved.
+ 2009 Dirk Schulze <krit@webkit.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
+ 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+
+#include "GraphicsContext.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderView.h"
+#include "SVGRenderSupport.h"
+#include "SVGResources.h"
+#include "SVGStyledElement.h"
+
+namespace WebCore {
+
+RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node)
+ : RenderSVGModelObject(node)
+ , m_needsBoundariesUpdate(true)
+{
+}
+
+RenderSVGContainer::~RenderSVGContainer()
+{
+}
+
+void RenderSVGContainer::layout()
+{
+ ASSERT(needsLayout());
+
+ // RenderSVGRoot disables layoutState for the SVG rendering tree.
+ ASSERT(!view()->layoutStateEnabled());
+
+ // Allow RenderSVGViewportContainer to update its viewport.
+ calcViewport();
+
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout() || selfWillPaint());
+
+ // Allow RenderSVGTransformableContainer to update its transform.
+ bool updatedTransform = calculateLocalTransform();
+
+ SVGRenderSupport::layoutChildren(this, selfNeedsLayout());
+
+ // Invalidate all resources of this client if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ SVGResourcesCache::clientLayoutChanged(this);
+
+ // At this point LayoutRepainter already grabbed the old bounds,
+ // recalculate them now so repaintAfterLayout() uses the new bounds.
+ if (m_needsBoundariesUpdate || updatedTransform) {
+ updateCachedBoundaries();
+ m_needsBoundariesUpdate = false;
+
+ // If our bounds changed, notify the parents.
+ RenderSVGModelObject::setNeedsBoundariesUpdate();
+ }
+
+ repainter.repaintAfterLayout();
+ setNeedsLayout(false);
+}
+
+bool RenderSVGContainer::selfWillPaint()
+{
+#if ENABLE(FILTERS)
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
+ return resources && resources->filter();
+#else
+ return false;
+#endif
+}
+
+void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int)
+{
+ if (paintInfo.context->paintingDisabled())
+ return;
+
+ // Spec: groups w/o children still may render filter content.
+ if (!firstChild() && !selfWillPaint())
+ return;
+
+ FloatRect repaintRect = repaintRectInLocalCoordinates();
+ if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(repaintRect, localToParentTransform(), paintInfo))
+ return;
+
+ PaintInfo childPaintInfo(paintInfo);
+ childPaintInfo.context->save();
+
+ // Let the RenderSVGViewportContainer subclass clip if necessary
+ applyViewportClip(childPaintInfo);
+
+ childPaintInfo.applyTransform(localToParentTransform());
+
+ bool continueRendering = true;
+ if (childPaintInfo.phase == PaintPhaseForeground)
+ continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo);
+
+ if (continueRendering) {
+ childPaintInfo.updatePaintingRootForChildren(this);
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling())
+ child->paint(childPaintInfo, 0, 0);
+ }
+
+ if (paintInfo.phase == PaintPhaseForeground)
+ SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context);
+
+ childPaintInfo.context->restore();
+
+ // FIXME: This really should be drawn from local coordinates, but currently we hack it
+ // to avoid our clip killing our outline rect. Thus we translate our
+ // outline rect into parent coords before drawing.
+ // FIXME: This means our focus ring won't share our rotation like it should.
+ // We should instead disable our clip during PaintPhaseOutline
+ if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) {
+ IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRect));
+ paintOutline(paintInfo.context, paintRectInParent.x(), paintRectInParent.y(), paintRectInParent.width(), paintRectInParent.height());
+ }
+}
+
+// addFocusRingRects is called from paintOutline and needs to be in the same coordinates as the paintOuline call
+void RenderSVGContainer::addFocusRingRects(Vector<IntRect>& rects, int, int)
+{
+ IntRect paintRectInParent = enclosingIntRect(localToParentTransform().mapRect(repaintRectInLocalCoordinates()));
+ if (!paintRectInParent.isEmpty())
+ rects.append(paintRectInParent);
+}
+
+void RenderSVGContainer::updateCachedBoundaries()
+{
+ m_objectBoundingBox = FloatRect();
+ m_strokeBoundingBox = FloatRect();
+ m_repaintBoundingBox = FloatRect();
+
+ SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_strokeBoundingBox, m_repaintBoundingBox);
+ SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
+}
+
+bool RenderSVGContainer::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
+{
+ // Give RenderSVGViewportContainer a chance to apply its viewport clip
+ if (!pointIsInsideViewportClip(pointInParent))
+ return false;
+
+ FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
+
+ if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
+ return false;
+
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
+ updateHitTestResult(result, roundedIntPoint(localPoint));
+ return true;
+ }
+ }
+
+ // Spec: Only graphical elements can be targeted by the mouse, period.
+ // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched."
+ return false;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGContainer.h b/Source/WebCore/rendering/RenderSVGContainer.h
new file mode 100644
index 0000000..3bd5346
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGContainer.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007 Rob Buis <buis@kde.org>
+ Copyright (C) 2009 Google, Inc. All rights reserved.
+ Copyright (C) 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
+ 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 RenderSVGContainer_h
+#define RenderSVGContainer_h
+
+#if ENABLE(SVG)
+
+#include "RenderSVGModelObject.h"
+
+namespace WebCore {
+
+class SVGElement;
+
+class RenderSVGContainer : public RenderSVGModelObject {
+public:
+ explicit RenderSVGContainer(SVGStyledElement*);
+ virtual ~RenderSVGContainer();
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ virtual void paint(PaintInfo&, int parentX, int parentY);
+ virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
+
+protected:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual bool isSVGContainer() const { return true; }
+ virtual const char* renderName() const { return "RenderSVGContainer"; }
+
+ virtual void layout();
+
+ virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
+
+ virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; }
+ virtual FloatRect strokeBoundingBox() const { return m_strokeBoundingBox; }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
+
+ virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+
+ // Allow RenderSVGTransformableContainer to hook in at the right time in layout()
+ virtual bool calculateLocalTransform() { return false; }
+
+ // Allow RenderSVGViewportContainer to hook in at the right times in layout(), paint() and nodeAtFloatPoint()
+ virtual void calcViewport() { }
+ virtual void applyViewportClip(PaintInfo&) { }
+ virtual bool pointIsInsideViewportClip(const FloatPoint& /*pointInParent*/) { return true; }
+
+ bool selfWillPaint();
+ void updateCachedBoundaries();
+
+private:
+ RenderObjectChildList m_children;
+ FloatRect m_objectBoundingBox;
+ FloatRect m_strokeBoundingBox;
+ FloatRect m_repaintBoundingBox;
+ bool m_needsBoundariesUpdate : 1;
+};
+
+inline RenderSVGContainer* toRenderSVGContainer(RenderObject* object)
+{
+ // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this.
+ ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")));
+ return static_cast<RenderSVGContainer*>(object);
+}
+
+inline const RenderSVGContainer* toRenderSVGContainer(const RenderObject* object)
+{
+ // Note: isSVGContainer is also true for RenderSVGViewportContainer, which is not derived from this.
+ ASSERT(!object || (object->isSVGContainer() && strcmp(object->renderName(), "RenderSVGViewportContainer")));
+ return static_cast<const RenderSVGContainer*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGContainer(const RenderSVGContainer*);
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGContainer_h
diff --git a/Source/WebCore/rendering/RenderSVGGradientStop.cpp b/Source/WebCore/rendering/RenderSVGGradientStop.cpp
new file mode 100644
index 0000000..094fc7f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGGradientStop.cpp
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.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.
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGGradientStop.h"
+
+#include "RenderSVGResourceContainer.h"
+#include "SVGGradientElement.h"
+#include "SVGNames.h"
+#include "SVGResourcesCache.h"
+#include "SVGStopElement.h"
+
+namespace WebCore {
+
+using namespace SVGNames;
+
+RenderSVGGradientStop::RenderSVGGradientStop(SVGStopElement* element)
+ : RenderObject(element)
+{
+}
+
+RenderSVGGradientStop::~RenderSVGGradientStop()
+{
+}
+
+void RenderSVGGradientStop::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderObject::styleDidChange(diff, oldStyle);
+ if (diff == StyleDifferenceEqual)
+ return;
+
+ // <stop> elements should only be allowed to make renderers under gradient elements
+ // but I can imagine a few cases we might not be catching, so let's not crash if our parent isn't a gradient.
+ SVGGradientElement* gradient = gradientElement();
+ if (!gradient)
+ return;
+
+ RenderObject* renderer = gradient->renderer();
+ if (!renderer)
+ return;
+
+ ASSERT(renderer->isSVGResourceContainer());
+ RenderSVGResourceContainer* container = renderer->toRenderSVGResourceContainer();
+ container->removeAllClientsFromCache();
+}
+
+void RenderSVGGradientStop::layout()
+{
+ setNeedsLayout(false);
+}
+
+SVGGradientElement* RenderSVGGradientStop::gradientElement() const
+{
+ ContainerNode* parentNode = node()->parentNode();
+ if (parentNode->hasTagName(linearGradientTag) || parentNode->hasTagName(radialGradientTag))
+ return static_cast<SVGGradientElement*>(parentNode);
+ return 0;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGGradientStop.h b/Source/WebCore/rendering/RenderSVGGradientStop.h
new file mode 100644
index 0000000..f06a9a5
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGGradientStop.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ * 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 RenderSVGGradientStop_h
+#define RenderSVGGradientStop_h
+
+#if ENABLE(SVG)
+#include "RenderObject.h"
+
+namespace WebCore {
+
+class SVGGradientElement;
+class SVGStopElement;
+
+// This class exists mostly so we can hear about gradient stop style changes
+class RenderSVGGradientStop : public RenderObject {
+public:
+ RenderSVGGradientStop(SVGStopElement*);
+ virtual ~RenderSVGGradientStop();
+
+ virtual bool isSVGGradientStop() const { return true; }
+ virtual const char* renderName() const { return "RenderSVGGradientStop"; }
+
+ virtual void layout();
+
+ // This overrides are needed to prevent ASSERTs on <svg><stop /></svg>
+ // RenderObject's default implementations ASSERT_NOT_REACHED()
+ // https://bugs.webkit.org/show_bug.cgi?id=20400
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject*) { return IntRect(); }
+ virtual FloatRect objectBoundingBox() const { return FloatRect(); }
+ virtual FloatRect strokeBoundingBox() const { return FloatRect(); }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); }
+
+protected:
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+private:
+ SVGGradientElement* gradientElement() const;
+};
+
+inline const RenderSVGGradientStop* toRenderSVGGradientStop(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGGradientStop());
+ return static_cast<const RenderSVGGradientStop*>(object);
+}
+
+}
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGGradientStop_h
diff --git a/Source/WebCore/rendering/RenderSVGHiddenContainer.cpp b/Source/WebCore/rendering/RenderSVGHiddenContainer.cpp
new file mode 100644
index 0000000..fb14ffe
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGHiddenContainer.cpp
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.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.
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGHiddenContainer.h"
+
+#include "RenderSVGPath.h"
+#include "SVGStyledElement.h"
+
+namespace WebCore {
+
+RenderSVGHiddenContainer::RenderSVGHiddenContainer(SVGStyledElement* element)
+ : RenderSVGContainer(element)
+{
+}
+
+void RenderSVGHiddenContainer::layout()
+{
+ ASSERT(needsLayout());
+ SVGRenderSupport::layoutChildren(this, selfNeedsLayout());
+ setNeedsLayout(false);
+}
+
+void RenderSVGHiddenContainer::paint(PaintInfo&, int, int)
+{
+ // This subtree does not paint.
+}
+
+void RenderSVGHiddenContainer::absoluteQuads(Vector<FloatQuad>&)
+{
+ // This subtree does not take up space or paint
+}
+
+bool RenderSVGHiddenContainer::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction)
+{
+ return false;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGHiddenContainer.h b/Source/WebCore/rendering/RenderSVGHiddenContainer.h
new file mode 100644
index 0000000..c591a88
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGHiddenContainer.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.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 RenderSVGHiddenContainer_h
+#define RenderSVGHiddenContainer_h
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+
+namespace WebCore {
+
+ class SVGStyledElement;
+
+ // This class is for containers which are never drawn, but do need to support style
+ // <defs>, <linearGradient>, <radialGradient> are all good examples
+ class RenderSVGHiddenContainer : public RenderSVGContainer {
+ public:
+ explicit RenderSVGHiddenContainer(SVGStyledElement*);
+
+ virtual const char* renderName() const { return "RenderSVGHiddenContainer"; }
+
+ protected:
+ virtual void layout();
+
+ private:
+ virtual bool isSVGHiddenContainer() const { return true; }
+ virtual bool requiresLayer() const { return false; }
+
+ virtual void paint(PaintInfo&, int parentX, int parentY);
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject*) { return IntRect(); }
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+ };
+}
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGHiddenContainer_h
diff --git a/Source/WebCore/rendering/RenderSVGImage.cpp b/Source/WebCore/rendering/RenderSVGImage.cpp
new file mode 100644
index 0000000..237dbaa
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGImage.cpp
@@ -0,0 +1,195 @@
+/*
+ Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
+ Copyright (C) 2006 Apple Computer, Inc.
+ Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ Copyright (C) 2007, 2008, 2009 Rob Buis <buis@kde.org>
+ Copyright (C) 2009, Google, Inc.
+ Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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.
+*/
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGImage.h"
+
+#include "Attr.h"
+#include "FloatConversion.h"
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "PointerEventsHitRules.h"
+#include "RenderImageResource.h"
+#include "RenderLayer.h"
+#include "RenderSVGResourceContainer.h"
+#include "RenderSVGResourceFilter.h"
+#include "SVGImageElement.h"
+#include "SVGLength.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGRenderSupport.h"
+#include "SVGResources.h"
+
+namespace WebCore {
+
+RenderSVGImage::RenderSVGImage(SVGImageElement* impl)
+ : RenderSVGModelObject(impl)
+ , m_updateCachedRepaintRect(true)
+ , m_needsTransformUpdate(true)
+ , m_imageResource(RenderImageResource::create())
+{
+ m_imageResource->initialize(this);
+}
+
+RenderSVGImage::~RenderSVGImage()
+{
+ m_imageResource->shutdown();
+}
+
+void RenderSVGImage::layout()
+{
+ ASSERT(needsLayout());
+
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
+ SVGImageElement* image = static_cast<SVGImageElement*>(node());
+
+ bool transformOrBoundariesUpdate = m_needsTransformUpdate || m_updateCachedRepaintRect;
+ if (m_needsTransformUpdate) {
+ m_localTransform = image->animatedLocalTransform();
+ m_needsTransformUpdate = false;
+ }
+
+ if (m_updateCachedRepaintRect) {
+ m_repaintBoundingBox = m_objectBoundingBox;
+ SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
+ m_updateCachedRepaintRect = false;
+ }
+
+ // Invalidate all resources of this client if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ SVGResourcesCache::clientLayoutChanged(this);
+
+ // If our bounds changed, notify the parents.
+ if (transformOrBoundariesUpdate)
+ RenderSVGModelObject::setNeedsBoundariesUpdate();
+
+ repainter.repaintAfterLayout();
+ setNeedsLayout(false);
+}
+
+void RenderSVGImage::updateFromElement()
+{
+ SVGImageElement* image = static_cast<SVGImageElement*>(node());
+
+ FloatRect oldBoundaries = m_objectBoundingBox;
+ m_objectBoundingBox = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image));
+ if (m_objectBoundingBox != oldBoundaries) {
+ m_updateCachedRepaintRect = true;
+ setNeedsLayout(true);
+ }
+ RenderSVGModelObject::updateFromElement();
+}
+
+void RenderSVGImage::paint(PaintInfo& paintInfo, int, int)
+{
+ if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || !m_imageResource->hasImage())
+ return;
+
+ FloatRect boundingBox = repaintRectInLocalCoordinates();
+ if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
+ return;
+
+ PaintInfo childPaintInfo(paintInfo);
+ bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
+ if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
+ childPaintInfo.context->save();
+ childPaintInfo.applyTransform(m_localTransform);
+
+ if (childPaintInfo.phase == PaintPhaseForeground) {
+ PaintInfo savedInfo(childPaintInfo);
+
+ if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
+ Image* image = m_imageResource->image();
+ FloatRect destRect = m_objectBoundingBox;
+ FloatRect srcRect(0, 0, image->width(), image->height());
+
+ SVGImageElement* imageElement = static_cast<SVGImageElement*>(node());
+ if (imageElement->preserveAspectRatio().align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE)
+ imageElement->preserveAspectRatio().transformRect(destRect, srcRect);
+
+ childPaintInfo.context->drawImage(image, ColorSpaceDeviceRGB, destRect, srcRect);
+ }
+
+ SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
+ }
+
+ if (drawsOutline)
+ paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
+ static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()));
+
+ childPaintInfo.context->restore();
+ }
+}
+
+bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
+{
+ // We only draw in the forground phase, so we only hit-test then.
+ if (hitTestAction != HitTestForeground)
+ return false;
+
+ PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
+ bool isVisible = (style()->visibility() == VISIBLE);
+ if (isVisible || !hitRules.requireVisible) {
+ FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
+
+ if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
+ return false;
+
+ if (hitRules.canHitFill) {
+ if (m_objectBoundingBox.contains(localPoint)) {
+ updateHitTestResult(result, roundedIntPoint(localPoint));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void RenderSVGImage::imageChanged(WrappedImagePtr, const IntRect*)
+{
+ // The image resource defaults to nullImage until the resource arrives.
+ // This empty image may be cached by SVG resources which must be invalidated.
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this))
+ resources->removeClientFromCache(this);
+
+ // Eventually notify parent resources, that we've changed.
+ RenderSVGResource::markForLayoutAndParentResourceInvalidation(this, false);
+
+ repaint();
+}
+
+void RenderSVGImage::addFocusRingRects(Vector<IntRect>& rects, int, int)
+{
+ // this is called from paint() after the localTransform has already been applied
+ IntRect contentRect = enclosingIntRect(repaintRectInLocalCoordinates());
+ if (!contentRect.isEmpty())
+ rects.append(contentRect);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGImage.h b/Source/WebCore/rendering/RenderSVGImage.h
new file mode 100644
index 0000000..485d6ab
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGImage.h
@@ -0,0 +1,99 @@
+/*
+ Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
+ Copyright (C) 2006, 2009 Apple Inc. All rights reserved.
+ Copyright (C) 2007 Rob Buis <buis@kde.org>
+ Copyright (C) 2009 Google, Inc.
+ Copyright (C) 2010 Patrick Gansterer <paroga@paroga.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 RenderSVGImage_h
+#define RenderSVGImage_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "RenderSVGModelObject.h"
+#include "SVGPreserveAspectRatio.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+class RenderImageResource;
+class SVGImageElement;
+
+class RenderSVGImage : public RenderSVGModelObject {
+public:
+ RenderSVGImage(SVGImageElement*);
+ virtual ~RenderSVGImage();
+
+ virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+ virtual void updateFromElement();
+
+ RenderImageResource* imageResource() { return m_imageResource.get(); }
+ const RenderImageResource* imageResource() const { return m_imageResource.get(); }
+
+private:
+ virtual const char* renderName() const { return "RenderSVGImage"; }
+ virtual bool isSVGImage() const { return true; }
+
+ virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
+
+ virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; }
+ virtual FloatRect strokeBoundingBox() const { return m_objectBoundingBox; }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
+
+ virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
+
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+ virtual void layout();
+ virtual void paint(PaintInfo&, int parentX, int parentY);
+
+ virtual bool requiresLayer() const { return false; }
+
+ virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+
+ virtual AffineTransform localTransform() const { return m_localTransform; }
+
+ bool m_updateCachedRepaintRect : 1;
+ bool m_needsTransformUpdate : 1;
+ AffineTransform m_localTransform;
+ FloatRect m_objectBoundingBox;
+ FloatRect m_repaintBoundingBox;
+ OwnPtr<RenderImageResource> m_imageResource;
+};
+
+inline RenderSVGImage* toRenderSVGImage(RenderObject* object)
+{
+ ASSERT(!object || object->isSVGImage());
+ return static_cast<RenderSVGImage*>(object);
+}
+
+inline const RenderSVGImage* toRenderSVGImage(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGImage());
+ return static_cast<const RenderSVGImage*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGImage(const RenderSVGImage*);
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGImage_h
diff --git a/Source/WebCore/rendering/RenderSVGModelObject.cpp b/Source/WebCore/rendering/RenderSVGModelObject.cpp
new file mode 100644
index 0000000..28760a0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGModelObject.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2009, 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGModelObject.h"
+
+#include "RenderSVGResource.h"
+#include "SVGStyledElement.h"
+
+namespace WebCore {
+
+RenderSVGModelObject::RenderSVGModelObject(SVGStyledElement* node)
+ : RenderObject(node)
+{
+}
+
+IntRect RenderSVGModelObject::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
+}
+
+void RenderSVGModelObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+ SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
+}
+
+void RenderSVGModelObject::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const
+{
+ SVGRenderSupport::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState);
+}
+
+// Copied from RenderBox, this method likely requires further refactoring to work easily for both SVG and CSS Box Model content.
+// FIXME: This may also need to move into SVGRenderSupport as the RenderBox version depends
+// on borderBoundingBox() which SVG RenderBox subclases (like SVGRenderBlock) do not implement.
+IntRect RenderSVGModelObject::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint*) const
+{
+ IntRect box = enclosingIntRect(repaintRectInLocalCoordinates());
+ adjustRectForOutlineAndShadow(box);
+
+ FloatQuad containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
+ return containerRelativeQuad.enclosingBoundingBox();
+}
+
+void RenderSVGModelObject::absoluteRects(Vector<IntRect>&, int, int)
+{
+ // This code path should never be taken for SVG, as we're assuming useTransforms=true everywhere, absoluteQuads should be used.
+ ASSERT_NOT_REACHED();
+}
+
+void RenderSVGModelObject::absoluteQuads(Vector<FloatQuad>& quads)
+{
+ quads.append(localToAbsoluteQuad(strokeBoundingBox()));
+}
+
+void RenderSVGModelObject::destroy()
+{
+ SVGResourcesCache::clientDestroyed(this);
+ RenderObject::destroy();
+}
+
+void RenderSVGModelObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (diff == StyleDifferenceLayout)
+ setNeedsBoundariesUpdate();
+ RenderObject::styleWillChange(diff, newStyle);
+}
+
+void RenderSVGModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderObject::styleDidChange(diff, oldStyle);
+ SVGResourcesCache::clientStyleChanged(this, diff, style());
+}
+
+void RenderSVGModelObject::updateFromElement()
+{
+ RenderObject::updateFromElement();
+ SVGResourcesCache::clientUpdatedFromElement(this, style());
+}
+
+bool RenderSVGModelObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction)
+{
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGModelObject.h b/Source/WebCore/rendering/RenderSVGModelObject.h
new file mode 100644
index 0000000..fb8f89f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGModelObject.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009, 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 RenderSVGModelObject_h
+#define RenderSVGModelObject_h
+
+#if ENABLE(SVG)
+
+#include "RenderObject.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+// Most renderers in the SVG rendering tree will inherit from this class
+// but not all. (e.g. RenderSVGForeignObject, RenderSVGBlock) thus methods
+// required by SVG renders need to be declared on RenderObject, but shared
+// logic can go in this class or in SVGRenderSupport.
+
+class SVGStyledElement;
+
+class RenderSVGModelObject : public RenderObject {
+public:
+ explicit RenderSVGModelObject(SVGStyledElement*);
+
+ virtual bool requiresLayer() const { return false; }
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+ virtual IntRect outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer, IntPoint*) const;
+
+ virtual void absoluteRects(Vector<IntRect>&, int tx, int ty);
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ virtual void destroy();
+
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void updateFromElement();
+
+private:
+ // This method should never be called, SVG uses a different nodeAtPoint method
+ bool nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xInContainer, int yInContainer, int dxParentToContainer, int dyParentToContainer, HitTestAction hitTestAction);
+};
+
+}
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResource.cpp b/Source/WebCore/rendering/RenderSVGResource.cpp
new file mode 100644
index 0000000..f4c65d5
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResource.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2007 Rob Buis <buis@kde.org>
+ * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
+ * 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)
+#include "RenderSVGResource.h"
+
+#include "RenderSVGResourceContainer.h"
+#include "RenderSVGResourceSolidColor.h"
+#include "SVGResources.h"
+#include "SVGURIReference.h"
+
+namespace WebCore {
+
+static inline RenderSVGResource* requestPaintingResource(RenderSVGResourceMode mode, RenderObject* object, const RenderStyle* style, Color& fallbackColor)
+{
+ ASSERT(object);
+ ASSERT(style);
+
+ // If we have no style at all, ignore it.
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ if (!svgStyle)
+ return 0;
+
+ // If we have no fill/stroke, return 0.
+ if (mode == ApplyToFillMode) {
+ if (!svgStyle->hasFill())
+ return 0;
+ } else {
+ if (!svgStyle->hasStroke())
+ return 0;
+ }
+
+ SVGPaint* paint = mode == ApplyToFillMode ? svgStyle->fillPaint() : svgStyle->strokePaint();
+ ASSERT(paint);
+
+ SVGPaint::SVGPaintType paintType = paint->paintType();
+ if (paintType == SVGPaint::SVG_PAINTTYPE_NONE)
+ return 0;
+
+ Color color;
+ if (paintType == SVGPaint::SVG_PAINTTYPE_RGBCOLOR
+ || paintType == SVGPaint::SVG_PAINTTYPE_RGBCOLOR_ICCCOLOR
+ || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR
+ || paintType == SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR_ICCCOLOR)
+ color = paint->color();
+ else if (paintType == SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR || paintType == SVGPaint::SVG_PAINTTYPE_URI_CURRENTCOLOR)
+ color = style->visitedDependentColor(CSSPropertyColor);
+
+ if (style->insideLink() == InsideVisitedLink) {
+ RenderStyle* visitedStyle = style->getCachedPseudoStyle(VISITED_LINK);
+ ASSERT(visitedStyle);
+
+ if (SVGPaint* visitedPaint = mode == ApplyToFillMode ? visitedStyle->svgStyle()->fillPaint() : visitedStyle->svgStyle()->strokePaint()) {
+ // For SVG_PAINTTYPE_CURRENTCOLOR, 'color' already contains the 'visitedColor'.
+ if (visitedPaint->paintType() < SVGPaint::SVG_PAINTTYPE_URI_NONE && visitedPaint->paintType() != SVGPaint::SVG_PAINTTYPE_CURRENTCOLOR) {
+ const Color& visitedColor = visitedPaint->color();
+ if (visitedColor.isValid())
+ color = Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), color.alpha());
+ }
+ }
+ }
+
+ // If the primary resource is just a color, return immediately.
+ RenderSVGResourceSolidColor* colorResource = RenderSVGResource::sharedSolidPaintingResource();
+ if (paintType < SVGPaint::SVG_PAINTTYPE_URI_NONE) {
+ // If an invalid fill color is specified, fallback to fill/stroke="none".
+ if (!color.isValid())
+ return 0;
+
+ colorResource->setColor(color);
+ return colorResource;
+ }
+
+ // If no resources are associated with the given renderer, return the color resource.
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources) {
+ // If a paint server is specified, and no or an invalid fallback color is given, default to fill/stroke="black".
+ if (!color.isValid())
+ color = Color::black;
+
+ colorResource->setColor(color);
+ return colorResource;
+ }
+
+ // If the requested resource is not available, return the color resource.
+ RenderSVGResource* uriResource = mode == ApplyToFillMode ? resources->fill() : resources->stroke();
+ if (!uriResource) {
+ // If a paint server is specified, and no or an invalid fallback color is given, default to fill/stroke="black".
+ if (!color.isValid())
+ color = Color::black;
+
+ colorResource->setColor(color);
+ return colorResource;
+ }
+
+ // The paint server resource exists, though it may be invalid (pattern with width/height=0). Pass the fallback color to our caller
+ // so it can use the solid color painting resource, if applyResource() on the URI resource failed.
+ fallbackColor = color;
+ return uriResource;
+}
+
+RenderSVGResource* RenderSVGResource::fillPaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor)
+{
+ return requestPaintingResource(ApplyToFillMode, object, style, fallbackColor);
+}
+
+RenderSVGResource* RenderSVGResource::strokePaintingResource(RenderObject* object, const RenderStyle* style, Color& fallbackColor)
+{
+ return requestPaintingResource(ApplyToStrokeMode, object, style, fallbackColor);
+}
+
+RenderSVGResourceSolidColor* RenderSVGResource::sharedSolidPaintingResource()
+{
+ static RenderSVGResourceSolidColor* s_sharedSolidPaintingResource = 0;
+ if (!s_sharedSolidPaintingResource)
+ s_sharedSolidPaintingResource = new RenderSVGResourceSolidColor;
+ return s_sharedSolidPaintingResource;
+}
+
+void RenderSVGResource::markForLayoutAndParentResourceInvalidation(RenderObject* object, bool needsLayout)
+{
+ ASSERT(object);
+ if (needsLayout)
+ object->setNeedsLayout(true);
+
+ // Invalidate resources in ancestor chain, if needed.
+ RenderObject* current = object->parent();
+ while (current) {
+ if (current->isSVGResourceContainer()) {
+ current->toRenderSVGResourceContainer()->removeAllClientsFromCache();
+ break;
+ }
+
+ current = current->parent();
+ }
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResource.h b/Source/WebCore/rendering/RenderSVGResource.h
new file mode 100644
index 0000000..6fc2dae
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResource.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-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.
+ *
+ */
+
+#ifndef RenderSVGResource_h
+#define RenderSVGResource_h
+
+#if ENABLE(SVG)
+#include "RenderStyleConstants.h"
+#include "SVGDocumentExtensions.h"
+
+namespace WebCore {
+
+enum RenderSVGResourceType {
+ MaskerResourceType,
+ MarkerResourceType,
+ PatternResourceType,
+ LinearGradientResourceType,
+ RadialGradientResourceType,
+ SolidColorResourceType,
+ FilterResourceType,
+ ClipperResourceType
+};
+
+enum RenderSVGResourceMode {
+ ApplyToDefaultMode = 1 << 0, // used for all resources except gradient/pattern
+ ApplyToFillMode = 1 << 1,
+ ApplyToStrokeMode = 1 << 2,
+ ApplyToTextMode = 1 << 3 // used in combination with ApplyTo{Fill|Stroke}Mode
+};
+
+class Color;
+class FloatRect;
+class GraphicsContext;
+class Path;
+class RenderObject;
+class RenderStyle;
+class RenderSVGResourceSolidColor;
+
+class RenderSVGResource {
+public:
+ RenderSVGResource() { }
+ virtual ~RenderSVGResource() { }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true) = 0;
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true) = 0;
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode) = 0;
+ virtual void postApplyResource(RenderObject*, GraphicsContext*&, unsigned short, const Path*) { }
+ virtual FloatRect resourceBoundingBox(RenderObject*) = 0;
+
+ virtual RenderSVGResourceType resourceType() const = 0;
+
+ template<class Renderer>
+ Renderer* cast()
+ {
+ if (Renderer::s_resourceType == resourceType())
+ return static_cast<Renderer*>(this);
+
+ return 0;
+ }
+
+ // Helper utilities used in the render tree to access resources used for painting shapes/text (gradients & patterns & solid colors only)
+ static RenderSVGResource* fillPaintingResource(RenderObject*, const RenderStyle*, Color& fallbackColor);
+ static RenderSVGResource* strokePaintingResource(RenderObject*, const RenderStyle*, Color& fallbackColor);
+ static RenderSVGResourceSolidColor* sharedSolidPaintingResource();
+
+ static void markForLayoutAndParentResourceInvalidation(RenderObject*, bool needsLayout = true);
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceClipper.cpp b/Source/WebCore/rendering/RenderSVGResourceClipper.cpp
new file mode 100644
index 0000000..190c1a4
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceClipper.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+ * 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
+ * Copyright (C) Research In Motion Limited 2009-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)
+#include "RenderSVGResourceClipper.h"
+
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "ImageBuffer.h"
+#include "IntRect.h"
+#include "RenderObject.h"
+#include "RenderSVGResource.h"
+#include "RenderStyle.h"
+#include "SVGClipPathElement.h"
+#include "SVGElement.h"
+#include "SVGImageBufferTools.h"
+#include "SVGNames.h"
+#include "SVGRenderSupport.h"
+#include "SVGResources.h"
+#include "SVGStyledElement.h"
+#include "SVGStyledTransformableElement.h"
+#include "SVGUnitTypes.h"
+#include "SVGUseElement.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResourceType;
+
+RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
+ : RenderSVGResourceContainer(node)
+ , m_invalidationBlocked(false)
+{
+}
+
+RenderSVGResourceClipper::~RenderSVGResourceClipper()
+{
+ if (m_clipper.isEmpty())
+ return;
+
+ deleteAllValues(m_clipper);
+ m_clipper.clear();
+}
+
+void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidation)
+{
+ if (m_invalidationBlocked)
+ return;
+
+ m_clipBoundaries = FloatRect();
+ if (!m_clipper.isEmpty()) {
+ deleteAllValues(m_clipper);
+ m_clipper.clear();
+ }
+
+ markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+ if (m_invalidationBlocked)
+ return;
+
+ if (m_clipper.contains(client))
+ delete m_clipper.take(client);
+
+ markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(object);
+ ASSERT(context);
+#ifndef NDEBUG
+ ASSERT(resourceMode == ApplyToDefaultMode);
+#else
+ UNUSED_PARAM(resourceMode);
+#endif
+
+ return applyClippingToContext(object, object->objectBoundingBox(), object->repaintRectInLocalCoordinates(), context);
+}
+
+bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const FloatRect& objectBoundingBox)
+{
+ // If the current clip-path gets clipped itself, we have to fallback to masking.
+ if (!style()->svgStyle()->clipperResource().isEmpty())
+ return false;
+ WindRule clipRule = RULE_NONZERO;
+ Path clipPath = Path();
+
+ // If clip-path only contains one visible shape or path, we can use path-based clipping. Invisible
+ // shapes don't affect the clipping and can be ignored. If clip-path contains more than one
+ // visible shape, the additive clipping may not work, caused by the clipRule. EvenOdd
+ // as well as NonZero can cause self-clipping of the elements.
+ // See also http://www.w3.org/TR/SVG/painting.html#FillRuleProperty
+ for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
+ RenderObject* renderer = childNode->renderer();
+ if (!renderer)
+ continue;
+ // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts.
+ if (renderer->isSVGText())
+ return false;
+ if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable())
+ continue;
+ SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode);
+ RenderStyle* style = renderer->style();
+ if (!style || style->display() == NONE || style->visibility() != VISIBLE)
+ continue;
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ // Current shape in clip-path gets clipped too. Fallback to masking.
+ if (!svgStyle->clipperResource().isEmpty())
+ return false;
+ // Fallback to masking, if there is more than one clipping path.
+ if (clipPath.isEmpty()) {
+ styled->toClipPath(clipPath);
+ clipRule = svgStyle->clipRule();
+ } else
+ return false;
+ }
+ // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary.
+ if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ AffineTransform transform;
+ transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ clipPath.transform(transform);
+ }
+ // The SVG specification wants us to clip everything, if clip-path doesn't have a child.
+ if (clipPath.isEmpty())
+ clipPath.addRect(FloatRect());
+ context->clipPath(clipPath, clipRule);
+ return true;
+}
+
+bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, const FloatRect& objectBoundingBox,
+ const FloatRect& repaintRect, GraphicsContext* context)
+{
+ if (!m_clipper.contains(object))
+ m_clipper.set(object, new ClipperData);
+
+ bool shouldCreateClipData = false;
+ ClipperData* clipperData = m_clipper.get(object);
+ if (!clipperData->clipMaskImage) {
+ if (pathOnlyClipping(context, objectBoundingBox))
+ return true;
+ shouldCreateClipData = true;
+ }
+
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
+
+ FloatRect absoluteTargetRect = absoluteTransform.mapRect(repaintRect);
+ FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect);
+
+ if (shouldCreateClipData && !clampedAbsoluteTargetRect.isEmpty()) {
+ if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, clipperData->clipMaskImage, ColorSpaceDeviceRGB))
+ return false;
+
+ GraphicsContext* maskContext = clipperData->clipMaskImage->context();
+ ASSERT(maskContext);
+
+ // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
+ maskContext->save();
+ maskContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
+ maskContext->concatCTM(absoluteTransform);
+
+ // clipPath can also be clipped by another clipPath.
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this)) {
+ if (RenderSVGResourceClipper* clipper = resources->clipper()) {
+ if (!clipper->applyClippingToContext(this, objectBoundingBox, repaintRect, maskContext)) {
+ maskContext->restore();
+ return false;
+ }
+ }
+ }
+
+ drawContentIntoMaskImage(clipperData, objectBoundingBox);
+ maskContext->restore();
+ }
+
+ if (!clipperData->clipMaskImage)
+ return false;
+
+ SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, clipperData->clipMaskImage);
+ return true;
+}
+
+bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox)
+{
+ ASSERT(clipperData);
+ ASSERT(clipperData->clipMaskImage);
+
+ GraphicsContext* maskContext = clipperData->clipMaskImage->context();
+ ASSERT(maskContext);
+
+ AffineTransform maskContentTransformation;
+ SVGClipPathElement* clipPath = static_cast<SVGClipPathElement*>(node());
+ if (clipPath->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ maskContext->concatCTM(maskContentTransformation);
+ }
+
+ // Draw all clipPath children into a global mask.
+ for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
+ RenderObject* renderer = childNode->renderer();
+ if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
+ continue;
+ RenderStyle* style = renderer->style();
+ if (!style || style->display() == NONE || style->visibility() != VISIBLE)
+ continue;
+
+ WindRule newClipRule = style->svgStyle()->clipRule();
+ bool isUseElement = renderer->isSVGShadowTreeRootContainer();
+ if (isUseElement) {
+ SVGUseElement* useElement = static_cast<SVGUseElement*>(childNode);
+ renderer = useElement->rendererClipChild();
+ if (!renderer)
+ continue;
+ if (!useElement->hasAttribute(SVGNames::clip_ruleAttr))
+ newClipRule = renderer->style()->svgStyle()->clipRule();
+ }
+
+ // Only shapes, paths and texts are allowed for clipping.
+ if (!renderer->isSVGPath() && !renderer->isSVGText())
+ continue;
+
+ // Save the old RenderStyle of the current object for restoring after drawing
+ // it to the MaskImage. The new intermediate RenderStyle needs to inherit from
+ // the old one.
+ RefPtr<RenderStyle> oldRenderStyle = renderer->style();
+ RefPtr<RenderStyle> newRenderStyle = RenderStyle::clone(oldRenderStyle.get());
+ SVGRenderStyle* svgStyle = newRenderStyle.get()->accessSVGStyle();
+ svgStyle->setFillPaint(SVGPaint::defaultFill());
+ svgStyle->setStrokePaint(SVGPaint::defaultStroke());
+ svgStyle->setFillRule(newClipRule);
+ newRenderStyle.get()->setOpacity(1.0f);
+ svgStyle->setFillOpacity(1.0f);
+ svgStyle->setStrokeOpacity(1.0f);
+ svgStyle->setFilterResource(String());
+ svgStyle->setMaskerResource(String());
+
+ // The setStyle() call results in a styleDidChange() call, which in turn invalidations the resources.
+ // As we're mutating the resource on purpose, block updates until we've resetted the style again.
+ m_invalidationBlocked = true;
+ renderer->setStyle(newRenderStyle.release());
+
+ // In the case of a <use> element, we obtained its renderere above, to retrieve its clipRule.
+ // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering.
+ // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overriden above.
+ SVGImageBufferTools::renderSubtreeToImageBuffer(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation);
+
+ renderer->setStyle(oldRenderStyle.release());
+ m_invalidationBlocked = false;
+ }
+
+ return true;
+}
+
+void RenderSVGResourceClipper::calculateClipContentRepaintRect()
+{
+ // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip.
+ for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
+ RenderObject* renderer = childNode->renderer();
+ if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
+ continue;
+ if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer())
+ continue;
+ RenderStyle* style = renderer->style();
+ if (!style || style->display() == NONE || style->visibility() != VISIBLE)
+ continue;
+ m_clipBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
+ }
+}
+
+bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint)
+{
+ FloatPoint point = nodeAtPoint;
+ if (!SVGRenderSupport::pointInClippingArea(this, point))
+ return false;
+
+ if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ AffineTransform transform;
+ transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ point = transform.inverse().mapPoint(point);
+ }
+
+ for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
+ RenderObject* renderer = childNode->renderer();
+ if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
+ continue;
+ if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer())
+ continue;
+ IntPoint hitPoint;
+ HitTestResult result(hitPoint);
+ if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground))
+ return true;
+ }
+
+ return false;
+}
+
+FloatRect RenderSVGResourceClipper::resourceBoundingBox(RenderObject* object)
+{
+ // Resource was not layouted yet. Give back the boundingBox of the object.
+ if (selfNeedsLayout())
+ return object->objectBoundingBox();
+
+ if (m_clipBoundaries.isEmpty())
+ calculateClipContentRepaintRect();
+
+ if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+ AffineTransform transform;
+ transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ return transform.mapRect(m_clipBoundaries);
+ }
+
+ return m_clipBoundaries;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGResourceClipper.h b/Source/WebCore/rendering/RenderSVGResourceClipper.h
new file mode 100644
index 0000000..20153e9
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceClipper.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-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.
+ *
+ */
+
+#ifndef RenderSVGResourceClipper_h
+#define RenderSVGResourceClipper_h
+
+#if ENABLE(SVG)
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "IntSize.h"
+#include "Path.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGClipPathElement.h"
+#include "SVGUnitTypes.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+struct ClipperData : FastAllocBase {
+ OwnPtr<ImageBuffer> clipMaskImage;
+};
+
+class RenderSVGResourceClipper : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourceClipper(SVGClipPathElement*);
+ virtual ~RenderSVGResourceClipper();
+
+ virtual const char* renderName() const { return "RenderSVGResourceClipper"; }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual FloatRect resourceBoundingBox(RenderObject*);
+
+ virtual RenderSVGResourceType resourceType() const { return ClipperResourceType; }
+
+ bool hitTestClipContent(const FloatRect&, const FloatPoint&);
+
+ SVGUnitTypes::SVGUnitType clipPathUnits() const { return toUnitType(static_cast<SVGClipPathElement*>(node())->clipPathUnits()); }
+
+ static RenderSVGResourceType s_resourceType;
+private:
+ // clipPath can be clipped too, but don't have a boundingBox or repaintRect. So we can't call
+ // applyResource directly and use the rects from the object, since they are empty for RenderSVGResources
+ bool applyClippingToContext(RenderObject*, const FloatRect&, const FloatRect&, GraphicsContext*);
+ bool pathOnlyClipping(GraphicsContext*, const FloatRect&);
+ bool drawContentIntoMaskImage(ClipperData*, const FloatRect& objectBoundingBox);
+ void calculateClipContentRepaintRect();
+
+ bool m_invalidationBlocked;
+ FloatRect m_clipBoundaries;
+ HashMap<RenderObject*, ClipperData*> m_clipper;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceContainer.cpp b/Source/WebCore/rendering/RenderSVGResourceContainer.cpp
new file mode 100644
index 0000000..fb30efd
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceContainer.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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)
+#include "RenderSVGResourceContainer.h"
+
+#include "RenderSVGShadowTreeRootContainer.h"
+#include "SVGStyledTransformableElement.h"
+
+namespace WebCore {
+
+static inline SVGDocumentExtensions* svgExtensionsFromNode(Node* node)
+{
+ ASSERT(node);
+ ASSERT(node->document());
+ return node->document()->accessSVGExtensions();
+}
+
+RenderSVGResourceContainer::RenderSVGResourceContainer(SVGStyledElement* node)
+ : RenderSVGHiddenContainer(node)
+ , m_id(node->hasID() ? node->getIdAttribute() : nullAtom)
+ , m_registered(false)
+{
+}
+
+RenderSVGResourceContainer::~RenderSVGResourceContainer()
+{
+ if (m_registered)
+ svgExtensionsFromNode(node())->removeResource(m_id);
+}
+
+void RenderSVGResourceContainer::layout()
+{
+ // Invalidate all resources if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ removeAllClientsFromCache();
+
+ RenderSVGHiddenContainer::layout();
+}
+
+void RenderSVGResourceContainer::destroy()
+{
+ SVGResourcesCache::resourceDestroyed(this);
+ RenderSVGHiddenContainer::destroy();
+}
+
+void RenderSVGResourceContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderSVGHiddenContainer::styleDidChange(diff, oldStyle);
+
+ if (!m_registered) {
+ m_registered = true;
+ registerResource();
+ }
+}
+
+void RenderSVGResourceContainer::idChanged()
+{
+ // Invalidate all our current clients.
+ removeAllClientsFromCache();
+
+ // Remove old id, that is guaranteed to be present in cache.
+ SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
+ extensions->removeResource(m_id);
+ m_id = static_cast<Element*>(node())->getIdAttribute();
+
+ registerResource();
+}
+
+void RenderSVGResourceContainer::markAllClientsForInvalidation(InvalidationMode mode)
+{
+ if (m_clients.isEmpty())
+ return;
+
+ bool needsLayout = mode == LayoutAndBoundariesInvalidation;
+ bool markForInvalidation = mode != ParentOnlyInvalidation;
+
+ HashSet<RenderObject*>::iterator end = m_clients.end();
+ for (HashSet<RenderObject*>::iterator it = m_clients.begin(); it != end; ++it) {
+ RenderObject* client = *it;
+ if (client->isSVGResourceContainer()) {
+ client->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
+ continue;
+ }
+
+ if (markForInvalidation)
+ markClientForInvalidation(client, mode);
+
+ if (needsLayout)
+ client->setNeedsLayout(true);
+
+ // Invalidate resources in ancestor chain, if needed.
+ RenderObject* current = client->parent();
+ while (current) {
+ if (current->isSVGResourceContainer()) {
+ current->toRenderSVGResourceContainer()->removeAllClientsFromCache(markForInvalidation);
+ break;
+ }
+
+ current = current->parent();
+ }
+ }
+}
+
+void RenderSVGResourceContainer::markClientForInvalidation(RenderObject* client, InvalidationMode mode)
+{
+ ASSERT(client);
+ ASSERT(!m_clients.isEmpty());
+
+ switch (mode) {
+ case LayoutAndBoundariesInvalidation:
+ case BoundariesInvalidation:
+ client->setNeedsBoundariesUpdate();
+ break;
+ case RepaintInvalidation:
+ if (client->view())
+ client->repaint();
+ break;
+ case ParentOnlyInvalidation:
+ break;
+ }
+}
+
+void RenderSVGResourceContainer::addClient(RenderObject* client)
+{
+ ASSERT(client);
+ m_clients.add(client);
+}
+
+void RenderSVGResourceContainer::removeClient(RenderObject* client)
+{
+ ASSERT(client);
+ m_clients.remove(client);
+}
+
+void RenderSVGResourceContainer::registerResource()
+{
+ SVGDocumentExtensions* extensions = svgExtensionsFromNode(node());
+ if (!extensions->isPendingResource(m_id)) {
+ extensions->addResource(m_id, this);
+ return;
+ }
+
+ OwnPtr<SVGDocumentExtensions::SVGPendingElements> clients(extensions->removePendingResource(m_id));
+
+ // Cache us with the new id.
+ extensions->addResource(m_id, this);
+
+ // Update cached resources of pending clients.
+ const SVGDocumentExtensions::SVGPendingElements::const_iterator end = clients->end();
+ for (SVGDocumentExtensions::SVGPendingElements::const_iterator it = clients->begin(); it != end; ++it) {
+ RenderObject* renderer = (*it)->renderer();
+ if (!renderer)
+ continue;
+ SVGResourcesCache::clientUpdatedFromElement(renderer, renderer->style());
+ renderer->setNeedsLayout(true);
+ }
+}
+
+// FIXME: This does not belong here.
+AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
+{
+ if (!object->isSVGPath())
+ return resourceTransform;
+
+ SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node());
+ AffineTransform transform = resourceTransform;
+ transform.multiply(element->getScreenCTM(SVGLocatable::DisallowStyleUpdate));
+ return transform;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceContainer.h b/Source/WebCore/rendering/RenderSVGResourceContainer.h
new file mode 100644
index 0000000..4e5e475
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceContainer.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceContainer_h
+#define RenderSVGResourceContainer_h
+
+#if ENABLE(SVG)
+#include "RenderSVGHiddenContainer.h"
+#include "RenderSVGResource.h"
+
+namespace WebCore {
+
+class RenderSVGResourceContainer : public RenderSVGHiddenContainer,
+ public RenderSVGResource {
+public:
+ RenderSVGResourceContainer(SVGStyledElement*);
+ virtual ~RenderSVGResourceContainer();
+
+ virtual void layout();
+ virtual void destroy();
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual bool isSVGResourceContainer() const { return true; }
+ virtual RenderSVGResourceContainer* toRenderSVGResourceContainer() { return this; }
+
+ static AffineTransform transformOnNonScalingStroke(RenderObject*, const AffineTransform& resourceTransform);
+
+ void idChanged();
+
+protected:
+ enum InvalidationMode {
+ LayoutAndBoundariesInvalidation,
+ BoundariesInvalidation,
+ RepaintInvalidation,
+ ParentOnlyInvalidation
+ };
+
+ // Used from the invalidateClient/invalidateClients methods from classes, inheriting from us.
+ void markAllClientsForInvalidation(InvalidationMode);
+ void markClientForInvalidation(RenderObject*, InvalidationMode);
+
+private:
+ friend class SVGResourcesCache;
+ void addClient(RenderObject*);
+ void removeClient(RenderObject*);
+
+private:
+ void registerResource();
+
+ AtomicString m_id;
+ bool m_registered;
+ HashSet<RenderObject*> m_clients;
+};
+
+inline RenderSVGResourceContainer* getRenderSVGResourceContainerById(Document* document, const AtomicString& id)
+{
+ if (id.isEmpty())
+ return 0;
+
+ if (RenderSVGResourceContainer* renderResource = document->accessSVGExtensions()->resourceById(id))
+ return renderResource;
+
+ return 0;
+}
+
+template<typename Renderer>
+Renderer* getRenderSVGResourceById(Document* document, const AtomicString& id)
+{
+ if (RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id))
+ return container->cast<Renderer>();
+
+ return 0;
+}
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceFilter.cpp b/Source/WebCore/rendering/RenderSVGResourceFilter.cpp
new file mode 100644
index 0000000..8aa9370
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceFilter.cpp
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org>
+ * Copyright (C) 2005 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * 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(FILTERS)
+#include "RenderSVGResourceFilter.h"
+
+#include "AffineTransform.h"
+#include "FloatPoint.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "Image.h"
+#include "ImageBuffer.h"
+#include "ImageData.h"
+#include "IntRect.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceFilterPrimitive.h"
+#include "SVGElement.h"
+#include "SVGFilter.h"
+#include "SVGFilterElement.h"
+#include "SVGFilterPrimitiveStandardAttributes.h"
+#include "SVGImageBufferTools.h"
+#include "SVGNames.h"
+#include "SVGStyledElement.h"
+#include "SVGUnitTypes.h"
+#include <wtf/Vector.h>
+#include <wtf/UnusedParam.h>
+
+static const float kMaxFilterSize = 5000.0f;
+
+using namespace std;
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType;
+
+RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node)
+ : RenderSVGResourceContainer(node)
+{
+}
+
+RenderSVGResourceFilter::~RenderSVGResourceFilter()
+{
+ if (m_filter.isEmpty())
+ return;
+
+ deleteAllValues(m_filter);
+ m_filter.clear();
+}
+
+void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation)
+{
+ if (!m_filter.isEmpty()) {
+ deleteAllValues(m_filter);
+ m_filter.clear();
+ }
+
+ markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+
+ if (m_filter.contains(client))
+ delete m_filter.take(client);
+
+ markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(Filter* filter)
+{
+ SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
+ bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
+
+ // Add effects to the builder
+ RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(filter);
+ for (Node* node = filterElement->firstChild(); node; node = node->nextSibling()) {
+ if (!node->isSVGElement())
+ continue;
+
+ SVGElement* element = static_cast<SVGElement*>(node);
+ if (!element->isFilterEffect())
+ continue;
+
+ SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element);
+ RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter);
+ if (!effect) {
+ builder->clearEffects();
+ return 0;
+ }
+ builder->appendEffectToEffectReferences(effect);
+ effectElement->setStandardAttributes(primitiveBoundingBoxMode, effect.get());
+ builder->add(effectElement->result(), effect);
+ }
+ return builder.release();
+}
+
+bool RenderSVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size, FloatSize& scale)
+{
+ bool matchesFilterSize = true;
+ if (size.width() > kMaxFilterSize) {
+ scale.setWidth(scale.width() * kMaxFilterSize / size.width());
+ matchesFilterSize = false;
+ }
+ if (size.height() > kMaxFilterSize) {
+ scale.setHeight(scale.height() * kMaxFilterSize / size.height());
+ matchesFilterSize = false;
+ }
+
+ return matchesFilterSize;
+}
+
+bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(object);
+ ASSERT(context);
+#ifndef NDEBUG
+ ASSERT(resourceMode == ApplyToDefaultMode);
+#else
+ UNUSED_PARAM(resourceMode);
+#endif
+
+ // Returning false here, to avoid drawings onto the context. We just want to
+ // draw the stored filter output, not the unfiltered object as well.
+ if (m_filter.contains(object)) {
+ FilterData* filterData = m_filter.get(object);
+ if (filterData->builded)
+ return false;
+
+ delete m_filter.take(object); // Oops, have to rebuild, go through normal code path
+ }
+
+ OwnPtr<FilterData> filterData(new FilterData);
+ FloatRect targetBoundingBox = object->objectBoundingBox();
+
+ SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node());
+ filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox);
+ if (filterData->boundaries.isEmpty())
+ return false;
+
+ // Determine absolute transformation matrix for filter.
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
+ if (!absoluteTransform.isInvertible())
+ return false;
+
+ // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile.
+ filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f());
+
+ // Determine absolute boundaries of the filter and the drawing region.
+ FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries);
+ FloatRect drawingRegion = object->strokeBoundingBox();
+ drawingRegion.intersect(filterData->boundaries);
+ FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion);
+
+ // Create the SVGFilter object.
+ bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
+ filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode);
+
+ // Create all relevant filter primitives.
+ filterData->builder = buildPrimitives(filterData->filter.get());
+ if (!filterData->builder)
+ return false;
+
+ // Calculate the scale factor for the use of filterRes.
+ // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion
+ FloatSize scale(1, 1);
+ if (filterElement->hasAttribute(SVGNames::filterResAttr)) {
+ scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width());
+ scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height());
+ }
+
+ if (scale.isEmpty())
+ return false;
+
+ // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize.
+ FloatRect tempSourceRect = absoluteDrawingRegion;
+ tempSourceRect.scale(scale.width(), scale.height());
+ fitsInMaximumImageSize(tempSourceRect.size(), scale);
+
+ // Set the scale level in SVGFilter.
+ filterData->filter->setFilterResolution(scale);
+
+ FilterEffect* lastEffect = filterData->builder->lastEffect();
+ if (!lastEffect)
+ return false;
+
+ RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect, filterData->filter.get());
+ FloatRect subRegion = lastEffect->maxEffectRect();
+ // At least one FilterEffect has a too big image size,
+ // recalculate the effect sizes with new scale factors.
+ if (!fitsInMaximumImageSize(subRegion.size(), scale)) {
+ filterData->filter->setFilterResolution(scale);
+ RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect, filterData->filter.get());
+ }
+
+ // If the drawingRegion is empty, we have something like <g filter=".."/>.
+ // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource.
+ if (drawingRegion.isEmpty()) {
+ ASSERT(!m_filter.contains(object));
+ filterData->savedContext = context;
+ m_filter.set(object, filterData.leakPtr());
+ return false;
+ }
+
+ absoluteDrawingRegion.scale(scale.width(), scale.height());
+
+ OwnPtr<ImageBuffer> sourceGraphic;
+ if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) {
+ ASSERT(!m_filter.contains(object));
+ filterData->savedContext = context;
+ m_filter.set(object, filterData.leakPtr());
+ return false;
+ }
+
+ GraphicsContext* sourceGraphicContext = sourceGraphic->context();
+ ASSERT(sourceGraphicContext);
+
+ sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y());
+ if (scale.width() != 1 || scale.height() != 1)
+ sourceGraphicContext->scale(scale);
+
+ sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform);
+ sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size()));
+ filterData->sourceGraphicBuffer = sourceGraphic.release();
+ filterData->savedContext = context;
+
+ context = sourceGraphicContext;
+
+ ASSERT(!m_filter.contains(object));
+ m_filter.set(object, filterData.leakPtr());
+
+ return true;
+}
+
+void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path*)
+{
+ ASSERT(object);
+ ASSERT(context);
+#ifndef NDEBUG
+ ASSERT(resourceMode == ApplyToDefaultMode);
+#else
+ UNUSED_PARAM(resourceMode);
+#endif
+
+ if (!m_filter.contains(object))
+ return;
+
+ FilterData* filterData = m_filter.get(object);
+ if (!filterData->builded) {
+ if (!filterData->savedContext) {
+ removeClientFromCache(object);
+ return;
+ }
+
+ context = filterData->savedContext;
+ filterData->savedContext = 0;
+#if !PLATFORM(CG)
+ if (filterData->sourceGraphicBuffer)
+ filterData->sourceGraphicBuffer->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
+#endif
+ }
+
+ FilterEffect* lastEffect = filterData->builder->lastEffect();
+
+ if (lastEffect && !filterData->boundaries.isEmpty() && !lastEffect->filterPrimitiveSubregion().isEmpty()) {
+ // This is the real filtering of the object. It just needs to be called on the
+ // initial filtering process. We just take the stored filter result on a
+ // second drawing.
+ if (!filterData->builded) {
+ filterData->filter->setSourceImage(filterData->sourceGraphicBuffer.release());
+ lastEffect->apply();
+#if !PLATFORM(CG)
+ ImageBuffer* resultImage = lastEffect->asImageBuffer();
+ if (resultImage)
+ resultImage->transformColorSpace(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
+#endif
+ filterData->builded = true;
+ }
+
+ ImageBuffer* resultImage = lastEffect->asImageBuffer();
+ if (resultImage) {
+ context->concatCTM(filterData->shearFreeAbsoluteTransform.inverse());
+
+ context->scale(FloatSize(1 / filterData->filter->filterResolution().width(), 1 / filterData->filter->filterResolution().height()));
+ context->clip(lastEffect->maxEffectRect());
+ context->drawImageBuffer(resultImage, object->style()->colorSpace(), lastEffect->absolutePaintRect());
+ context->scale(filterData->filter->filterResolution());
+
+ context->concatCTM(filterData->shearFreeAbsoluteTransform);
+ }
+ }
+ filterData->sourceGraphicBuffer.clear();
+}
+
+FloatRect RenderSVGResourceFilter::resourceBoundingBox(RenderObject* object)
+{
+ if (SVGFilterElement* element = static_cast<SVGFilterElement*>(node()))
+ return element->filterBoundingBox(object->objectBoundingBox());
+
+ return FloatRect();
+}
+
+}
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceFilter.h b/Source/WebCore/rendering/RenderSVGResourceFilter.h
new file mode 100644
index 0000000..5950c44
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceFilter.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 2004, 2005 Rob Buis <buis@kde.org>
+ * 2005 Eric Seidel <eric@webkit.org>
+ * 2009 Dirk Schulze <krit@webkit.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceFilter_h
+#define RenderSVGResourceFilter_h
+
+#if ENABLE(SVG) && ENABLE(FILTERS)
+#include "FloatRect.h"
+#include "ImageBuffer.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGFilter.h"
+#include "SVGFilterBuilder.h"
+#include "SVGFilterElement.h"
+#include "SVGUnitTypes.h"
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+struct FilterData {
+ FilterData()
+ : savedContext(0)
+ , builded(false)
+ {
+ }
+
+ RefPtr<SVGFilter> filter;
+ RefPtr<SVGFilterBuilder> builder;
+ OwnPtr<ImageBuffer> sourceGraphicBuffer;
+ GraphicsContext* savedContext;
+ AffineTransform shearFreeAbsoluteTransform;
+ FloatRect boundaries;
+ FloatSize scale;
+ bool builded;
+};
+
+class GraphicsContext;
+
+class RenderSVGResourceFilter : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourceFilter(SVGFilterElement*);
+ virtual ~RenderSVGResourceFilter();
+
+ virtual const char* renderName() const { return "RenderSVGResourceFilter"; }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual void postApplyResource(RenderObject*, GraphicsContext*&, unsigned short resourceMode, const Path*);
+
+ virtual FloatRect resourceBoundingBox(RenderObject*);
+
+ PassRefPtr<SVGFilterBuilder> buildPrimitives(Filter*);
+
+ SVGUnitTypes::SVGUnitType filterUnits() const { return toUnitType(static_cast<SVGFilterElement*>(node())->filterUnits()); }
+ SVGUnitTypes::SVGUnitType primitiveUnits() const { return toUnitType(static_cast<SVGFilterElement*>(node())->primitiveUnits()); }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+private:
+ bool fitsInMaximumImageSize(const FloatSize&, FloatSize&);
+
+ HashMap<RenderObject*, FilterData*> m_filter;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.cpp b/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.cpp
new file mode 100644
index 0000000..bf2ef6e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Zoltan Herczeg
+ *
+ * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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(SVG) && ENABLE(FILTERS)
+#include "RenderSVGResourceFilterPrimitive.h"
+#include "SVGFEImage.h"
+
+namespace WebCore {
+
+FloatRect RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(FilterEffect* effect, SVGFilter* filter)
+{
+ FloatRect uniteRect;
+ FloatRect subregionBoundingBox = effect->effectBoundaries();
+ FloatRect subregion = subregionBoundingBox;
+
+ if (effect->filterEffectType() != FilterEffectTypeTile) {
+ // FETurbulence, FEImage and FEFlood don't have input effects, take the filter region as unite rect.
+ if (unsigned numberOfInputEffects = effect->inputEffects().size()) {
+ for (unsigned i = 0; i < numberOfInputEffects; ++i)
+ uniteRect.unite(determineFilterPrimitiveSubregion(effect->inputEffect(i), filter));
+ } else
+ uniteRect = filter->filterRegionInUserSpace();
+ } else {
+ determineFilterPrimitiveSubregion(effect->inputEffect(0), filter);
+ uniteRect = filter->filterRegionInUserSpace();
+ }
+
+ if (filter->effectBoundingBoxMode()) {
+ subregion = uniteRect;
+ // Avoid the calling of a virtual method several times.
+ FloatRect targetBoundingBox = filter->targetBoundingBox();
+
+ if (effect->hasX())
+ subregion.setX(targetBoundingBox.x() + subregionBoundingBox.x() * targetBoundingBox.width());
+
+ if (effect->hasY())
+ subregion.setY(targetBoundingBox.y() + subregionBoundingBox.y() * targetBoundingBox.height());
+
+ if (effect->hasWidth())
+ subregion.setWidth(subregionBoundingBox.width() * targetBoundingBox.width());
+
+ if (effect->hasHeight())
+ subregion.setHeight(subregionBoundingBox.height() * targetBoundingBox.height());
+ } else {
+ if (!effect->hasX())
+ subregion.setX(uniteRect.x());
+
+ if (!effect->hasY())
+ subregion.setY(uniteRect.y());
+
+ if (!effect->hasWidth())
+ subregion.setWidth(uniteRect.width());
+
+ if (!effect->hasHeight())
+ subregion.setHeight(uniteRect.height());
+ }
+
+ effect->setFilterPrimitiveSubregion(subregion);
+
+ FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion);
+ FloatSize filterResolution = filter->filterResolution();
+ absoluteSubregion.scale(filterResolution.width(), filterResolution.height());
+
+ // FEImage needs the unclipped subregion in absolute coordinates to determine the correct
+ // destination rect in combination with preserveAspectRatio.
+ if (effect->filterEffectType() == FilterEffectTypeImage)
+ reinterpret_cast<FEImage*>(effect)->setAbsoluteSubregion(absoluteSubregion);
+
+ // Clip every filter effect to the filter region.
+ FloatRect absoluteScaledFilterRegion = filter->filterRegion();
+ absoluteScaledFilterRegion.scale(filterResolution.width(), filterResolution.height());
+ absoluteSubregion.intersect(absoluteScaledFilterRegion);
+
+ effect->setMaxEffectRect(enclosingIntRect(absoluteSubregion));
+ return subregion;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG) && ENABLE(FILTERS)
diff --git a/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.h b/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.h
new file mode 100644
index 0000000..c890ffe
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceFilterPrimitive.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Zoltan Herczeg
+ *
+ * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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 RenderSVGResourceFilterPrimitive_h
+#define RenderSVGResourceFilterPrimitive_h
+
+#if ENABLE(SVG) && ENABLE(FILTERS)
+
+#include "RenderSVGHiddenContainer.h"
+#include "SVGFilterPrimitiveStandardAttributes.h"
+#include "SVGFilter.h"
+
+namespace WebCore {
+
+class RenderSVGResourceFilterPrimitive : public RenderSVGHiddenContainer {
+public:
+
+ explicit RenderSVGResourceFilterPrimitive(SVGFilterPrimitiveStandardAttributes* filterPrimitiveElement)
+ : RenderSVGHiddenContainer(filterPrimitiveElement)
+ {
+ }
+
+ // They depend on the RenderObject argument of RenderSVGResourceFilter::applyResource.
+ static FloatRect determineFilterPrimitiveSubregion(FilterEffect* effect, SVGFilter* filter);
+
+private:
+ virtual const char* renderName() const { return "RenderSVGResourceFilterPrimitive"; }
+ virtual bool isSVGResourceFilterPrimitive() const { return true; }
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG) && ENABLE(FILTERS)
+
+#endif // RenderSVGResourceFilterPrimitive_h
diff --git a/Source/WebCore/rendering/RenderSVGResourceGradient.cpp b/Source/WebCore/rendering/RenderSVGResourceGradient.cpp
new file mode 100644
index 0000000..7c383d0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceGradient.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
+ * 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)
+#include "RenderSVGResourceGradient.h"
+
+#include "GradientAttributes.h"
+#include "GraphicsContext.h"
+#include "RenderSVGText.h"
+#include "SVGImageBufferTools.h"
+#include "SVGRenderSupport.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement* node)
+ : RenderSVGResourceContainer(node)
+ , m_shouldCollectGradientAttributes(true)
+#if PLATFORM(CG)
+ , m_savedContext(0)
+#endif
+{
+}
+
+RenderSVGResourceGradient::~RenderSVGResourceGradient()
+{
+ if (m_gradient.isEmpty())
+ return;
+
+ deleteAllValues(m_gradient);
+ m_gradient.clear();
+}
+
+void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation)
+{
+ if (!m_gradient.isEmpty()) {
+ deleteAllValues(m_gradient);
+ m_gradient.clear();
+ }
+
+ m_shouldCollectGradientAttributes = true;
+ markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceGradient::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+
+ if (m_gradient.contains(client))
+ delete m_gradient.take(client);
+
+ markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
+}
+
+#if PLATFORM(CG)
+static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context,
+ GraphicsContext*& savedContext,
+ OwnPtr<ImageBuffer>& imageBuffer,
+ RenderObject* object)
+{
+ RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
+ ASSERT(textRootBlock);
+
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
+
+ FloatRect absoluteTargetRect = absoluteTransform.mapRect(textRootBlock->repaintRectInLocalCoordinates());
+ FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
+ if (clampedAbsoluteTargetRect.isEmpty())
+ return false;
+
+ OwnPtr<ImageBuffer> maskImage;
+ if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, ColorSpaceDeviceRGB))
+ return false;
+
+ GraphicsContext* maskImageContext = maskImage->context();
+ ASSERT(maskImageContext);
+
+ maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
+ maskImageContext->concatCTM(absoluteTransform);
+
+ ASSERT(maskImage);
+ savedContext = context;
+ context = maskImageContext;
+ imageBuffer = maskImage.release();
+ return true;
+}
+
+static inline AffineTransform clipToTextMask(GraphicsContext* context,
+ OwnPtr<ImageBuffer>& imageBuffer,
+ FloatRect& targetRect,
+ RenderObject* object,
+ bool boundingBoxMode,
+ const AffineTransform& gradientTransform)
+{
+ RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
+ ASSERT(textRootBlock);
+
+ targetRect = textRootBlock->repaintRectInLocalCoordinates();
+
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
+
+ FloatRect absoluteTargetRect = absoluteTransform.mapRect(targetRect);
+ FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
+
+ SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, imageBuffer);
+
+ AffineTransform matrix;
+ if (boundingBoxMode) {
+ FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
+ matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
+ matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
+ }
+ matrix.multLeft(gradientTransform);
+ return matrix;
+}
+#endif
+
+bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(object);
+ ASSERT(style);
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further.
+ // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property
+ // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
+ // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash.
+ SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node());
+ if (!gradientElement)
+ return false;
+
+ if (m_shouldCollectGradientAttributes) {
+ gradientElement->updateAnimatedSVGAttribute(anyQName());
+ collectGradientAttributes(gradientElement);
+ m_shouldCollectGradientAttributes = false;
+ }
+
+ // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
+ // then the given effect (e.g. a gradient or a filter) will be ignored.
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+ if (boundingBoxMode() && objectBoundingBox.isEmpty())
+ return false;
+
+ if (!m_gradient.contains(object))
+ m_gradient.set(object, new GradientData);
+
+ GradientData* gradientData = m_gradient.get(object);
+ bool isPaintingText = resourceMode & ApplyToTextMode;
+
+ // Create gradient object
+ if (!gradientData->gradient) {
+ buildGradient(gradientData, gradientElement);
+
+ // CG platforms will handle the gradient space transform for text after applying the
+ // resource, so don't apply it here. For non-CG platforms, we want the text bounding
+ // box applied to the gradient space transform now, so the gradient shader can use it.
+#if PLATFORM(CG)
+ if (boundingBoxMode() && !objectBoundingBox.isEmpty() && !isPaintingText) {
+#else
+ if (boundingBoxMode() && !objectBoundingBox.isEmpty()) {
+#endif
+ gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ }
+
+ AffineTransform gradientTransform;
+ calculateGradientTransform(gradientTransform);
+
+ gradientData->userspaceTransform.multLeft(gradientTransform);
+ gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
+ }
+
+ if (!gradientData->gradient)
+ return false;
+
+ // Draw gradient
+ context->save();
+
+ if (isPaintingText) {
+#if PLATFORM(CG)
+ if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
+ context->restore();
+ return false;
+ }
+#endif
+
+ context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
+ }
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ if (resourceMode & ApplyToFillMode) {
+ context->setAlpha(svgStyle->fillOpacity());
+ context->setFillGradient(gradientData->gradient);
+ context->setFillRule(svgStyle->fillRule());
+ } else if (resourceMode & ApplyToStrokeMode) {
+ if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
+ gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform));
+ context->setAlpha(svgStyle->strokeOpacity());
+ context->setStrokeGradient(gradientData->gradient);
+ SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
+ }
+
+ return true;
+}
+
+void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
+{
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ if (resourceMode & ApplyToTextMode) {
+#if PLATFORM(CG)
+ // CG requires special handling for gradient on text
+ if (m_savedContext && m_gradient.contains(object)) {
+ GradientData* gradientData = m_gradient.get(object);
+
+ // Restore on-screen drawing context
+ context = m_savedContext;
+ m_savedContext = 0;
+
+ AffineTransform gradientTransform;
+ calculateGradientTransform(gradientTransform);
+
+ FloatRect targetRect;
+ gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, boundingBoxMode(), gradientTransform));
+ context->setFillGradient(gradientData->gradient);
+
+ context->fillRect(targetRect);
+ m_imageBuffer.clear();
+ }
+#else
+ UNUSED_PARAM(object);
+#endif
+ } else if (path) {
+ if (resourceMode & ApplyToFillMode)
+ context->fillPath(*path);
+ else if (resourceMode & ApplyToStrokeMode)
+ context->strokePath(*path);
+ }
+
+ context->restore();
+}
+
+void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
+{
+ ASSERT(gradientData->gradient);
+
+ const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
+ for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
+ gradientData->gradient->addColorStop(*it);
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceGradient.h b/Source/WebCore/rendering/RenderSVGResourceGradient.h
new file mode 100644
index 0000000..1c7f52d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceGradient.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 2008 Eric Seidel <eric@webkit.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceGradient_h
+#define RenderSVGResourceGradient_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "Gradient.h"
+#include "ImageBuffer.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGGradientElement.h"
+
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+struct GradientData {
+ RefPtr<Gradient> gradient;
+ AffineTransform userspaceTransform;
+};
+
+class GraphicsContext;
+
+class RenderSVGResourceGradient : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourceGradient(SVGGradientElement*);
+ virtual ~RenderSVGResourceGradient();
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual void postApplyResource(RenderObject*, GraphicsContext*&, unsigned short resourceMode, const Path*);
+ virtual FloatRect resourceBoundingBox(RenderObject*) { return FloatRect(); }
+
+protected:
+ void addStops(GradientData*, const Vector<Gradient::ColorStop>&) const;
+
+ virtual bool boundingBoxMode() const = 0;
+ virtual void calculateGradientTransform(AffineTransform&) = 0;
+ virtual void collectGradientAttributes(SVGGradientElement*) = 0;
+ virtual void buildGradient(GradientData*, SVGGradientElement*) const = 0;
+
+private:
+ bool m_shouldCollectGradientAttributes : 1;
+ HashMap<RenderObject*, GradientData*> m_gradient;
+
+#if PLATFORM(CG)
+ GraphicsContext* m_savedContext;
+ OwnPtr<ImageBuffer> m_imageBuffer;
+#endif
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceLinearGradient.cpp b/Source/WebCore/rendering/RenderSVGResourceLinearGradient.cpp
new file mode 100644
index 0000000..ce8d69e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceLinearGradient.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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)
+#include "RenderSVGResourceLinearGradient.h"
+
+#include "LinearGradientAttributes.h"
+#include "SVGLinearGradientElement.h"
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceLinearGradient::s_resourceType = LinearGradientResourceType;
+
+RenderSVGResourceLinearGradient::RenderSVGResourceLinearGradient(SVGLinearGradientElement* node)
+ : RenderSVGResourceGradient(node)
+{
+}
+
+RenderSVGResourceLinearGradient::~RenderSVGResourceLinearGradient()
+{
+}
+
+void RenderSVGResourceLinearGradient::collectGradientAttributes(SVGGradientElement* gradientElement)
+{
+ m_attributes = LinearGradientAttributes();
+ static_cast<SVGLinearGradientElement*>(gradientElement)->collectGradientAttributes(m_attributes);
+}
+
+void RenderSVGResourceLinearGradient::buildGradient(GradientData* gradientData, SVGGradientElement* gradientElement) const
+{
+ SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradientElement);
+
+ // Determine gradient start/end points
+ FloatPoint startPoint;
+ FloatPoint endPoint;
+ linearGradientElement->calculateStartEndPoints(m_attributes, startPoint, endPoint);
+
+ gradientData->gradient = Gradient::create(startPoint, endPoint);
+ gradientData->gradient->setSpreadMethod(m_attributes.spreadMethod());
+
+ // Add stops
+ addStops(gradientData, m_attributes.stops());
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceLinearGradient.h b/Source/WebCore/rendering/RenderSVGResourceLinearGradient.h
new file mode 100644
index 0000000..9e4530d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceLinearGradient.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceLinearGradient_h
+#define RenderSVGResourceLinearGradient_h
+
+#if ENABLE(SVG)
+#include "LinearGradientAttributes.h"
+#include "RenderSVGResourceGradient.h"
+
+namespace WebCore {
+
+class SVGLinearGradientElement;
+
+class RenderSVGResourceLinearGradient : public RenderSVGResourceGradient {
+public:
+ RenderSVGResourceLinearGradient(SVGLinearGradientElement*);
+ virtual ~RenderSVGResourceLinearGradient();
+
+ virtual const char* renderName() const { return "RenderSVGResourceLinearGradient"; }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+ virtual bool boundingBoxMode() const { return m_attributes.boundingBoxMode(); }
+ virtual void calculateGradientTransform(AffineTransform& transform) { transform = m_attributes.gradientTransform(); }
+ virtual void collectGradientAttributes(SVGGradientElement*);
+ virtual void buildGradient(GradientData*, SVGGradientElement*) const;
+
+private:
+ LinearGradientAttributes m_attributes;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceMarker.cpp b/Source/WebCore/rendering/RenderSVGResourceMarker.cpp
new file mode 100644
index 0000000..1d5663b
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceMarker.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
+ * 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
+ * Copyright (C) Research In Motion Limited 2009-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)
+#include "RenderSVGResourceMarker.h"
+
+#include "GraphicsContext.h"
+#include "RenderSVGContainer.h"
+#include "SVGElement.h"
+#include "SVGMarkerElement.h"
+#include "SVGRenderSupport.h"
+#include "SVGStyledElement.h"
+#include "SVGStyledTransformableElement.h"
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceMarker::s_resourceType = MarkerResourceType;
+
+RenderSVGResourceMarker::RenderSVGResourceMarker(SVGMarkerElement* node)
+ : RenderSVGResourceContainer(node)
+{
+}
+
+RenderSVGResourceMarker::~RenderSVGResourceMarker()
+{
+}
+
+void RenderSVGResourceMarker::layout()
+{
+ // Invalidate all resources if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ removeAllClientsFromCache();
+
+ // RenderSVGHiddenContainer overwrites layout(). We need the
+ // layouting of RenderSVGContainer for calculating local
+ // transformations and repaint.
+ RenderSVGContainer::layout();
+}
+
+void RenderSVGResourceMarker::removeAllClientsFromCache(bool markForInvalidation)
+{
+ markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceMarker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+ markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceMarker::applyViewportClip(PaintInfo& paintInfo)
+{
+ if (SVGRenderSupport::isOverflowHidden(this))
+ paintInfo.context->clip(m_viewport);
+}
+
+FloatRect RenderSVGResourceMarker::markerBoundaries(const AffineTransform& markerTransformation) const
+{
+ FloatRect coordinates = RenderSVGContainer::repaintRectInLocalCoordinates();
+
+ // Map repaint rect into parent coordinate space, in which the marker boundaries have to be evaluated
+ coordinates = localToParentTransform().mapRect(coordinates);
+
+ return markerTransformation.mapRect(coordinates);
+}
+
+const AffineTransform& RenderSVGResourceMarker::localToParentTransform() const
+{
+ AffineTransform viewportTranslation(viewportTransform());
+ m_localToParentTransform = viewportTranslation.translateRight(m_viewport.x(), m_viewport.y());
+ return m_localToParentTransform;
+ // If this class were ever given a localTransform(), then the above would read:
+ // return viewportTransform() * localTransform() * viewportTranslation;
+}
+
+FloatPoint RenderSVGResourceMarker::referencePoint() const
+{
+ SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
+ ASSERT(marker);
+
+ return FloatPoint(marker->refX().value(marker), marker->refY().value(marker));
+}
+
+float RenderSVGResourceMarker::angle() const
+{
+ SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
+ ASSERT(marker);
+
+ float angle = -1;
+ if (marker->orientType() == SVGMarkerElement::SVG_MARKER_ORIENT_ANGLE)
+ angle = marker->orientAngle().value();
+
+ return angle;
+}
+
+AffineTransform RenderSVGResourceMarker::markerTransformation(const FloatPoint& origin, float autoAngle, float strokeWidth) const
+{
+ SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
+ ASSERT(marker);
+
+ float markerAngle = angle();
+ bool useStrokeWidth = (marker->markerUnits() == SVGMarkerElement::SVG_MARKERUNITS_STROKEWIDTH);
+
+ AffineTransform transform;
+ transform.translate(origin.x(), origin.y());
+ transform.rotate(markerAngle == -1 ? autoAngle : markerAngle);
+ transform = markerContentTransformation(transform, referencePoint(), useStrokeWidth ? strokeWidth : -1);
+ return transform;
+}
+
+void RenderSVGResourceMarker::draw(PaintInfo& paintInfo, const AffineTransform& transform)
+{
+ PaintInfo info(paintInfo);
+ info.context->save();
+ info.applyTransform(transform);
+ RenderSVGContainer::paint(info, 0, 0);
+ info.context->restore();
+}
+
+AffineTransform RenderSVGResourceMarker::markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth) const
+{
+ // The 'origin' coordinate maps to SVGs refX/refY, given in coordinates relative to the viewport established by the marker
+ FloatPoint mappedOrigin = viewportTransform().mapPoint(origin);
+
+ AffineTransform transformation = contentTransformation;
+ if (strokeWidth != -1)
+ transformation.scaleNonUniform(strokeWidth, strokeWidth);
+
+ transformation.translate(-mappedOrigin.x(), -mappedOrigin.y());
+ return transformation;
+}
+
+AffineTransform RenderSVGResourceMarker::viewportTransform() const
+{
+ SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
+ ASSERT(marker);
+
+ return marker->viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
+}
+
+void RenderSVGResourceMarker::calcViewport()
+{
+ if (!selfNeedsLayout())
+ return;
+
+ SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(node());
+ ASSERT(marker);
+
+ float w = marker->markerWidth().value(marker);
+ float h = marker->markerHeight().value(marker);
+ m_viewport = FloatRect(0, 0, w, h);
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGResourceMarker.h b/Source/WebCore/rendering/RenderSVGResourceMarker.h
new file mode 100644
index 0000000..e41096e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceMarker.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-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.
+ *
+ */
+
+#ifndef RenderSVGResourceMarker_h
+#define RenderSVGResourceMarker_h
+
+#if ENABLE(SVG)
+#include "FloatRect.h"
+#include "RenderObject.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGMarkerElement.h"
+#include "SVGStyledElement.h"
+
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+class AffineTransform;
+
+class RenderSVGResourceMarker : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourceMarker(SVGMarkerElement*);
+ virtual ~RenderSVGResourceMarker();
+
+ virtual const char* renderName() const { return "RenderSVGResourceMarker"; }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+
+ void draw(PaintInfo&, const AffineTransform&);
+
+ // Calculates marker boundaries, mapped to the target element's coordinate space
+ FloatRect markerBoundaries(const AffineTransform& markerTransformation) const;
+
+ virtual void applyViewportClip(PaintInfo&);
+ virtual void layout();
+ virtual void calcViewport();
+
+ virtual const AffineTransform& localToParentTransform() const;
+ AffineTransform markerTransformation(const FloatPoint& origin, float angle, float strokeWidth) const;
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short) { return false; }
+ virtual FloatRect resourceBoundingBox(RenderObject*) { return FloatRect(); }
+
+ FloatPoint referencePoint() const;
+ float angle() const;
+ SVGMarkerElement::SVGMarkerUnitsType markerUnits() const { return static_cast<SVGMarkerElement::SVGMarkerUnitsType>(static_cast<SVGMarkerElement*>(node())->markerUnits()); }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+private:
+ // Generates a transformation matrix usable to render marker content. Handles scaling the marker content
+ // acording to SVGs markerUnits="strokeWidth" concept, when a strokeWidth value != -1 is passed in.
+ AffineTransform markerContentTransformation(const AffineTransform& contentTransformation, const FloatPoint& origin, float strokeWidth = -1) const;
+
+ AffineTransform viewportTransform() const;
+
+ mutable AffineTransform m_localToParentTransform;
+ FloatRect m_viewport;
+};
+
+}
+#endif
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceMasker.cpp b/Source/WebCore/rendering/RenderSVGResourceMasker.cpp
new file mode 100644
index 0000000..ebbbe93
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceMasker.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-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)
+#include "RenderSVGResourceMasker.h"
+
+#include "AffineTransform.h"
+#include "Element.h"
+#include "FloatPoint.h"
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "Image.h"
+#include "ImageBuffer.h"
+#include "IntRect.h"
+#include "RenderSVGResource.h"
+#include "SVGElement.h"
+#include "SVGImageBufferTools.h"
+#include "SVGMaskElement.h"
+#include "SVGStyledElement.h"
+#include "SVGUnitTypes.h"
+
+#include <wtf/ByteArray.h>
+#include <wtf/Vector.h>
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType;
+
+RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
+ : RenderSVGResourceContainer(node)
+{
+}
+
+RenderSVGResourceMasker::~RenderSVGResourceMasker()
+{
+ if (m_masker.isEmpty())
+ return;
+
+ deleteAllValues(m_masker);
+ m_masker.clear();
+}
+
+void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation)
+{
+ m_maskContentBoundaries = FloatRect();
+ if (!m_masker.isEmpty()) {
+ deleteAllValues(m_masker);
+ m_masker.clear();
+ }
+
+ markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+
+ if (m_masker.contains(client))
+ delete m_masker.take(client);
+
+ markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
+}
+
+bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(object);
+ ASSERT(context);
+#ifndef NDEBUG
+ ASSERT(resourceMode == ApplyToDefaultMode);
+#else
+ UNUSED_PARAM(resourceMode);
+#endif
+
+ if (!m_masker.contains(object))
+ m_masker.set(object, new MaskerData);
+
+ MaskerData* maskerData = m_masker.get(object);
+
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
+
+ FloatRect absoluteTargetRect = absoluteTransform.mapRect(object->repaintRectInLocalCoordinates());
+ FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect);
+
+ if (!maskerData->maskImage && !clampedAbsoluteTargetRect.isEmpty()) {
+ SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
+ if (!maskElement)
+ return false;
+
+ if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, ColorSpaceLinearRGB))
+ return false;
+
+ GraphicsContext* maskImageContext = maskerData->maskImage->context();
+ ASSERT(maskImageContext);
+
+ // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
+ maskImageContext->save();
+ maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
+ maskImageContext->concatCTM(absoluteTransform);
+
+ drawContentIntoMaskImage(maskerData, maskElement, object);
+ }
+
+ if (!maskerData->maskImage)
+ return false;
+
+ SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, maskerData->maskImage);
+ return true;
+}
+
+void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object)
+{
+ GraphicsContext* maskImageContext = maskerData->maskImage->context();
+ ASSERT(maskImageContext);
+
+ // Eventually adjust the mask image context according to the target objectBoundingBox.
+ AffineTransform maskContentTransformation;
+ if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+ maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ maskImageContext->concatCTM(maskContentTransformation);
+ }
+
+ // Draw the content into the ImageBuffer.
+ for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
+ RenderObject* renderer = node->renderer();
+ if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer)
+ continue;
+ RenderStyle* style = renderer->style();
+ if (!style || style->display() == NONE || style->visibility() != VISIBLE)
+ continue;
+ SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
+ }
+
+ maskImageContext->restore();
+
+#if !PLATFORM(CG)
+ maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
+#endif
+
+ // Create the luminance mask.
+ IntRect maskImageRect(IntPoint(), maskerData->maskImage->size());
+ RefPtr<ByteArray> srcPixelArray = maskerData->maskImage->getUnmultipliedImageData(maskImageRect);
+
+ unsigned pixelArrayLength = srcPixelArray->length();
+ for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) {
+ unsigned char a = srcPixelArray->get(pixelOffset + 3);
+ if (!a)
+ continue;
+ unsigned char r = srcPixelArray->get(pixelOffset);
+ unsigned char g = srcPixelArray->get(pixelOffset + 1);
+ unsigned char b = srcPixelArray->get(pixelOffset + 2);
+
+ double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
+ srcPixelArray->set(pixelOffset + 3, luma);
+ }
+
+ maskerData->maskImage->putUnmultipliedImageData(srcPixelArray.get(), maskImageRect.size(), maskImageRect, IntPoint());
+}
+
+void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
+{
+ for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
+ RenderObject* renderer = childNode->renderer();
+ if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
+ continue;
+ RenderStyle* style = renderer->style();
+ if (!style || style->display() == NONE || style->visibility() != VISIBLE)
+ continue;
+ m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
+ }
+}
+
+FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
+{
+ SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
+ ASSERT(maskElement);
+
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+ FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox);
+
+ // Resource was not layouted yet. Give back clipping rect of the mask.
+ if (selfNeedsLayout())
+ return maskBoundaries;
+
+ if (m_maskContentBoundaries.isEmpty())
+ calculateMaskContentRepaintRect();
+
+ FloatRect maskRect = m_maskContentBoundaries;
+ if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ AffineTransform transform;
+ transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ maskRect = transform.mapRect(maskRect);
+ }
+
+ maskRect.intersect(maskBoundaries);
+ return maskRect;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGResourceMasker.h b/Source/WebCore/rendering/RenderSVGResourceMasker.h
new file mode 100644
index 0000000..fddecd0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceMasker.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) Research In Motion Limited 2009-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.
+ *
+ */
+
+#ifndef RenderSVGResourceMasker_h
+#define RenderSVGResourceMasker_h
+
+#if ENABLE(SVG)
+#include "FloatRect.h"
+#include "GraphicsContext.h"
+#include "ImageBuffer.h"
+#include "IntSize.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGMaskElement.h"
+#include "SVGUnitTypes.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+struct MaskerData {
+ OwnPtr<ImageBuffer> maskImage;
+};
+
+class RenderSVGResourceMasker : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourceMasker(SVGMaskElement*);
+ virtual ~RenderSVGResourceMasker();
+
+ virtual const char* renderName() const { return "RenderSVGResourceMasker"; }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual FloatRect resourceBoundingBox(RenderObject*);
+
+ SVGUnitTypes::SVGUnitType maskUnits() const { return toUnitType(static_cast<SVGMaskElement*>(node())->maskUnits()); }
+ SVGUnitTypes::SVGUnitType maskContentUnits() const { return toUnitType(static_cast<SVGMaskElement*>(node())->maskContentUnits()); }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+private:
+ void drawContentIntoMaskImage(MaskerData*, const SVGMaskElement*, RenderObject*);
+ void calculateMaskContentRepaintRect();
+
+ FloatRect m_maskContentBoundaries;
+ HashMap<RenderObject*, MaskerData*> m_masker;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourcePattern.cpp b/Source/WebCore/rendering/RenderSVGResourcePattern.cpp
new file mode 100644
index 0000000..cd64183
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourcePattern.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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)
+#include "RenderSVGResourcePattern.h"
+
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "PatternAttributes.h"
+#include "RenderSVGRoot.h"
+#include "SVGImageBufferTools.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
+
+RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
+ : RenderSVGResourceContainer(node)
+ , m_shouldCollectPatternAttributes(true)
+{
+}
+
+RenderSVGResourcePattern::~RenderSVGResourcePattern()
+{
+ if (m_pattern.isEmpty())
+ return;
+
+ deleteAllValues(m_pattern);
+ m_pattern.clear();
+}
+
+void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation)
+{
+ if (!m_pattern.isEmpty()) {
+ deleteAllValues(m_pattern);
+ m_pattern.clear();
+ }
+
+ m_shouldCollectPatternAttributes = true;
+ markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
+}
+
+void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation)
+{
+ ASSERT(client);
+
+ if (m_pattern.contains(client))
+ delete m_pattern.take(client);
+
+ markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
+}
+
+bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
+{
+ ASSERT(object);
+ ASSERT(style);
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ // Be sure to synchronize all SVG properties on the patternElement _before_ processing any further.
+ // Otherwhise the call to collectPatternAttributes() below, may cause the SVG DOM property
+ // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
+ // PatternData object! Leaving out the line below will cause svg/dynamic-updates/SVGPatternElement-svgdom* to crash.
+ SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
+ if (!patternElement)
+ return false;
+
+ if (m_shouldCollectPatternAttributes) {
+ patternElement->updateAnimatedSVGAttribute(anyQName());
+
+ m_attributes = PatternAttributes();
+ patternElement->collectPatternAttributes(m_attributes);
+ m_shouldCollectPatternAttributes = false;
+ }
+
+ // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
+ // then the given effect (e.g. a gradient or a filter) will be ignored.
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+ if (m_attributes.boundingBoxMode() && objectBoundingBox.isEmpty())
+ return false;
+
+ if (!m_pattern.contains(object))
+ m_pattern.set(object, new PatternData);
+
+ PatternData* patternData = m_pattern.get(object);
+ if (!patternData->pattern) {
+ // If we couldn't determine the pattern content element root, stop here.
+ if (!m_attributes.patternContentElement())
+ return false;
+
+ // Compute all necessary transformations to build the tile image & the pattern.
+ FloatRect tileBoundaries;
+ AffineTransform tileImageTransform;
+ if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform))
+ return false;
+
+ AffineTransform absoluteTransform;
+ SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
+
+ FloatRect absoluteTileBoundaries = absoluteTransform.mapRect(tileBoundaries);
+
+ // Build tile image.
+ OwnPtr<ImageBuffer> tileImage = createTileImage(object, m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform);
+ if (!tileImage)
+ return false;
+
+ RefPtr<Image> copiedImage = tileImage->copyImage();
+ if (!copiedImage)
+ return false;
+
+ // Build pattern.
+ patternData->pattern = Pattern::create(copiedImage, true, true);
+ if (!patternData->pattern)
+ return false;
+
+ // Compute pattern space transformation.
+ patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y());
+ patternData->transform.scale(tileBoundaries.width() / absoluteTileBoundaries.width(), tileBoundaries.height() / absoluteTileBoundaries.height());
+
+ AffineTransform patternTransform = m_attributes.patternTransform();
+ if (!patternTransform.isIdentity())
+ patternData->transform.multiply(patternTransform);
+
+ patternData->pattern->setPatternSpaceTransform(patternData->transform);
+ }
+
+ // Draw pattern
+ context->save();
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ if (resourceMode & ApplyToFillMode) {
+ context->setAlpha(svgStyle->fillOpacity());
+ context->setFillPattern(patternData->pattern);
+ context->setFillRule(svgStyle->fillRule());
+ } else if (resourceMode & ApplyToStrokeMode) {
+ if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
+ patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(object, patternData->transform));
+ context->setAlpha(svgStyle->strokeOpacity());
+ context->setStrokePattern(patternData->pattern);
+ SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
+ }
+
+ if (resourceMode & ApplyToTextMode) {
+ if (resourceMode & ApplyToFillMode) {
+ context->setTextDrawingMode(TextModeFill);
+
+#if PLATFORM(CG)
+ context->applyFillPattern();
+#endif
+ } else if (resourceMode & ApplyToStrokeMode) {
+ context->setTextDrawingMode(TextModeStroke);
+
+#if PLATFORM(CG)
+ context->applyStrokePattern();
+#endif
+ }
+ }
+
+ return true;
+}
+
+void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
+{
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ if (path && !(resourceMode & ApplyToTextMode)) {
+ if (resourceMode & ApplyToFillMode)
+ context->fillPath(*path);
+ else if (resourceMode & ApplyToStrokeMode)
+ context->strokePath(*path);
+ }
+
+ context->restore();
+}
+
+static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
+ const FloatRect& objectBoundingBox,
+ const SVGPatternElement* patternElement)
+{
+ ASSERT(patternElement);
+
+ if (attributes.boundingBoxMode())
+ return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
+ attributes.y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
+ attributes.width().valueAsPercentage() * objectBoundingBox.width(),
+ attributes.height().valueAsPercentage() * objectBoundingBox.height());
+
+ return FloatRect(attributes.x().value(patternElement),
+ attributes.y().value(patternElement),
+ attributes.width().value(patternElement),
+ attributes.height().value(patternElement));
+}
+
+bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer,
+ const PatternAttributes& attributes,
+ const SVGPatternElement* patternElement,
+ FloatRect& patternBoundaries,
+ AffineTransform& tileImageTransform) const
+{
+ ASSERT(renderer);
+ ASSERT(patternElement);
+
+ FloatRect objectBoundingBox = renderer->objectBoundingBox();
+ patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
+ if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0)
+ return false;
+
+ AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(), patternElement->preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
+
+ // Apply viewBox/objectBoundingBox transformations.
+ if (!viewBoxCTM.isIdentity())
+ tileImageTransform = viewBoxCTM;
+ else if (attributes.boundingBoxModeContent()) {
+ tileImageTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
+ tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height());
+ }
+
+ return true;
+}
+
+PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(RenderObject* object,
+ const PatternAttributes& attributes,
+ const FloatRect& tileBoundaries,
+ const FloatRect& absoluteTileBoundaries,
+ const AffineTransform& tileImageTransform) const
+{
+ ASSERT(object);
+
+ // Clamp tile image size against SVG viewport size, as last resort, to avoid allocating huge image buffers.
+ FloatRect contentBoxRect = SVGRenderSupport::findTreeRootObject(object)->contentBoxRect();
+
+ FloatRect clampedAbsoluteTileBoundaries = absoluteTileBoundaries;
+ if (clampedAbsoluteTileBoundaries.width() > contentBoxRect.width())
+ clampedAbsoluteTileBoundaries.setWidth(contentBoxRect.width());
+
+ if (clampedAbsoluteTileBoundaries.height() > contentBoxRect.height())
+ clampedAbsoluteTileBoundaries.setHeight(contentBoxRect.height());
+
+ OwnPtr<ImageBuffer> tileImage;
+
+ if (!SVGImageBufferTools::createImageBuffer(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB))
+ return PassOwnPtr<ImageBuffer>();
+
+ GraphicsContext* tileImageContext = tileImage->context();
+ ASSERT(tileImageContext);
+
+ // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation).
+ tileImageContext->scale(FloatSize(absoluteTileBoundaries.width() / tileBoundaries.width(),
+ absoluteTileBoundaries.height() / tileBoundaries.height()));
+
+ // Apply tile image transformations.
+ if (!tileImageTransform.isIdentity())
+ tileImageContext->concatCTM(tileImageTransform);
+
+ AffineTransform contentTransformation;
+
+ // Draw the content into the ImageBuffer.
+ for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
+ if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
+ continue;
+ SVGImageBufferTools::renderSubtreeToImageBuffer(tileImage.get(), node->renderer(), contentTransformation);
+ }
+
+ return tileImage.release();
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourcePattern.h b/Source/WebCore/rendering/RenderSVGResourcePattern.h
new file mode 100644
index 0000000..ee3fb23
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourcePattern.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourcePattern_h
+#define RenderSVGResourcePattern_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "ImageBuffer.h"
+#include "Pattern.h"
+#include "PatternAttributes.h"
+#include "RenderSVGResourceContainer.h"
+#include "SVGPatternElement.h"
+#include "SVGUnitTypes.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+struct PatternData {
+ RefPtr<Pattern> pattern;
+ AffineTransform transform;
+};
+
+class RenderSVGResourcePattern : public RenderSVGResourceContainer {
+public:
+ RenderSVGResourcePattern(SVGPatternElement*);
+ virtual ~RenderSVGResourcePattern();
+
+ virtual const char* renderName() const { return "RenderSVGResourcePattern"; }
+
+ virtual void removeAllClientsFromCache(bool markForInvalidation = true);
+ virtual void removeClientFromCache(RenderObject*, bool markForInvalidation = true);
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual void postApplyResource(RenderObject*, GraphicsContext*&, unsigned short resourceMode, const Path*);
+ virtual FloatRect resourceBoundingBox(RenderObject*) { return FloatRect(); }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+private:
+ bool buildTileImageTransform(RenderObject*, const PatternAttributes&, const SVGPatternElement*, FloatRect& patternBoundaries, AffineTransform& tileImageTransform) const;
+
+ PassOwnPtr<ImageBuffer> createTileImage(RenderObject*, const PatternAttributes&, const FloatRect& tileBoundaries,
+ const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform) const;
+
+ bool m_shouldCollectPatternAttributes : 1;
+ PatternAttributes m_attributes;
+ HashMap<RenderObject*, PatternData*> m_pattern;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceRadialGradient.cpp b/Source/WebCore/rendering/RenderSVGResourceRadialGradient.cpp
new file mode 100644
index 0000000..300afcb
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceRadialGradient.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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)
+#include "RenderSVGResourceRadialGradient.h"
+
+#include "RadialGradientAttributes.h"
+#include "SVGRadialGradientElement.h"
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceRadialGradient::s_resourceType = RadialGradientResourceType;
+
+RenderSVGResourceRadialGradient::RenderSVGResourceRadialGradient(SVGRadialGradientElement* node)
+ : RenderSVGResourceGradient(node)
+{
+}
+
+RenderSVGResourceRadialGradient::~RenderSVGResourceRadialGradient()
+{
+}
+
+void RenderSVGResourceRadialGradient::collectGradientAttributes(SVGGradientElement* gradientElement)
+{
+ m_attributes = RadialGradientAttributes();
+ static_cast<SVGRadialGradientElement*>(gradientElement)->collectGradientAttributes(m_attributes);
+}
+
+void RenderSVGResourceRadialGradient::buildGradient(GradientData* gradientData, SVGGradientElement* gradientElement) const
+{
+ SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradientElement);
+
+ // Determine gradient focal/center points and radius
+ FloatPoint focalPoint;
+ FloatPoint centerPoint;
+ float radius;
+ radialGradientElement->calculateFocalCenterPointsAndRadius(m_attributes, focalPoint, centerPoint, radius);
+
+ gradientData->gradient = Gradient::create(focalPoint,
+ 0, // SVG does not support a "focus radius"
+ centerPoint,
+ radius);
+
+ gradientData->gradient->setSpreadMethod(m_attributes.spreadMethod());
+
+ // Add stops
+ addStops(gradientData, m_attributes.stops());
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceRadialGradient.h b/Source/WebCore/rendering/RenderSVGResourceRadialGradient.h
new file mode 100644
index 0000000..6492ee3
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceRadialGradient.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceRadialGradient_h
+#define RenderSVGResourceRadialGradient_h
+
+#if ENABLE(SVG)
+#include "RadialGradientAttributes.h"
+#include "RenderSVGResourceGradient.h"
+
+namespace WebCore {
+
+class SVGRadialGradientElement;
+
+class RenderSVGResourceRadialGradient : public RenderSVGResourceGradient {
+public:
+ RenderSVGResourceRadialGradient(SVGRadialGradientElement*);
+ virtual ~RenderSVGResourceRadialGradient();
+
+ virtual const char* renderName() const { return "RenderSVGResourceRadialGradient"; }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+ virtual bool boundingBoxMode() const { return m_attributes.boundingBoxMode(); }
+ virtual void calculateGradientTransform(AffineTransform& transform) { transform = m_attributes.gradientTransform(); }
+ virtual void collectGradientAttributes(SVGGradientElement*);
+ virtual void buildGradient(GradientData*, SVGGradientElement*) const;
+
+private:
+ RadialGradientAttributes m_attributes;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceSolidColor.cpp b/Source/WebCore/rendering/RenderSVGResourceSolidColor.cpp
new file mode 100644
index 0000000..9fbda40
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceSolidColor.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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)
+#include "RenderSVGResourceSolidColor.h"
+
+#include "GraphicsContext.h"
+#include "RenderStyle.h"
+#include "SVGRenderSupport.h"
+
+#if PLATFORM(SKIA) && !PLATFORM(ANDROID)
+#include "PlatformContextSkia.h"
+#endif
+
+namespace WebCore {
+
+RenderSVGResourceType RenderSVGResourceSolidColor::s_resourceType = SolidColorResourceType;
+
+RenderSVGResourceSolidColor::RenderSVGResourceSolidColor()
+{
+}
+
+RenderSVGResourceSolidColor::~RenderSVGResourceSolidColor()
+{
+}
+
+bool RenderSVGResourceSolidColor::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
+{
+ // We are NOT allowed to ASSERT(object) here, unlike all other resources.
+ // RenderSVGResourceSolidColor is the only resource which may be used from HTML, when rendering
+ // SVG Fonts for a HTML document. This will be indicated by a null RenderObject pointer.
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ const SVGRenderStyle* svgStyle = style ? style->svgStyle() : 0;
+ ColorSpace colorSpace = style ? style->colorSpace() : ColorSpaceDeviceRGB;
+
+ if (resourceMode & ApplyToFillMode) {
+ context->setAlpha(svgStyle ? svgStyle->fillOpacity() : 1.0f);
+ context->setFillColor(m_color, colorSpace);
+ context->setFillRule(svgStyle ? svgStyle->fillRule() : RULE_NONZERO);
+
+ if (resourceMode & ApplyToTextMode)
+ context->setTextDrawingMode(TextModeFill);
+ } else if (resourceMode & ApplyToStrokeMode) {
+ context->setAlpha(svgStyle ? svgStyle->strokeOpacity() : 1.0f);
+ context->setStrokeColor(m_color, colorSpace);
+
+ if (style)
+ SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
+
+ if (resourceMode & ApplyToTextMode)
+ context->setTextDrawingMode(TextModeStroke);
+ }
+
+ return true;
+}
+
+void RenderSVGResourceSolidColor::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
+{
+ ASSERT(context);
+ ASSERT(resourceMode != ApplyToDefaultMode);
+
+ if (path && !(resourceMode & ApplyToTextMode)) {
+ if (resourceMode & ApplyToFillMode)
+ context->fillPath(*path);
+ else if (resourceMode & ApplyToStrokeMode)
+ context->strokePath(*path);
+ }
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGResourceSolidColor.h b/Source/WebCore/rendering/RenderSVGResourceSolidColor.h
new file mode 100644
index 0000000..72de3b2
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGResourceSolidColor.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGResourceSolidColor_h
+#define RenderSVGResourceSolidColor_h
+
+#if ENABLE(SVG)
+#include "Color.h"
+#include "FloatRect.h"
+#include "RenderSVGResource.h"
+
+namespace WebCore {
+
+class RenderSVGResourceSolidColor : public RenderSVGResource {
+public:
+ RenderSVGResourceSolidColor();
+ virtual ~RenderSVGResourceSolidColor();
+
+ virtual void removeAllClientsFromCache(bool = true) { }
+ virtual void removeClientFromCache(RenderObject*, bool = true) { }
+
+ virtual bool applyResource(RenderObject*, RenderStyle*, GraphicsContext*&, unsigned short resourceMode);
+ virtual void postApplyResource(RenderObject*, GraphicsContext*&, unsigned short resourceMode, const Path*);
+ virtual FloatRect resourceBoundingBox(RenderObject*) { return FloatRect(); }
+
+ virtual RenderSVGResourceType resourceType() const { return s_resourceType; }
+ static RenderSVGResourceType s_resourceType;
+
+ const Color& color() const { return m_color; }
+ void setColor(const Color& color) { m_color = color; }
+
+private:
+ Color m_color;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGRoot.cpp b/Source/WebCore/rendering/RenderSVGRoot.cpp
new file mode 100644
index 0000000..215aac7
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGRoot.cpp
@@ -0,0 +1,361 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org>
+ 2007 Eric Seidel <eric@webkit.org>
+ 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
+ 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGRoot.h"
+
+#include "GraphicsContext.h"
+#include "HitTestResult.h"
+#include "RenderSVGContainer.h"
+#include "RenderSVGResource.h"
+#include "RenderView.h"
+#include "SVGLength.h"
+#include "SVGRenderSupport.h"
+#include "SVGResources.h"
+#include "SVGSVGElement.h"
+#include "SVGStyledElement.h"
+#include "TransformState.h"
+
+#if ENABLE(FILTERS)
+#include "RenderSVGResourceFilter.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node)
+ : RenderBox(node)
+ , m_isLayoutSizeChanged(false)
+ , m_needsBoundariesOrTransformUpdate(true)
+{
+ setReplaced(true);
+}
+
+RenderSVGRoot::~RenderSVGRoot()
+{
+}
+
+void RenderSVGRoot::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ int borderAndPadding = borderAndPaddingWidth();
+ int width = computeReplacedLogicalWidth(false) + borderAndPadding;
+
+ if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
+ width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? borderAndPadding : 0));
+
+ if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) {
+ m_minPreferredLogicalWidth = 0;
+ m_maxPreferredLogicalWidth = width;
+ } else
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = width;
+
+ setPreferredLogicalWidthsDirty(false);
+}
+
+int RenderSVGRoot::computeReplacedLogicalWidth(bool includeMaxWidth) const
+{
+ int replacedWidth = RenderBox::computeReplacedLogicalWidth(includeMaxWidth);
+ if (!style()->logicalWidth().isPercent())
+ return replacedWidth;
+
+ // FIXME: Investigate in size rounding issues
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+ return static_cast<int>(roundf(replacedWidth * svg->currentScale()));
+}
+
+int RenderSVGRoot::computeReplacedLogicalHeight() const
+{
+ int replacedHeight = RenderBox::computeReplacedLogicalHeight();
+ if (!style()->logicalHeight().isPercent())
+ return replacedHeight;
+
+ // FIXME: Investigate in size rounding issues
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+ return static_cast<int>(roundf(replacedHeight * svg->currentScale()));
+}
+
+void RenderSVGRoot::layout()
+{
+ ASSERT(needsLayout());
+
+ // Arbitrary affine transforms are incompatible with LayoutState.
+ view()->disableLayoutState();
+
+ bool needsLayout = selfNeedsLayout();
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && needsLayout);
+
+ IntSize oldSize(width(), height());
+ computeLogicalWidth();
+ computeLogicalHeight();
+ calcViewport();
+
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+ m_isLayoutSizeChanged = svg->hasRelativeLengths() && oldSize != size();
+
+ SVGRenderSupport::layoutChildren(this, needsLayout);
+ m_isLayoutSizeChanged = false;
+
+ // At this point LayoutRepainter already grabbed the old bounds,
+ // recalculate them now so repaintAfterLayout() uses the new bounds.
+ if (m_needsBoundariesOrTransformUpdate) {
+ updateCachedBoundaries();
+ m_needsBoundariesOrTransformUpdate = false;
+ }
+
+ repainter.repaintAfterLayout();
+
+ view()->enableLayoutState();
+ setNeedsLayout(false);
+}
+
+bool RenderSVGRoot::selfWillPaint()
+{
+#if ENABLE(FILTERS)
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
+ return resources && resources->filter();
+#else
+ return false;
+#endif
+}
+
+void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY)
+{
+ if (paintInfo.context->paintingDisabled())
+ return;
+
+ bool isVisible = style()->visibility() == VISIBLE;
+ IntPoint parentOriginInContainer(parentX, parentY);
+ IntPoint borderBoxOriginInContainer = parentOriginInContainer + parentOriginToBorderBox();
+
+ if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground) && isVisible)
+ paintBoxDecorations(paintInfo, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y());
+
+ if (paintInfo.phase == PaintPhaseBlockBackground)
+ return;
+
+ // An empty viewport disables rendering. FIXME: Should we still render filters?
+ if (m_viewportSize.isEmpty())
+ return;
+
+ // Don't paint if we don't have kids, except if we have filters we should paint those.
+ if (!firstChild() && !selfWillPaint())
+ return;
+
+ // Make a copy of the PaintInfo because applyTransform will modify the damage rect.
+ PaintInfo childPaintInfo(paintInfo);
+ childPaintInfo.context->save();
+
+ // Apply initial viewport clip - not affected by overflow handling
+ childPaintInfo.context->clip(overflowClipRect(borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y()));
+
+ // Convert from container offsets (html renderers) to a relative transform (svg renderers).
+ // Transform from our paint container's coordinate system to our local coords.
+ childPaintInfo.applyTransform(localToRepaintContainerTransform(parentOriginInContainer));
+
+ bool continueRendering = true;
+ if (childPaintInfo.phase == PaintPhaseForeground)
+ continueRendering = SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo);
+
+ if (continueRendering)
+ RenderBox::paint(childPaintInfo, 0, 0);
+
+ if (childPaintInfo.phase == PaintPhaseForeground)
+ SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, paintInfo.context);
+
+ childPaintInfo.context->restore();
+
+ if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && isVisible)
+ paintOutline(paintInfo.context, borderBoxOriginInContainer.x(), borderBoxOriginInContainer.y(), width(), height());
+}
+
+void RenderSVGRoot::destroy()
+{
+ SVGResourcesCache::clientDestroyed(this);
+ RenderBox::destroy();
+}
+
+void RenderSVGRoot::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (diff == StyleDifferenceLayout)
+ setNeedsBoundariesUpdate();
+ RenderBox::styleWillChange(diff, newStyle);
+}
+
+void RenderSVGRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBox::styleDidChange(diff, oldStyle);
+ SVGResourcesCache::clientStyleChanged(this, diff, style());
+}
+
+void RenderSVGRoot::updateFromElement()
+{
+ RenderBox::updateFromElement();
+ SVGResourcesCache::clientUpdatedFromElement(this, style());
+}
+
+void RenderSVGRoot::calcViewport()
+{
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+
+ if (!svg->hasSetContainerSize()) {
+ // In the normal case of <svg> being stand-alone or in a CSSBoxModel object we use
+ // RenderBox::width()/height() (which pulls data from RenderStyle)
+ m_viewportSize = FloatSize(width(), height());
+ return;
+ }
+
+ // In the SVGImage case grab the SVGLength values off of SVGSVGElement and use
+ // the special relativeWidthValue accessors which respect the specified containerSize
+ // FIXME: Check how SVGImage + zooming is supposed to be handled?
+ SVGLength width = svg->width();
+ SVGLength height = svg->height();
+ m_viewportSize = FloatSize(width.unitType() == LengthTypePercentage ? svg->relativeWidthValue() : width.value(svg),
+ height.unitType() == LengthTypePercentage ? svg->relativeHeightValue() : height.value(svg));
+}
+
+// RenderBox methods will expect coordinates w/o any transforms in coordinates
+// relative to our borderBox origin. This method gives us exactly that.
+AffineTransform RenderSVGRoot::localToBorderBoxTransform() const
+{
+ IntSize borderAndPadding = borderOriginToContentBox();
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+ float scale = svg->currentScale();
+ FloatPoint translate = svg->currentTranslate();
+ AffineTransform ctm(scale, 0, 0, scale, borderAndPadding.width() + translate.x(), borderAndPadding.height() + translate.y());
+ return svg->viewBoxToViewTransform(width() / scale, height() / scale) * ctm;
+}
+
+IntSize RenderSVGRoot::parentOriginToBorderBox() const
+{
+ return IntSize(x(), y());
+}
+
+IntSize RenderSVGRoot::borderOriginToContentBox() const
+{
+ return IntSize(borderLeft() + paddingLeft(), borderTop() + paddingTop());
+}
+
+AffineTransform RenderSVGRoot::localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const
+{
+ AffineTransform parentToContainer(localToParentTransform());
+ return parentToContainer.translateRight(parentOriginInContainer.x(), parentOriginInContainer.y());
+}
+
+const AffineTransform& RenderSVGRoot::localToParentTransform() const
+{
+ IntSize parentToBorderBoxOffset = parentOriginToBorderBox();
+
+ AffineTransform borderBoxOriginToParentOrigin(localToBorderBoxTransform());
+ borderBoxOriginToParentOrigin.translateRight(parentToBorderBoxOffset.width(), parentToBorderBoxOffset.height());
+
+ m_localToParentTransform = borderBoxOriginToParentOrigin;
+ return m_localToParentTransform;
+}
+
+IntRect RenderSVGRoot::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
+}
+
+void RenderSVGRoot::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+ // Apply our local transforms (except for x/y translation), then our shadow,
+ // and then call RenderBox's method to handle all the normal CSS Box model bits
+ repaintRect = localToBorderBoxTransform().mapRect(repaintRect);
+
+ // Apply initial viewport clip - not affected by overflow settings
+ repaintRect.intersect(enclosingIntRect(FloatRect(FloatPoint(), m_viewportSize)));
+
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+
+ RenderBox::computeRectForRepaint(repaintContainer, repaintRect, fixed);
+}
+
+void RenderSVGRoot::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState) const
+{
+ ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
+ ASSERT(useTransforms); // mapping a point through SVG w/o respecting trasnforms is useless.
+
+ // Transform to our border box and let RenderBox transform the rest of the way.
+ transformState.applyTransform(localToBorderBoxTransform());
+ RenderBox::mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
+}
+
+void RenderSVGRoot::updateCachedBoundaries()
+{
+ m_objectBoundingBox = FloatRect();
+ m_strokeBoundingBox = FloatRect();
+ m_repaintBoundingBox = FloatRect();
+
+ SVGRenderSupport::computeContainerBoundingBoxes(this, m_objectBoundingBox, m_strokeBoundingBox, m_repaintBoundingBox);
+ SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
+ m_repaintBoundingBox.inflate(borderAndPaddingWidth());
+}
+
+bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction)
+{
+ IntPoint pointInContainer(_x, _y);
+ IntSize containerToParentOffset(_tx, _ty);
+
+ IntPoint pointInParent = pointInContainer - containerToParentOffset;
+ IntPoint pointInBorderBox = pointInParent - parentOriginToBorderBox();
+
+ // Note: For now, we're ignoring hits to border and padding for <svg>
+ IntPoint pointInContentBox = pointInBorderBox - borderOriginToContentBox();
+ if (!contentBoxRect().contains(pointInContentBox))
+ return false;
+
+ IntPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
+
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
+ // FIXME: CSS/HTML assumes the local point is relative to the border box, right?
+ updateHitTestResult(result, pointInBorderBox);
+ // FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet.
+ result.addNodeToRectBasedTestResult(child->node(), _x, _y);
+ return true;
+ }
+ }
+
+ // If we didn't early exit above, we've just hit the container <svg> element. Unlike SVG 1.1, 2nd Edition allows container elements to be hit.
+ if (hitTestAction == HitTestBlockBackground) {
+ // Only return true here, if the last hit testing phase 'BlockBackground' is executed. If we'd return true in the 'Foreground' phase,
+ // hit testing would stop immediately. For SVG only trees this doesn't matter. Though when we have a <foreignObject> subtree we need
+ // to be able to detect hits on the background of a <div> element. If we'd return true here in the 'Foreground' phase, we are not able
+ // to detect these hits anymore.
+ updateHitTestResult(result, roundedIntPoint(localPoint));
+ return true;
+ }
+
+ return false;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGRoot.h b/Source/WebCore/rendering/RenderSVGRoot.h
new file mode 100644
index 0000000..1b6aa22
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGRoot.h
@@ -0,0 +1,120 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007 Rob Buis <buis@kde.org>
+ Copyright (C) 2009 Google, Inc. All rights reserved.
+ Copyright (C) 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
+ 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 RenderSVGRoot_h
+#define RenderSVGRoot_h
+
+#if ENABLE(SVG)
+#include "RenderBox.h"
+#include "FloatRect.h"
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+class SVGStyledElement;
+class AffineTransform;
+
+class RenderSVGRoot : public RenderBox {
+public:
+ explicit RenderSVGRoot(SVGStyledElement*);
+ virtual ~RenderSVGRoot();
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ bool isLayoutSizeChanged() const { return m_isLayoutSizeChanged; }
+ virtual void setNeedsBoundariesUpdate() { m_needsBoundariesOrTransformUpdate = true; }
+ virtual void setNeedsTransformUpdate() { m_needsBoundariesOrTransformUpdate = true; }
+
+private:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual bool isSVGRoot() const { return true; }
+ virtual const char* renderName() const { return "RenderSVGRoot"; }
+
+ virtual void computePreferredLogicalWidths();
+ virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const;
+ virtual int computeReplacedLogicalHeight() const;
+ virtual void layout();
+ virtual void paint(PaintInfo&, int parentX, int parentY);
+
+ virtual void destroy();
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void updateFromElement();
+
+ virtual const AffineTransform& localToParentTransform() const;
+
+ bool fillContains(const FloatPoint&) const;
+ bool strokeContains(const FloatPoint&) const;
+
+ virtual FloatRect objectBoundingBox() const { return m_objectBoundingBox; }
+ virtual FloatRect strokeBoundingBox() const { return m_strokeBoundingBox; }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
+
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed);
+
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+
+ void calcViewport();
+
+ bool selfWillPaint();
+ void updateCachedBoundaries();
+
+ IntSize parentOriginToBorderBox() const;
+ IntSize borderOriginToContentBox() const;
+ AffineTransform localToRepaintContainerTransform(const IntPoint& parentOriginInContainer) const;
+ AffineTransform localToBorderBoxTransform() const;
+
+ RenderObjectChildList m_children;
+ FloatSize m_viewportSize;
+ FloatRect m_objectBoundingBox;
+ FloatRect m_strokeBoundingBox;
+ FloatRect m_repaintBoundingBox;
+ mutable AffineTransform m_localToParentTransform;
+ bool m_isLayoutSizeChanged : 1;
+ bool m_needsBoundariesOrTransformUpdate : 1;
+};
+
+inline RenderSVGRoot* toRenderSVGRoot(RenderObject* object)
+{
+ ASSERT(!object || object->isSVGRoot());
+ return static_cast<RenderSVGRoot*>(object);
+}
+
+inline const RenderSVGRoot* toRenderSVGRoot(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGRoot());
+ return static_cast<const RenderSVGRoot*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGRoot(const RenderSVGRoot*);
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGRoot_h
diff --git a/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp b/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp
new file mode 100644
index 0000000..11b398a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.cpp
@@ -0,0 +1,108 @@
+/*
+ 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
+ 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGShadowTreeRootContainer.h"
+
+#include "MouseEvent.h"
+#include "SVGShadowTreeElements.h"
+#include "SVGUseElement.h"
+
+namespace WebCore {
+
+RenderSVGShadowTreeRootContainer::RenderSVGShadowTreeRootContainer(SVGUseElement* node)
+ : RenderSVGTransformableContainer(node)
+ , m_recreateTree(false)
+{
+}
+
+RenderSVGShadowTreeRootContainer::~RenderSVGShadowTreeRootContainer()
+{
+ if (m_shadowRoot && m_shadowRoot->attached()) {
+ m_shadowRoot->detach();
+ m_shadowRoot->clearShadowHost();
+ }
+}
+
+void RenderSVGShadowTreeRootContainer::updateStyle(Node::StyleChange change)
+{
+ if (m_shadowRoot && m_shadowRoot->attached())
+ m_shadowRoot->recalcStyle(change);
+}
+
+void RenderSVGShadowTreeRootContainer::updateFromElement()
+{
+ bool hadExistingTree = m_shadowRoot;
+
+ SVGUseElement* useElement = static_cast<SVGUseElement*>(node());
+ if (!m_shadowRoot) {
+ ASSERT(!m_recreateTree);
+ m_shadowRoot = SVGShadowTreeRootElement::create(document(), useElement);
+ useElement->buildPendingResource();
+ }
+
+ ASSERT(m_shadowRoot->shadowHost() == useElement);
+
+ bool shouldRecreateTree = m_recreateTree;
+ if (m_recreateTree) {
+ ASSERT(hadExistingTree);
+
+ if (m_shadowRoot->attached())
+ m_shadowRoot->detach();
+
+ m_shadowRoot->removeAllChildren();
+ m_recreateTree = false;
+ }
+
+ // Only rebuild the shadow tree, if we a) never had a tree or b) we were specifically asked to do so
+ // If the use element is a pending resource, and a) or b) is true, do nothing, and wait for the use
+ // element to be asked to buildPendingResource(), this will call us again, with m_recreateTrue=true.
+ if ((shouldRecreateTree || !hadExistingTree) && !useElement->isPendingResource()) {
+ useElement->buildShadowAndInstanceTree(m_shadowRoot.get());
+
+ // Attach shadow root element
+ m_shadowRoot->attachElement(style(), renderArena());
+
+ // Attach subtree, as if it was a regular non-shadow tree
+ for (Node* child = m_shadowRoot->firstChild(); child; child = child->nextSibling())
+ child->attach();
+ }
+
+ ASSERT(!m_recreateTree);
+ RenderSVGTransformableContainer::updateFromElement();
+}
+
+void RenderSVGShadowTreeRootContainer::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderSVGTransformableContainer::styleDidChange(diff, oldStyle);
+
+ if (RenderObject* shadowRootRenderer = m_shadowRoot ? m_shadowRoot->renderer() : 0)
+ shadowRootRenderer->setStyle(style());
+}
+
+Node* RenderSVGShadowTreeRootContainer::rootElement() const
+{
+ return m_shadowRoot.get();
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.h b/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.h
new file mode 100644
index 0000000..bff2a87
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGShadowTreeRootContainer.h
@@ -0,0 +1,54 @@
+/*
+ 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
+ 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 RenderSVGShadowTreeRootContainer_h
+#define RenderSVGShadowTreeRootContainer_h
+
+#if ENABLE(SVG)
+#include "RenderSVGTransformableContainer.h"
+
+namespace WebCore {
+
+class SVGUseElement;
+class SVGShadowTreeRootElement;
+
+class RenderSVGShadowTreeRootContainer : public RenderSVGTransformableContainer {
+public:
+ RenderSVGShadowTreeRootContainer(SVGUseElement*);
+ virtual ~RenderSVGShadowTreeRootContainer();
+
+ virtual bool isSVGShadowTreeRootContainer() const { return true; }
+
+ void markShadowTreeForRecreation() { m_recreateTree = true; }
+ void updateStyle(Node::StyleChange);
+ virtual void updateFromElement();
+
+ Node* rootElement() const;
+
+private:
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ bool m_recreateTree;
+ RefPtr<SVGShadowTreeRootElement> m_shadowRoot;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/RenderSVGTransformableContainer.cpp b/Source/WebCore/rendering/RenderSVGTransformableContainer.cpp
new file mode 100644
index 0000000..3c91170
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGTransformableContainer.cpp
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2004, 2005 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2006 Rob Buis <buis@kde.org>
+ 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.
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGTransformableContainer.h"
+
+#include "SVGNames.h"
+#include "SVGShadowTreeElements.h"
+#include "SVGStyledTransformableElement.h"
+
+namespace WebCore {
+
+RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransformableElement* node)
+ : RenderSVGContainer(node)
+ , m_needsTransformUpdate(true)
+{
+}
+
+bool RenderSVGTransformableContainer::calculateLocalTransform()
+{
+ SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
+
+ bool needsUpdate = m_needsTransformUpdate;
+ if (needsUpdate) {
+ m_localTransform = element->animatedLocalTransform();
+ m_needsTransformUpdate = false;
+ }
+
+ if (!element->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(element)->isShadowTreeContainerElement())
+ return needsUpdate;
+
+ FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(element)->containerTranslation();
+ if (translation.width() == 0 && translation.height() == 0)
+ return needsUpdate;
+
+ // FIXME: Could optimize this case for use to avoid refetching the animatedLocalTransform() here, if only the containerTranslation() changed.
+ if (!needsUpdate)
+ m_localTransform = element->animatedLocalTransform();
+
+ m_localTransform.translate(translation.width(), translation.height());
+ return true;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGTransformableContainer.h b/Source/WebCore/rendering/RenderSVGTransformableContainer.h
new file mode 100644
index 0000000..b49a403
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGTransformableContainer.h
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ 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
+ 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 RenderSVGTransformableContainer_h
+#define RenderSVGTransformableContainer_h
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+
+namespace WebCore {
+
+ class SVGStyledTransformableElement;
+ class RenderSVGTransformableContainer : public RenderSVGContainer {
+ public:
+ explicit RenderSVGTransformableContainer(SVGStyledTransformableElement*);
+
+ virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
+ virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+
+ private:
+ virtual bool calculateLocalTransform();
+ virtual AffineTransform localTransform() const { return m_localTransform; }
+
+ bool m_needsTransformUpdate : 1;
+ AffineTransform m_localTransform;
+ };
+}
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGTransformableContainer_h
diff --git a/Source/WebCore/rendering/RenderSVGViewportContainer.cpp b/Source/WebCore/rendering/RenderSVGViewportContainer.cpp
new file mode 100644
index 0000000..8031328
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGViewportContainer.cpp
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007 Rob Buis <buis@kde.org>
+ 2007 Eric Seidel <eric@webkit.org>
+ 2009 Google, Inc.
+ Copyright (C) Research In Motion Limited 2009-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
+ 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGViewportContainer.h"
+
+#include "GraphicsContext.h"
+#include "RenderView.h"
+#include "SVGNames.h"
+#include "SVGSVGElement.h"
+
+namespace WebCore {
+
+RenderSVGViewportContainer::RenderSVGViewportContainer(SVGStyledElement* node)
+ : RenderSVGContainer(node)
+{
+}
+
+void RenderSVGViewportContainer::applyViewportClip(PaintInfo& paintInfo)
+{
+ if (SVGRenderSupport::isOverflowHidden(this))
+ paintInfo.context->clip(m_viewport);
+}
+
+void RenderSVGViewportContainer::calcViewport()
+{
+ SVGElement* element = static_cast<SVGElement*>(node());
+ if (element->hasTagName(SVGNames::svgTag)) {
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(element);
+
+ FloatRect oldViewport = m_viewport;
+ m_viewport = FloatRect(svg->x().value(svg)
+ , svg->y().value(svg)
+ , svg->width().value(svg)
+ , svg->height().value(svg));
+
+ if (oldViewport != m_viewport)
+ setNeedsBoundariesUpdate();
+ }
+}
+
+AffineTransform RenderSVGViewportContainer::viewportTransform() const
+{
+ if (node()->hasTagName(SVGNames::svgTag)) {
+ SVGSVGElement* svg = static_cast<SVGSVGElement*>(node());
+ return svg->viewBoxToViewTransform(m_viewport.width(), m_viewport.height());
+ }
+
+ return AffineTransform();
+}
+
+const AffineTransform& RenderSVGViewportContainer::localToParentTransform() const
+{
+ AffineTransform viewportTranslation(viewportTransform());
+ m_localToParentTransform = viewportTranslation.translateRight(m_viewport.x(), m_viewport.y());
+ return m_localToParentTransform;
+ // If this class were ever given a localTransform(), then the above would read:
+ // return viewportTransform() * localTransform() * viewportTranslation;
+}
+
+bool RenderSVGViewportContainer::pointIsInsideViewportClip(const FloatPoint& pointInParent)
+{
+ // Respect the viewport clip (which is in parent coords)
+ if (!SVGRenderSupport::isOverflowHidden(this))
+ return true;
+
+ return m_viewport.contains(pointInParent);
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/RenderSVGViewportContainer.h b/Source/WebCore/rendering/RenderSVGViewportContainer.h
new file mode 100644
index 0000000..5373ca8
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSVGViewportContainer.h
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007 Rob Buis <buis@kde.org>
+ 2009 Google, Inc.
+ Copyright (C) 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
+ 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 RenderSVGViewportContainer_h
+#define RenderSVGViewportContainer_h
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+
+namespace WebCore {
+
+// This is used for non-root <svg> elements and <marker> elements, neither of which are SVGTransformable
+// thus we inherit from RenderSVGContainer instead of RenderSVGTransformableContainer
+class RenderSVGViewportContainer : public RenderSVGContainer {
+public:
+ explicit RenderSVGViewportContainer(SVGStyledElement*);
+
+private:
+ virtual bool isSVGContainer() const { return true; }
+ virtual bool isSVGViewportContainer() const { return true; }
+ virtual const char* renderName() const { return "RenderSVGViewportContainer"; }
+
+ AffineTransform viewportTransform() const;
+ virtual const AffineTransform& localToParentTransform() const;
+
+ virtual void calcViewport();
+
+ virtual void applyViewportClip(PaintInfo&);
+ virtual bool pointIsInsideViewportClip(const FloatPoint& pointInParent);
+
+ FloatRect m_viewport;
+ mutable AffineTransform m_localToParentTransform;
+};
+
+inline RenderSVGViewportContainer* toRenderSVGViewportContainer(RenderObject* object)
+{
+ ASSERT(!object || !strcmp(object->renderName(), "RenderSVGViewportContainer"));
+ return static_cast<RenderSVGViewportContainer*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGViewportContainer(const RenderSVGViewportContainer*);
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGViewportContainer_h
diff --git a/Source/WebCore/rendering/RenderScrollbar.cpp b/Source/WebCore/rendering/RenderScrollbar.cpp
new file mode 100644
index 0000000..44a0126
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbar.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2008, 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.
+ */
+
+#include "config.h"
+#include "RenderScrollbar.h"
+
+#include "Frame.h"
+#include "FrameView.h"
+#include "RenderPart.h"
+#include "RenderScrollbarPart.h"
+#include "RenderScrollbarTheme.h"
+
+namespace WebCore {
+
+PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
+{
+ return adoptRef(new RenderScrollbar(client, orientation, renderer, owningFrame));
+}
+
+RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer, Frame* owningFrame)
+ : Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
+ , m_owner(renderer)
+ , m_owningFrame(owningFrame)
+{
+ // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
+
+ // Update the scrollbar size.
+ int width = 0;
+ int height = 0;
+ updateScrollbarPart(ScrollbarBGPart);
+ if (RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart)) {
+ part->layout();
+ width = part->width();
+ height = part->height();
+ } else if (this->orientation() == HorizontalScrollbar)
+ width = this->width();
+ else
+ height = this->height();
+
+ setFrameRect(IntRect(0, 0, width, height));
+}
+
+RenderScrollbar::~RenderScrollbar()
+{
+ ASSERT(m_parts.isEmpty());
+}
+
+RenderBox* RenderScrollbar::owningRenderer() const
+{
+ if (m_owningFrame) {
+ RenderBox* currentRenderer = m_owningFrame->ownerRenderer();
+ return currentRenderer;
+ }
+ return m_owner;
+}
+
+void RenderScrollbar::setParent(ScrollView* parent)
+{
+ Scrollbar::setParent(parent);
+ if (!parent) {
+ // Destroy all of the scrollbar's RenderBoxes.
+ updateScrollbarParts(true);
+ }
+}
+
+void RenderScrollbar::setEnabled(bool e)
+{
+ bool wasEnabled = enabled();
+ Scrollbar::setEnabled(e);
+ if (wasEnabled != e)
+ updateScrollbarParts();
+}
+
+void RenderScrollbar::styleChanged()
+{
+ updateScrollbarParts();
+}
+
+void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
+{
+ if (context->updatingControlTints()) {
+ updateScrollbarParts();
+ return;
+ }
+ Scrollbar::paint(context, damageRect);
+}
+
+void RenderScrollbar::setHoveredPart(ScrollbarPart part)
+{
+ if (part == m_hoveredPart)
+ return;
+
+ ScrollbarPart oldPart = m_hoveredPart;
+ m_hoveredPart = part;
+
+ updateScrollbarPart(oldPart);
+ updateScrollbarPart(m_hoveredPart);
+
+ updateScrollbarPart(ScrollbarBGPart);
+ updateScrollbarPart(TrackBGPart);
+}
+
+void RenderScrollbar::setPressedPart(ScrollbarPart part)
+{
+ ScrollbarPart oldPart = m_pressedPart;
+ Scrollbar::setPressedPart(part);
+
+ updateScrollbarPart(oldPart);
+ updateScrollbarPart(part);
+
+ updateScrollbarPart(ScrollbarBGPart);
+ updateScrollbarPart(TrackBGPart);
+}
+
+static ScrollbarPart s_styleResolvePart;
+static RenderScrollbar* s_styleResolveScrollbar;
+
+RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
+{
+ return s_styleResolveScrollbar;
+}
+
+ScrollbarPart RenderScrollbar::partForStyleResolve()
+{
+ return s_styleResolvePart;
+}
+
+PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
+{
+ if (!m_owner)
+ return 0;
+
+ s_styleResolvePart = partType;
+ s_styleResolveScrollbar = this;
+ RefPtr<RenderStyle> result = owningRenderer()->getUncachedPseudoStyle(pseudoId, owningRenderer()->style());
+ s_styleResolvePart = NoPart;
+ s_styleResolveScrollbar = 0;
+
+ // Scrollbars for root frames should always have background color
+ // unless explicitly specified as transparent. So we force it.
+ // This is because WebKit assumes scrollbar to be always painted and missing background
+ // causes visual artifact like non-repainted dirty region.
+ if (result && m_owningFrame && m_owningFrame->view() && !m_owningFrame->view()->isTransparent() && !result->hasBackground())
+ result->setBackgroundColor(Color::white);
+
+ return result;
+}
+
+void RenderScrollbar::updateScrollbarParts(bool destroy)
+{
+ updateScrollbarPart(ScrollbarBGPart, destroy);
+ updateScrollbarPart(BackButtonStartPart, destroy);
+ updateScrollbarPart(ForwardButtonStartPart, destroy);
+ updateScrollbarPart(BackTrackPart, destroy);
+ updateScrollbarPart(ThumbPart, destroy);
+ updateScrollbarPart(ForwardTrackPart, destroy);
+ updateScrollbarPart(BackButtonEndPart, destroy);
+ updateScrollbarPart(ForwardButtonEndPart, destroy);
+ updateScrollbarPart(TrackBGPart, destroy);
+
+ if (destroy)
+ return;
+
+ // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout.
+ bool isHorizontal = orientation() == HorizontalScrollbar;
+ int oldThickness = isHorizontal ? height() : width();
+ int newThickness = 0;
+ RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
+ if (part) {
+ part->layout();
+ newThickness = isHorizontal ? part->height() : part->width();
+ }
+
+ if (newThickness != oldThickness) {
+ setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()));
+ owningRenderer()->setChildNeedsLayout(true);
+ }
+}
+
+static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
+{
+ switch (part) {
+ case BackButtonStartPart:
+ case ForwardButtonStartPart:
+ case BackButtonEndPart:
+ case ForwardButtonEndPart:
+ return SCROLLBAR_BUTTON;
+ case BackTrackPart:
+ case ForwardTrackPart:
+ return SCROLLBAR_TRACK_PIECE;
+ case ThumbPart:
+ return SCROLLBAR_THUMB;
+ case TrackBGPart:
+ return SCROLLBAR_TRACK;
+ case ScrollbarBGPart:
+ return SCROLLBAR;
+ case NoPart:
+ case AllParts:
+ break;
+ }
+ ASSERT_NOT_REACHED();
+ return SCROLLBAR;
+}
+
+void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
+{
+ if (partType == NoPart)
+ return;
+
+ RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0;
+
+ bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
+
+ if (needRenderer && partStyle->display() != BLOCK) {
+ // See if we are a button that should not be visible according to OS settings.
+ ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
+ switch (partType) {
+ case BackButtonStartPart:
+ needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
+ buttonsPlacement == ScrollbarButtonsDoubleBoth);
+ break;
+ case ForwardButtonStartPart:
+ needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
+ break;
+ case BackButtonEndPart:
+ needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
+ break;
+ case ForwardButtonEndPart:
+ needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
+ buttonsPlacement == ScrollbarButtonsDoubleBoth);
+ break;
+ default:
+ break;
+ }
+ }
+
+ RenderScrollbarPart* partRenderer = m_parts.get(partType);
+ if (!partRenderer && needRenderer) {
+ partRenderer = new (owningRenderer()->renderArena()) RenderScrollbarPart(owningRenderer()->document(), this, partType);
+ m_parts.set(partType, partRenderer);
+ } else if (partRenderer && !needRenderer) {
+ m_parts.remove(partType);
+ partRenderer->destroy();
+ partRenderer = 0;
+ }
+
+ if (partRenderer)
+ partRenderer->setStyle(partStyle.release());
+}
+
+void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
+{
+ RenderScrollbarPart* partRenderer = m_parts.get(partType);
+ if (!partRenderer)
+ return;
+ partRenderer->paintIntoRect(graphicsContext, x(), y(), rect);
+}
+
+IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
+{
+ RenderScrollbarPart* partRenderer = m_parts.get(partType);
+ if (!partRenderer)
+ return IntRect();
+
+ partRenderer->layout();
+
+ bool isHorizontal = orientation() == HorizontalScrollbar;
+ if (partType == BackButtonStartPart)
+ return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height());
+ if (partType == ForwardButtonEndPart)
+ return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(),
+
+ isHorizontal ? y() : y() + height() - partRenderer->height(),
+ isHorizontal ? partRenderer->width() : width(),
+ isHorizontal ? height() : partRenderer->height());
+
+ if (partType == ForwardButtonStartPart) {
+ IntRect previousButton = buttonRect(BackButtonStartPart);
+ return IntRect(isHorizontal ? x() + previousButton.width() : x(),
+ isHorizontal ? y() : y() + previousButton.height(),
+ isHorizontal ? partRenderer->width() : width(),
+ isHorizontal ? height() : partRenderer->height());
+ }
+
+ IntRect followingButton = buttonRect(ForwardButtonEndPart);
+ return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(),
+ isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(),
+ isHorizontal ? partRenderer->width() : width(),
+ isHorizontal ? height() : partRenderer->height());
+}
+
+IntRect RenderScrollbar::trackRect(int startLength, int endLength)
+{
+ RenderScrollbarPart* part = m_parts.get(TrackBGPart);
+ if (part)
+ part->layout();
+
+ if (orientation() == HorizontalScrollbar) {
+ int marginLeft = part ? part->marginLeft() : 0;
+ int marginRight = part ? part->marginRight() : 0;
+ startLength += marginLeft;
+ endLength += marginRight;
+ int totalLength = startLength + endLength;
+ return IntRect(x() + startLength, y(), width() - totalLength, height());
+ }
+
+ int marginTop = part ? part->marginTop() : 0;
+ int marginBottom = part ? part->marginBottom() : 0;
+ startLength += marginTop;
+ endLength += marginBottom;
+ int totalLength = startLength + endLength;
+
+ return IntRect(x(), y() + startLength, width(), height() - totalLength);
+}
+
+IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
+{
+ RenderScrollbarPart* partRenderer = m_parts.get(partType);
+ if (!partRenderer)
+ return oldRect;
+
+ partRenderer->layout();
+
+ IntRect rect = oldRect;
+ if (orientation() == HorizontalScrollbar) {
+ rect.setX(rect.x() + partRenderer->marginLeft());
+ rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight()));
+ } else {
+ rect.setY(rect.y() + partRenderer->marginTop());
+ rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom()));
+ }
+ return rect;
+}
+
+int RenderScrollbar::minimumThumbLength()
+{
+ RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
+ if (!partRenderer)
+ return 0;
+ partRenderer->layout();
+ return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderScrollbar.h b/Source/WebCore/rendering/RenderScrollbar.h
new file mode 100644
index 0000000..de70624
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbar.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008, 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 RenderScrollbar_h
+#define RenderScrollbar_h
+
+#include "RenderStyleConstants.h"
+#include "Scrollbar.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class Frame;
+class RenderBox;
+class RenderScrollbarPart;
+class RenderStyle;
+
+class RenderScrollbar : public Scrollbar {
+protected:
+ RenderScrollbar(ScrollbarClient*, ScrollbarOrientation, RenderBox*, Frame*);
+
+public:
+ friend class Scrollbar;
+ static PassRefPtr<Scrollbar> createCustomScrollbar(ScrollbarClient*, ScrollbarOrientation, RenderBox*, Frame* owningFrame = 0);
+ virtual ~RenderScrollbar();
+
+ static ScrollbarPart partForStyleResolve();
+ static RenderScrollbar* scrollbarForStyleResolve();
+
+ RenderBox* owningRenderer() const;
+ void clearOwningRenderer() { m_owner = 0; }
+
+ void paintPart(GraphicsContext*, ScrollbarPart, const IntRect&);
+
+ IntRect buttonRect(ScrollbarPart);
+ IntRect trackRect(int startLength, int endLength);
+ IntRect trackPieceRectWithMargins(ScrollbarPart, const IntRect&);
+
+ int minimumThumbLength();
+
+private:
+ virtual void setParent(ScrollView*);
+ virtual void setEnabled(bool);
+
+ virtual void paint(GraphicsContext*, const IntRect& damageRect);
+
+ virtual void setHoveredPart(ScrollbarPart);
+ virtual void setPressedPart(ScrollbarPart);
+
+ virtual void styleChanged();
+
+ virtual bool isCustomScrollbar() const { return true; }
+
+ void updateScrollbarParts(bool destroy = false);
+
+ PassRefPtr<RenderStyle> getScrollbarPseudoStyle(ScrollbarPart, PseudoId);
+ void updateScrollbarPart(ScrollbarPart, bool destroy = false);
+
+ RenderBox* m_owner;
+ Frame* m_owningFrame;
+ HashMap<unsigned, RenderScrollbarPart*> m_parts;
+};
+
+inline RenderScrollbar* toRenderScrollbar(Scrollbar* scrollbar)
+{
+ ASSERT(!scrollbar || scrollbar->isCustomScrollbar());
+ return static_cast<RenderScrollbar*>(scrollbar);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderScrollbar(const RenderScrollbar*);
+
+} // namespace WebCore
+
+#endif // RenderScrollbar_h
diff --git a/Source/WebCore/rendering/RenderScrollbarPart.cpp b/Source/WebCore/rendering/RenderScrollbarPart.cpp
new file mode 100644
index 0000000..16cc204
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbarPart.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ *
+ * 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 "RenderScrollbarPart.h"
+#include "RenderScrollbar.h"
+#include "RenderScrollbarTheme.h"
+#include "RenderView.h"
+
+using namespace std;
+
+namespace WebCore {
+
+RenderScrollbarPart::RenderScrollbarPart(Node* node, RenderScrollbar* scrollbar, ScrollbarPart part)
+ : RenderBlock(node)
+ , m_scrollbar(scrollbar)
+ , m_part(part)
+{
+}
+
+RenderScrollbarPart::~RenderScrollbarPart()
+{
+}
+
+void RenderScrollbarPart::layout()
+{
+ setLocation(IntPoint()); // We don't worry about positioning ourselves. We're just determining our minimum width/height.
+ if (m_scrollbar->orientation() == HorizontalScrollbar)
+ layoutHorizontalPart();
+ else
+ layoutVerticalPart();
+
+ setNeedsLayout(false);
+}
+
+void RenderScrollbarPart::layoutHorizontalPart()
+{
+ if (m_part == ScrollbarBGPart) {
+ setWidth(m_scrollbar->width());
+ computeScrollbarHeight();
+ } else {
+ computeScrollbarWidth();
+ setHeight(m_scrollbar->height());
+ }
+}
+
+void RenderScrollbarPart::layoutVerticalPart()
+{
+ if (m_part == ScrollbarBGPart) {
+ computeScrollbarWidth();
+ setHeight(m_scrollbar->height());
+ } else {
+ setWidth(m_scrollbar->width());
+ computeScrollbarHeight();
+ }
+}
+
+static int calcScrollbarThicknessUsing(const Length& l, int containingLength)
+{
+ if (l.isIntrinsicOrAuto())
+ return ScrollbarTheme::nativeTheme()->scrollbarThickness();
+ return l.calcMinValue(containingLength);
+}
+
+void RenderScrollbarPart::computeScrollbarWidth()
+{
+ if (!m_scrollbar->owningRenderer())
+ return;
+ int visibleSize = m_scrollbar->owningRenderer()->width() - m_scrollbar->owningRenderer()->borderLeft() - m_scrollbar->owningRenderer()->borderRight();
+ int w = calcScrollbarThicknessUsing(style()->width(), visibleSize);
+ int minWidth = calcScrollbarThicknessUsing(style()->minWidth(), visibleSize);
+ int maxWidth = style()->maxWidth().isUndefined() ? w : calcScrollbarThicknessUsing(style()->maxWidth(), visibleSize);
+ setWidth(max(minWidth, min(maxWidth, w)));
+
+ // Buttons and track pieces can all have margins along the axis of the scrollbar.
+ m_marginLeft = style()->marginLeft().calcMinValue(visibleSize);
+ m_marginRight = style()->marginRight().calcMinValue(visibleSize);
+}
+
+void RenderScrollbarPart::computeScrollbarHeight()
+{
+ if (!m_scrollbar->owningRenderer())
+ return;
+ int visibleSize = m_scrollbar->owningRenderer()->height() - m_scrollbar->owningRenderer()->borderTop() - m_scrollbar->owningRenderer()->borderBottom();
+ int h = calcScrollbarThicknessUsing(style()->height(), visibleSize);
+ int minHeight = calcScrollbarThicknessUsing(style()->minHeight(), visibleSize);
+ int maxHeight = style()->maxHeight().isUndefined() ? h : calcScrollbarThicknessUsing(style()->maxHeight(), visibleSize);
+ setHeight(max(minHeight, min(maxHeight, h)));
+
+ // Buttons and track pieces can all have margins along the axis of the scrollbar.
+ m_marginTop = style()->marginTop().calcMinValue(visibleSize);
+ m_marginBottom = style()->marginBottom().calcMinValue(visibleSize);
+}
+
+void RenderScrollbarPart::computePreferredLogicalWidths()
+{
+ if (!preferredLogicalWidthsDirty())
+ return;
+
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
+
+ setPreferredLogicalWidthsDirty(false);
+}
+
+void RenderScrollbarPart::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ RenderBlock::styleWillChange(diff, newStyle);
+ setInline(false);
+}
+
+void RenderScrollbarPart::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+ setInline(false);
+ setPositioned(false);
+ setFloating(false);
+ setHasOverflowClip(false);
+ if (oldStyle && m_scrollbar && m_part != NoPart && diff >= StyleDifferenceRepaint)
+ m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part);
+}
+
+void RenderScrollbarPart::imageChanged(WrappedImagePtr image, const IntRect* rect)
+{
+ if (m_scrollbar && m_part != NoPart)
+ m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part);
+ else {
+ if (FrameView* frameView = view()->frameView()) {
+ if (frameView->isFrameViewScrollCorner(this)) {
+ frameView->invalidateScrollCorner();
+ return;
+ }
+ }
+
+ RenderBlock::imageChanged(image, rect);
+ }
+}
+
+void RenderScrollbarPart::paintIntoRect(GraphicsContext* graphicsContext, int tx, int ty, const IntRect& rect)
+{
+ // Make sure our dimensions match the rect.
+ setLocation(rect.x() - tx, rect.y() - ty);
+ setWidth(rect.width());
+ setHeight(rect.height());
+
+ if (graphicsContext->paintingDisabled())
+ return;
+
+ // Now do the paint.
+ PaintInfo paintInfo(graphicsContext, rect, PaintPhaseBlockBackground, false, 0, 0);
+ paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseChildBlockBackgrounds;
+ paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseFloat;
+ paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseForeground;
+ paint(paintInfo, tx, ty);
+ paintInfo.phase = PaintPhaseOutline;
+ paint(paintInfo, tx, ty);
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderScrollbarPart.h b/Source/WebCore/rendering/RenderScrollbarPart.h
new file mode 100644
index 0000000..24485d0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbarPart.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ * 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 RenderScrollbarPart_h
+#define RenderScrollbarPart_h
+
+#include "RenderBlock.h"
+#include "ScrollTypes.h"
+
+namespace WebCore {
+
+class RenderScrollbar;
+
+class RenderScrollbarPart : public RenderBlock {
+public:
+ RenderScrollbarPart(Node*, RenderScrollbar* = 0, ScrollbarPart = NoPart);
+ virtual ~RenderScrollbarPart();
+
+ virtual const char* renderName() const { return "RenderScrollbarPart"; }
+
+ virtual bool requiresLayer() const { return false; }
+
+ virtual void layout();
+ virtual void computePreferredLogicalWidths();
+
+ void paintIntoRect(GraphicsContext*, int tx, int ty, const IntRect&);
+
+protected:
+ virtual void styleWillChange(StyleDifference diff, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+private:
+ void layoutHorizontalPart();
+ void layoutVerticalPart();
+
+ void computeScrollbarWidth();
+ void computeScrollbarHeight();
+
+ RenderScrollbar* m_scrollbar;
+ ScrollbarPart m_part;
+};
+
+} // namespace WebCore
+
+#endif // RenderScrollbarPart_h
diff --git a/Source/WebCore/rendering/RenderScrollbarTheme.cpp b/Source/WebCore/rendering/RenderScrollbarTheme.cpp
new file mode 100644
index 0000000..e32d87a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbarTheme.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ *
+ * 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 "RenderScrollbarTheme.h"
+#include "RenderScrollbar.h"
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+RenderScrollbarTheme* RenderScrollbarTheme::renderScrollbarTheme()
+{
+ DEFINE_STATIC_LOCAL(RenderScrollbarTheme, theme, ());
+ return &theme;
+}
+
+void RenderScrollbarTheme::buttonSizesAlongTrackAxis(Scrollbar* scrollbar, int& beforeSize, int& afterSize)
+{
+ IntRect firstButton = backButtonRect(scrollbar, BackButtonStartPart);
+ IntRect secondButton = forwardButtonRect(scrollbar, ForwardButtonStartPart);
+ IntRect thirdButton = backButtonRect(scrollbar, BackButtonEndPart);
+ IntRect fourthButton = forwardButtonRect(scrollbar, ForwardButtonEndPart);
+ if (scrollbar->orientation() == HorizontalScrollbar) {
+ beforeSize = firstButton.width() + secondButton.width();
+ afterSize = thirdButton.width() + fourthButton.width();
+ } else {
+ beforeSize = firstButton.height() + secondButton.height();
+ afterSize = thirdButton.height() + fourthButton.height();
+ }
+}
+
+bool RenderScrollbarTheme::hasButtons(Scrollbar* scrollbar)
+{
+ int startSize;
+ int endSize;
+ buttonSizesAlongTrackAxis(scrollbar, startSize, endSize);
+ return (startSize + endSize) <= (scrollbar->orientation() == HorizontalScrollbar ? scrollbar->width() : scrollbar->height());
+}
+
+bool RenderScrollbarTheme::hasThumb(Scrollbar* scrollbar)
+{
+ return trackLength(scrollbar) - thumbLength(scrollbar) >= 0;
+}
+
+int RenderScrollbarTheme::minimumThumbLength(Scrollbar* scrollbar)
+{
+ return toRenderScrollbar(scrollbar)->minimumThumbLength();
+}
+
+IntRect RenderScrollbarTheme::backButtonRect(Scrollbar* scrollbar, ScrollbarPart partType, bool)
+{
+ return toRenderScrollbar(scrollbar)->buttonRect(partType);
+}
+
+IntRect RenderScrollbarTheme::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart partType, bool)
+{
+ return toRenderScrollbar(scrollbar)->buttonRect(partType);
+}
+
+IntRect RenderScrollbarTheme::trackRect(Scrollbar* scrollbar, bool)
+{
+ if (!hasButtons(scrollbar))
+ return scrollbar->frameRect();
+
+ int startLength;
+ int endLength;
+ buttonSizesAlongTrackAxis(scrollbar, startLength, endLength);
+
+ return toRenderScrollbar(scrollbar)->trackRect(startLength, endLength);
+}
+
+IntRect RenderScrollbarTheme::constrainTrackRectToTrackPieces(Scrollbar* scrollbar, const IntRect& rect)
+{
+ IntRect backRect = toRenderScrollbar(scrollbar)->trackPieceRectWithMargins(BackTrackPart, rect);
+ IntRect forwardRect = toRenderScrollbar(scrollbar)->trackPieceRectWithMargins(ForwardTrackPart, rect);
+ IntRect result = rect;
+ if (scrollbar->orientation() == HorizontalScrollbar) {
+ result.setX(backRect.x());
+ result.setWidth(forwardRect.right() - backRect.x());
+ } else {
+ result.setY(backRect.y());
+ result.setHeight(forwardRect.bottom() - backRect.y());
+ }
+ return result;
+}
+
+void RenderScrollbarTheme::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect)
+{
+ // FIXME: Implement.
+ context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB);
+}
+
+void RenderScrollbarTheme::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar)
+{
+ toRenderScrollbar(scrollbar)->paintPart(context, ScrollbarBGPart, scrollbar->frameRect());
+}
+
+void RenderScrollbarTheme::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
+{
+ toRenderScrollbar(scrollbar)->paintPart(context, TrackBGPart, rect);
+}
+
+void RenderScrollbarTheme::paintTrackPiece(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
+{
+ toRenderScrollbar(scrollbar)->paintPart(context, part, rect);
+}
+
+void RenderScrollbarTheme::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part)
+{
+ toRenderScrollbar(scrollbar)->paintPart(context, part, rect);
+}
+
+void RenderScrollbarTheme::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect)
+{
+ toRenderScrollbar(scrollbar)->paintPart(context, ThumbPart, rect);
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderScrollbarTheme.h b/Source/WebCore/rendering/RenderScrollbarTheme.h
new file mode 100644
index 0000000..9b8b2c1
--- /dev/null
+++ b/Source/WebCore/rendering/RenderScrollbarTheme.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * 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 RenderScrollbarTheme_h
+#define RenderScrollbarTheme_h
+
+#include "ScrollbarThemeComposite.h"
+
+namespace WebCore {
+
+class PlatformMouseEvent;
+class Scrollbar;
+class ScrollView;
+
+class RenderScrollbarTheme : public ScrollbarThemeComposite {
+public:
+ virtual ~RenderScrollbarTheme() {};
+
+ virtual int scrollbarThickness(ScrollbarControlSize controlSize) { return ScrollbarTheme::nativeTheme()->scrollbarThickness(controlSize); }
+
+ virtual ScrollbarButtonsPlacement buttonsPlacement() const { return ScrollbarTheme::nativeTheme()->buttonsPlacement(); }
+
+ virtual bool supportsControlTints() const { return true; }
+
+ virtual void paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect);
+
+ virtual bool shouldCenterOnThumb(Scrollbar* scrollbar, const PlatformMouseEvent& event) { return ScrollbarTheme::nativeTheme()->shouldCenterOnThumb(scrollbar, event); }
+
+ virtual double initialAutoscrollTimerDelay() { return ScrollbarTheme::nativeTheme()->initialAutoscrollTimerDelay(); }
+ virtual double autoscrollTimerDelay() { return ScrollbarTheme::nativeTheme()->autoscrollTimerDelay(); }
+
+ virtual void registerScrollbar(Scrollbar* scrollbar) { return ScrollbarTheme::nativeTheme()->registerScrollbar(scrollbar); }
+ virtual void unregisterScrollbar(Scrollbar* scrollbar) { return ScrollbarTheme::nativeTheme()->unregisterScrollbar(scrollbar); }
+
+ virtual int minimumThumbLength(Scrollbar*);
+
+ void buttonSizesAlongTrackAxis(Scrollbar* scrollbar, int& beforeSize, int& afterSize);
+
+ static RenderScrollbarTheme* renderScrollbarTheme();
+
+protected:
+ virtual bool hasButtons(Scrollbar*);
+ virtual bool hasThumb(Scrollbar*);
+
+ virtual IntRect backButtonRect(Scrollbar*, ScrollbarPart, bool painting = false);
+ virtual IntRect forwardButtonRect(Scrollbar*, ScrollbarPart, bool painting = false);
+ virtual IntRect trackRect(Scrollbar*, bool painting = false);
+
+ virtual void paintScrollbarBackground(GraphicsContext*, Scrollbar*);
+ virtual void paintTrackBackground(GraphicsContext*, Scrollbar*, const IntRect&);
+ virtual void paintTrackPiece(GraphicsContext*, Scrollbar*, const IntRect&, ScrollbarPart);
+ virtual void paintButton(GraphicsContext*, Scrollbar*, const IntRect&, ScrollbarPart);
+ virtual void paintThumb(GraphicsContext*, Scrollbar*, const IntRect&);
+
+ virtual IntRect constrainTrackRectToTrackPieces(Scrollbar*, const IntRect&);
+};
+
+} // namespace WebCore
+
+#endif // RenderScrollbarTheme_h
diff --git a/Source/WebCore/rendering/RenderSelectionInfo.h b/Source/WebCore/rendering/RenderSelectionInfo.h
new file mode 100644
index 0000000..a09fc1a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSelectionInfo.h
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ *
+ * 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 RenderSelectionInfo_h
+#define RenderSelectionInfo_h
+
+#include "IntRect.h"
+#include "RenderBox.h"
+
+namespace WebCore {
+
+class RenderSelectionInfoBase : public Noncopyable {
+public:
+ RenderSelectionInfoBase()
+ : m_object(0)
+ , m_repaintContainer(0)
+ , m_state(RenderObject::SelectionNone)
+ {
+ }
+
+ RenderSelectionInfoBase(RenderObject* o)
+ : m_object(o)
+ , m_repaintContainer(o->containerForRepaint())
+ , m_state(o->selectionState())
+ {
+ }
+
+ RenderObject* object() const { return m_object; }
+ RenderBoxModelObject* repaintContainer() const { return m_repaintContainer; }
+ RenderObject::SelectionState state() const { return m_state; }
+
+protected:
+ RenderObject* m_object;
+ RenderBoxModelObject* m_repaintContainer;
+ RenderObject::SelectionState m_state;
+};
+
+// This struct is used when the selection changes to cache the old and new state of the selection for each RenderObject.
+class RenderSelectionInfo : public RenderSelectionInfoBase {
+public:
+ RenderSelectionInfo(RenderObject* o, bool clipToVisibleContent)
+ : RenderSelectionInfoBase(o)
+ , m_rect(o->needsLayout() ? IntRect() : o->selectionRectForRepaint(m_repaintContainer, clipToVisibleContent))
+ {
+ }
+
+ void repaint()
+ {
+ m_object->repaintUsingContainer(m_repaintContainer, m_rect);
+ }
+
+ IntRect rect() const { return m_rect; }
+
+private:
+ IntRect m_rect; // relative to repaint container
+};
+
+
+// This struct is used when the selection changes to cache the old and new state of the selection for each RenderBlock.
+class RenderBlockSelectionInfo : public RenderSelectionInfoBase {
+public:
+ RenderBlockSelectionInfo(RenderBlock* b)
+ : RenderSelectionInfoBase(b)
+ , m_rects(b->needsLayout() ? GapRects() : block()->selectionGapRectsForRepaint(m_repaintContainer))
+ {
+ }
+
+ void repaint()
+ {
+ m_object->repaintUsingContainer(m_repaintContainer, m_rects);
+ }
+
+ RenderBlock* block() const { return toRenderBlock(m_object); }
+ GapRects rects() const { return m_rects; }
+
+private:
+ GapRects m_rects; // relative to repaint container
+};
+
+} // namespace WebCore
+
+
+#endif // RenderSelectionInfo_h
diff --git a/Source/WebCore/rendering/RenderSlider.cpp b/Source/WebCore/rendering/RenderSlider.cpp
new file mode 100644
index 0000000..b73a1ac
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSlider.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 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 "RenderSlider.h"
+
+#include "CSSPropertyNames.h"
+#include "Document.h"
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
+#include "MediaControlElements.h"
+#include "MouseEvent.h"
+#include "RenderLayer.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+#include "ShadowElement.h"
+#include "SliderThumbElement.h"
+#include "StepRange.h"
+#include <wtf/MathExtras.h>
+
+using std::min;
+
+namespace WebCore {
+
+static const int defaultTrackLength = 129;
+
+// Returns a value between 0 and 1.
+static double sliderPosition(HTMLInputElement* element)
+{
+ StepRange range(element);
+ return range.proportionFromValue(range.valueFromElement(element));
+}
+
+RenderSlider::RenderSlider(HTMLInputElement* element)
+ : RenderBlock(element)
+{
+}
+
+RenderSlider::~RenderSlider()
+{
+ if (m_thumb)
+ m_thumb->detach();
+}
+
+int RenderSlider::baselinePosition(FontBaseline, bool /*firstLine*/, LineDirectionMode, LinePositionMode) const
+{
+ // FIXME: Patch this function for writing-mode.
+ return height() + marginTop();
+}
+
+void RenderSlider::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 = defaultTrackLength * style()->effectiveZoom();
+
+ 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 RenderSlider::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+
+ if (m_thumb)
+ m_thumb->renderer()->setStyle(createThumbStyle(style()));
+
+ setReplaced(isInline());
+}
+
+PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parentStyle)
+{
+ RefPtr<RenderStyle> style;
+ RenderStyle* pseudoStyle = getCachedPseudoStyle(SLIDER_THUMB);
+ if (pseudoStyle)
+ // We may be sharing style with another slider, but we must not share the thumb style.
+ style = RenderStyle::clone(pseudoStyle);
+ else
+ style = RenderStyle::create();
+
+ if (parentStyle)
+ style->inheritFrom(parentStyle);
+
+ style->setDisplay(BLOCK);
+
+ if (parentStyle->appearance() == SliderVerticalPart)
+ style->setAppearance(SliderThumbVerticalPart);
+ else if (parentStyle->appearance() == SliderHorizontalPart)
+ style->setAppearance(SliderThumbHorizontalPart);
+ else if (parentStyle->appearance() == MediaSliderPart)
+ style->setAppearance(MediaSliderThumbPart);
+ else if (parentStyle->appearance() == MediaVolumeSliderPart)
+ style->setAppearance(MediaVolumeSliderThumbPart);
+
+ return style.release();
+}
+
+IntRect RenderSlider::thumbRect()
+{
+ if (!m_thumb)
+ return IntRect();
+
+ IntRect thumbRect;
+ RenderBox* thumb = toRenderBox(m_thumb->renderer());
+
+ thumbRect.setWidth(thumb->style()->width().calcMinValue(contentWidth()));
+ thumbRect.setHeight(thumb->style()->height().calcMinValue(contentHeight()));
+
+ double fraction = sliderPosition(static_cast<HTMLInputElement*>(node()));
+ IntRect contentRect = contentBoxRect();
+ if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart) {
+ thumbRect.setX(contentRect.x() + (contentRect.width() - thumbRect.width()) / 2);
+ thumbRect.setY(contentRect.y() + static_cast<int>(nextafter((contentRect.height() - thumbRect.height()) + 1, 0) * (1 - fraction)));
+ } else {
+ thumbRect.setX(contentRect.x() + static_cast<int>(nextafter((contentRect.width() - thumbRect.width()) + 1, 0) * fraction));
+ thumbRect.setY(contentRect.y() + (contentRect.height() - thumbRect.height()) / 2);
+ }
+
+ return thumbRect;
+}
+
+void RenderSlider::layout()
+{
+ ASSERT(needsLayout());
+
+ RenderBox* thumb = m_thumb ? toRenderBox(m_thumb->renderer()) : 0;
+
+ IntSize baseSize(borderAndPaddingWidth(), borderAndPaddingHeight());
+
+ if (thumb) {
+ // Allow the theme to set the size of the thumb.
+ if (thumb->style()->hasAppearance()) {
+ // FIXME: This should pass the style, not the renderer, to the theme.
+ theme()->adjustSliderThumbSize(thumb);
+ }
+
+ baseSize.expand(thumb->style()->width().calcMinValue(0), thumb->style()->height().calcMinValue(0));
+ }
+
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
+
+ IntSize oldSize = size();
+
+ setSize(baseSize);
+ computeLogicalWidth();
+ computeLogicalHeight();
+ updateLayerTransform();
+
+ if (thumb) {
+ if (oldSize != size())
+ thumb->setChildNeedsLayout(true, false);
+
+ LayoutStateMaintainer statePusher(view(), this, size(), style()->isFlippedBlocksWritingMode());
+
+ IntRect oldThumbRect = thumb->frameRect();
+
+ thumb->layoutIfNeeded();
+
+ IntRect rect = thumbRect();
+ thumb->setFrameRect(rect);
+ if (thumb->checkForRepaintDuringLayout())
+ thumb->repaintDuringLayoutIfMoved(oldThumbRect);
+
+ statePusher.pop();
+ addOverflowFromChild(thumb);
+ }
+
+ repainter.repaintAfterLayout();
+
+ setNeedsLayout(false);
+}
+
+void RenderSlider::updateFromElement()
+{
+ // Layout will take care of the thumb's size and position.
+ if (!m_thumb) {
+ m_thumb = SliderThumbElement::create(static_cast<HTMLElement*>(node()));
+ RefPtr<RenderStyle> thumbStyle = createThumbStyle(style());
+ m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle.get()));
+ m_thumb->renderer()->setStyle(thumbStyle.release());
+ m_thumb->setAttached();
+ m_thumb->setInDocument();
+ addChild(m_thumb->renderer());
+ }
+ setNeedsLayout(true);
+}
+
+bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return false;
+
+#if ENABLE(VIDEO)
+ if (style()->appearance() == MediaSliderPart || style()->appearance() == MediaVolumeSliderPart) {
+ MediaControlInputElement *sliderThumb = static_cast<MediaControlInputElement*>(m_thumb->renderer()->node());
+ return sliderThumb->hitTest(evt->absoluteLocation());
+ }
+#endif
+
+ FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(evt->absoluteLocation(), false, true);
+ IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect();
+ return thumbBounds.contains(roundedIntPoint(localPoint));
+}
+
+FloatPoint RenderSlider::mouseEventOffsetToThumb(MouseEvent* evt)
+{
+ ASSERT(m_thumb && m_thumb->renderer());
+ FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(evt->absoluteLocation(), false, true);
+ IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect();
+ FloatPoint offset;
+ offset.setX(thumbBounds.x() + thumbBounds.width() / 2 - localPoint.x());
+ offset.setY(thumbBounds.y() + thumbBounds.height() / 2 - localPoint.y());
+ return offset;
+}
+
+void RenderSlider::setValueForPosition(int position)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return;
+
+ HTMLInputElement* element = static_cast<HTMLInputElement*>(node());
+
+ // Calculate the new value based on the position, and send it to the element.
+ StepRange range(element);
+ double fraction = static_cast<double>(position) / trackSize();
+ if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart)
+ fraction = 1 - fraction;
+ double value = range.clampValue(range.valueFromProportion(fraction));
+ element->setValueFromRenderer(serializeForNumberType(value));
+
+ // Also update the position if appropriate.
+ if (position != currentPosition()) {
+ setNeedsLayout(true);
+
+ // FIXME: It seems like this could send extra change events if the same value is set
+ // multiple times with no layout in between.
+ element->dispatchFormControlChangeEvent();
+ }
+}
+
+int RenderSlider::positionForOffset(const IntPoint& p)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return 0;
+
+ int position;
+ if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart)
+ position = p.y() - m_thumb->renderBox()->height() / 2;
+ else
+ position = p.x() - m_thumb->renderBox()->width() / 2;
+
+ return max(0, min(position, trackSize()));
+}
+
+int RenderSlider::currentPosition()
+{
+ ASSERT(m_thumb);
+ ASSERT(m_thumb->renderer());
+
+ if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart)
+ return toRenderBox(m_thumb->renderer())->y() - contentBoxRect().y();
+ return toRenderBox(m_thumb->renderer())->x() - contentBoxRect().x();
+}
+
+int RenderSlider::trackSize()
+{
+ ASSERT(m_thumb);
+ ASSERT(m_thumb->renderer());
+
+ if (style()->appearance() == SliderVerticalPart || style()->appearance() == MediaVolumeSliderPart)
+ return contentHeight() - m_thumb->renderBox()->height();
+ return contentWidth() - m_thumb->renderBox()->width();
+}
+
+void RenderSlider::forwardEvent(Event* event)
+{
+ if (event->isMouseEvent()) {
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ if (event->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
+ if (!mouseEventIsInThumb(mouseEvent)) {
+ IntPoint eventOffset = roundedIntPoint(absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
+ setValueForPosition(positionForOffset(eventOffset));
+ }
+ }
+ }
+
+ m_thumb->defaultEventHandler(event);
+}
+
+bool RenderSlider::inDragMode() const
+{
+ return m_thumb && m_thumb->inDragMode();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderSlider.h b/Source/WebCore/rendering/RenderSlider.h
new file mode 100644
index 0000000..03779a3
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSlider.h
@@ -0,0 +1,83 @@
+/*
+ * 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 RenderSlider_h
+#define RenderSlider_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+ class HTMLInputElement;
+ class MouseEvent;
+ class SliderThumbElement;
+
+ class RenderSlider : public RenderBlock {
+ public:
+ RenderSlider(HTMLInputElement*);
+ virtual ~RenderSlider();
+
+ void forwardEvent(Event*);
+ bool inDragMode() const;
+ IntRect thumbRect();
+
+ private:
+ virtual const char* renderName() const { return "RenderSlider"; }
+ virtual bool isSlider() const { return true; }
+
+ virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
+ virtual void computePreferredLogicalWidths();
+ virtual void layout();
+ virtual void updateFromElement();
+
+ bool mouseEventIsInThumb(MouseEvent*);
+ FloatPoint mouseEventOffsetToThumb(MouseEvent*);
+
+ void setValueForPosition(int position);
+ void setPositionFromValue();
+ int positionForOffset(const IntPoint&);
+
+ int currentPosition();
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return true; }
+
+ PassRefPtr<RenderStyle> createThumbStyle(const RenderStyle* parentStyle);
+
+ int trackSize();
+
+ RefPtr<SliderThumbElement> m_thumb;
+
+ friend class SliderThumbElement;
+ };
+
+ inline RenderSlider* toRenderSlider(RenderObject* object)
+ {
+ ASSERT(!object || object->isSlider());
+ return static_cast<RenderSlider*>(object);
+ }
+
+ // This will catch anyone doing an unnecessary cast.
+ void toRenderSlider(const RenderSlider*);
+
+} // namespace WebCore
+
+#endif // RenderSlider_h
diff --git a/Source/WebCore/rendering/RenderSummary.cpp b/Source/WebCore/rendering/RenderSummary.cpp
new file mode 100644
index 0000000..8fccf66
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSummary.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 "RenderSummary.h"
+
+namespace WebCore {
+
+RenderSummary::RenderSummary(Node* element)
+ : RenderBlock(element)
+{
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderSummary.h b/Source/WebCore/rendering/RenderSummary.h
new file mode 100644
index 0000000..afc18fa
--- /dev/null
+++ b/Source/WebCore/rendering/RenderSummary.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 RenderSummary_h
+#define RenderSummary_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class RenderSummary : public RenderBlock {
+public:
+ explicit RenderSummary(Node*);
+
+private:
+ virtual const char* renderName() const { return "RenderSummary"; }
+ virtual bool isSummary() const { return true; }
+};
+
+inline RenderSummary* toRenderSummary(RenderObject* object)
+{
+ ASSERT(!object || object->isSummary());
+ return static_cast<RenderSummary*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSummary(const RenderSummary*);
+
+}
+
+#endif // RenderSummary_h
+
diff --git a/Source/WebCore/rendering/RenderTable.cpp b/Source/WebCore/rendering/RenderTable.cpp
new file mode 100644
index 0000000..f0cc264
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTable.cpp
@@ -0,0 +1,1249 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.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.
+ */
+
+#include "config.h"
+#include "RenderTable.h"
+
+#include "AutoTableLayout.h"
+#include "DeleteButtonController.h"
+#include "Document.h"
+#include "FixedTableLayout.h"
+#include "FrameView.h"
+#include "HitTestResult.h"
+#include "HTMLNames.h"
+#include "RenderLayer.h"
+#include "RenderTableCell.h"
+#include "RenderTableCol.h"
+#include "RenderTableSection.h"
+#ifdef ANDROID_LAYOUT
+#include "Settings.h"
+#endif
+#include "RenderView.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTable::RenderTable(Node* node)
+ : RenderBlock(node)
+ , m_caption(0)
+ , m_head(0)
+ , m_foot(0)
+ , m_firstBody(0)
+ , m_currentBorder(0)
+ , m_hasColElements(false)
+ , m_needsSectionRecalc(0)
+ , m_hSpacing(0)
+ , m_vSpacing(0)
+ , m_borderStart(0)
+ , m_borderEnd(0)
+{
+ setChildrenInline(false);
+ m_columnPos.fill(0, 2);
+ m_columns.fill(ColumnStruct(), 1);
+
+#ifdef ANDROID_LAYOUT
+ m_singleColumn = false;
+#endif
+}
+
+RenderTable::~RenderTable()
+{
+}
+
+void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+
+ ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
+
+ // In the collapsed border model, there is no cell spacing.
+ m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
+ m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
+ m_columnPos[0] = m_hSpacing;
+
+ if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
+ // According to the CSS2 spec, you only use fixed table layout if an
+ // explicit width is specified on the table. Auto width implies auto table layout.
+ if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto())
+ m_tableLayout.set(new FixedTableLayout(this));
+ else
+ m_tableLayout.set(new AutoTableLayout(this));
+ }
+}
+
+static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
+{
+ if (!before || !ptr)
+ return;
+ RenderObject* o = before->previousSibling();
+ while (o && o != ptr)
+ o = o->previousSibling();
+ if (!o)
+ ptr = 0;
+}
+
+void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if (!beforeChild && isAfterContent(lastChild()))
+ beforeChild = lastChild();
+
+ bool wrapInAnonymousSection = !child->isPositioned();
+
+ if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) {
+ // First caption wins.
+ if (beforeChild && m_caption) {
+ RenderObject* o = beforeChild->previousSibling();
+ while (o && o != m_caption)
+ o = o->previousSibling();
+ if (!o)
+ m_caption = 0;
+ }
+ if (!m_caption)
+ m_caption = toRenderBlock(child);
+ wrapInAnonymousSection = false;
+ } else if (child->isTableCol()) {
+ m_hasColElements = true;
+ wrapInAnonymousSection = false;
+ } else if (child->isTableSection()) {
+ switch (child->style()->display()) {
+ case TABLE_HEADER_GROUP:
+ resetSectionPointerIfNotBefore(m_head, beforeChild);
+ if (!m_head) {
+ m_head = toRenderTableSection(child);
+ } else {
+ resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
+ if (!m_firstBody)
+ m_firstBody = toRenderTableSection(child);
+ }
+ wrapInAnonymousSection = false;
+ break;
+ case TABLE_FOOTER_GROUP:
+ resetSectionPointerIfNotBefore(m_foot, beforeChild);
+ if (!m_foot) {
+ m_foot = toRenderTableSection(child);
+ wrapInAnonymousSection = false;
+ break;
+ }
+ // Fall through.
+ case TABLE_ROW_GROUP:
+ resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
+ if (!m_firstBody)
+ m_firstBody = toRenderTableSection(child);
+ wrapInAnonymousSection = false;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+ } else if (child->isTableCell() || child->isTableRow())
+ wrapInAnonymousSection = true;
+ else
+ wrapInAnonymousSection = true;
+
+ if (!wrapInAnonymousSection) {
+ // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that.
+ while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION)
+ beforeChild = beforeChild->parent();
+
+ RenderBox::addChild(child, beforeChild);
+ return;
+ }
+
+ if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
+ lastChild()->addChild(child);
+ return;
+ }
+
+ RenderObject* lastBox = beforeChild;
+ while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP)
+ lastBox = lastBox->parent();
+ if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
+ if (beforeChild == lastBox)
+ beforeChild = lastBox->firstChild();
+ lastBox->addChild(child, beforeChild);
+ return;
+ }
+
+ if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
+ beforeChild = 0;
+ RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_ROW_GROUP);
+ section->setStyle(newStyle.release());
+ addChild(section, beforeChild);
+ section->addChild(child);
+}
+
+void RenderTable::removeChild(RenderObject* oldChild)
+{
+ RenderBox::removeChild(oldChild);
+ setNeedsSectionRecalc();
+}
+
+void RenderTable::computeLogicalWidth()
+{
+#ifdef ANDROID_LAYOUT
+ if (view()->frameView())
+ setVisibleWidth(view()->frameView()->textWrapWidth());
+#endif
+
+ if (isPositioned())
+ computePositionedLogicalWidth();
+
+ RenderBlock* cb = containingBlock();
+
+ int availableLogicalWidth = containingBlockLogicalWidthForContent();
+ bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
+ int containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
+
+ LengthType logicalWidthType = style()->logicalWidth().type();
+ if (logicalWidthType > Relative && style()->logicalWidth().isPositive()) {
+ // Percent or fixed table
+ setLogicalWidth(style()->logicalWidth().calcMinValue(containerWidthInInlineDirection));
+ setLogicalWidth(max(minPreferredLogicalWidth(), logicalWidth()));
+ } else {
+ // Subtract out any fixed margins from our available width for auto width tables.
+ int marginTotal = 0;
+ if (!style()->marginStart().isAuto())
+ marginTotal += style()->marginStart().calcValue(availableLogicalWidth);
+ if (!style()->marginEnd().isAuto())
+ marginTotal += style()->marginEnd().calcValue(availableLogicalWidth);
+
+ // Subtract out our margins to get the available content width.
+ int availableContentLogicalWidth = max(0, containerWidthInInlineDirection - marginTotal);
+
+ // Ensure we aren't bigger than our max width or smaller than our min width.
+ setLogicalWidth(min(availableContentLogicalWidth, maxPreferredLogicalWidth()));
+ }
+
+ setLogicalWidth(max(logicalWidth(), minPreferredLogicalWidth()));
+
+ // Finally, with our true width determined, compute our margins for real.
+ setMarginStart(0);
+ setMarginEnd(0);
+#ifdef ANDROID_LAYOUT
+ // in SSR mode, we ignore left/right margin for table
+ if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
+ return;
+#endif
+ if (!hasPerpendicularContainingBlock)
+ computeInlineDirectionMargins(cb, availableLogicalWidth, logicalWidth());
+ else {
+ setMarginStart(style()->marginStart().calcMinValue(availableLogicalWidth));
+ setMarginEnd(style()->marginEnd().calcMinValue(availableLogicalWidth));
+ }
+}
+
+void RenderTable::layout()
+{
+ ASSERT(needsLayout());
+
+ if (layoutOnlyPositionedObjects())
+ return;
+
+ recalcSectionsIfNeeded();
+
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
+ LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
+
+ setLogicalHeight(0);
+ m_overflow.clear();
+
+ initMaxMarginValues();
+
+#ifdef ANDROID_LAYOUT
+ bool relayoutChildren = false;
+#endif
+
+ int oldLogicalWidth = logicalWidth();
+ computeLogicalWidth();
+
+#ifdef ANDROID_LAYOUT
+ if (!checkAndSetRelayoutChildren(&relayoutChildren)
+ && document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
+ // if the width of a table is wider than its container width, or it has a nested table,
+ // we will render it with single column.
+ int cw = containingBlockLogicalWidthForContent();
+ bool shouldRenderAsSingleColumn = (width() > cw);
+ if (!shouldRenderAsSingleColumn) {
+ RenderObject* child = firstChild();
+ while (child) {
+ if (child->isTable()) {
+ shouldRenderAsSingleColumn = true;
+ break;
+ }
+ child = child->nextInPreOrder();
+ }
+ }
+
+ if (shouldRenderAsSingleColumn) {
+ m_singleColumn = true;
+ if (width() > cw)
+ setWidth(cw);
+ if (m_minPreferredLogicalWidth > cw)
+ m_minPreferredLogicalWidth = cw;
+ if (m_maxPreferredLogicalWidth > cw)
+ m_maxPreferredLogicalWidth = cw;
+ }
+ }
+#endif
+ if (m_caption && logicalWidth() != oldLogicalWidth)
+ m_caption->setNeedsLayout(true, false);
+
+ // FIXME: The optimisation below doesn't work since the internal table
+ // layout could have changed. we need to add a flag to the table
+ // layout that tells us if something has changed in the min max
+ // calculations to do it correctly.
+// if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
+ m_tableLayout->layout();
+
+ setCellLogicalWidths();
+
+ int totalSectionLogicalHeight = 0;
+ int oldTableLogicalTop = m_caption ? m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter() : 0;
+
+ bool collapsing = collapseBorders();
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+#ifdef ANDROID_LAYOUT
+ if (relayoutChildren) {
+ child->setNeedsLayout(true, false);
+ if (!child->isTableSection()) {
+ child->layoutIfNeeded();
+ continue;
+ }
+ // fall through
+ }
+#endif
+ if (child->isTableSection()) {
+ child->layoutIfNeeded();
+ RenderTableSection* section = toRenderTableSection(child);
+ totalSectionLogicalHeight += section->calcRowLogicalHeight();
+ if (collapsing)
+ section->recalcOuterBorder();
+ ASSERT(!section->needsLayout());
+ } else if (child->isTableCol()) {
+ child->layoutIfNeeded();
+ ASSERT(!child->needsLayout());
+ }
+ }
+
+ // Only lay out one caption, since it's the only one we're going to end up painting.
+ if (m_caption)
+ m_caption->layoutIfNeeded();
+
+ // If any table section moved vertically, we will just repaint everything from that
+ // section down (it is quite unlikely that any of the following sections
+ // did not shift).
+ bool sectionMoved = false;
+ int movedSectionLogicalTop = 0;
+
+ // FIXME: Collapse caption margin.
+ if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) {
+ IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
+
+ m_caption->setLogicalLocation(m_caption->marginStart(), logicalHeight());
+ if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
+ m_caption->repaintDuringLayoutIfMoved(captionRect);
+
+ setLogicalHeight(logicalHeight() + m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter());
+
+ if (logicalHeight() != oldTableLogicalTop) {
+ sectionMoved = true;
+ movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop);
+ }
+ }
+
+ int borderAndPaddingBefore = borderBefore() + (collapsing ? 0 : paddingBefore());
+ int borderAndPaddingAfter = borderAfter() + (collapsing ? 0 : paddingAfter());
+
+ setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
+
+ if (!isPositioned())
+ computeLogicalHeight();
+
+ Length logicalHeightLength = style()->logicalHeight();
+ int computedLogicalHeight = 0;
+ if (logicalHeightLength.isFixed()) {
+ // Tables size as though CSS height includes border/padding.
+ computedLogicalHeight = logicalHeightLength.value() - (borderAndPaddingBefore + borderAndPaddingAfter);
+ } else if (logicalHeightLength.isPercent())
+ computedLogicalHeight = computePercentageLogicalHeight(logicalHeightLength);
+ computedLogicalHeight = max(0, computedLogicalHeight);
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one.
+ toRenderTableSection(child)->layoutRows(child == m_firstBody ? max(0, computedLogicalHeight - totalSectionLogicalHeight) : 0);
+ }
+
+ if (!m_firstBody && computedLogicalHeight > totalSectionLogicalHeight && !document()->inQuirksMode()) {
+ // Completely empty tables (with no sections or anything) should at least honor specified height
+ // in strict mode.
+ setLogicalHeight(logicalHeight() + computedLogicalHeight);
+ }
+
+ int sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd();
+ if (!collapsing)
+ sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd();
+
+ // position the table sections
+ RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+ while (section) {
+ if (!sectionMoved && section->logicalTop() != logicalHeight()) {
+ sectionMoved = true;
+ movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->topVisualOverflow() : section->leftVisualOverflow());
+ }
+ section->setLogicalLocation(sectionLogicalLeft, logicalHeight());
+
+ setLogicalHeight(logicalHeight() + section->logicalHeight());
+ section = sectionBelow(section);
+ }
+
+ setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
+
+ if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) {
+ IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
+
+ m_caption->setLogicalLocation(m_caption->marginStart(), logicalHeight());
+ if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
+ m_caption->repaintDuringLayoutIfMoved(captionRect);
+
+ setLogicalHeight(logicalHeight() + m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter());
+ }
+
+ if (isPositioned())
+ computeLogicalHeight();
+
+ // table can be containing block of positioned elements.
+ // FIXME: Only pass true if width or height changed.
+ layoutPositionedObjects(true);
+
+ updateLayerTransform();
+
+ computeOverflow(clientLogicalBottom());
+
+ statePusher.pop();
+
+ if (view()->layoutState()->pageLogicalHeight())
+ setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(y()));
+
+ bool didFullRepaint = repainter.repaintAfterLayout();
+ // Repaint with our new bounds if they are different from our old bounds.
+ if (!didFullRepaint && sectionMoved) {
+ if (style()->isHorizontalWritingMode())
+ repaintRectangle(IntRect(leftVisualOverflow(), movedSectionLogicalTop, rightVisualOverflow() - leftVisualOverflow(), bottomVisualOverflow() - movedSectionLogicalTop));
+ else
+ repaintRectangle(IntRect(movedSectionLogicalTop, topVisualOverflow(), rightVisualOverflow() - movedSectionLogicalTop, bottomVisualOverflow() - topVisualOverflow()));
+ }
+
+ setNeedsLayout(false);
+}
+
+void RenderTable::addOverflowFromChildren()
+{
+ // Add overflow from borders.
+ // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our
+ // descendant objects, but since tables don't support overflow:auto, this works out fine.
+ if (collapseBorders()) {
+ int rightBorderOverflow = width() + outerBorderRight() - borderRight();
+ int leftBorderOverflow = borderLeft() - outerBorderLeft();
+ int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
+ int topBorderOverflow = borderTop() - outerBorderTop();
+ IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
+ if (borderOverflowRect != borderBoxRect()) {
+ addLayoutOverflow(borderOverflowRect);
+ addVisualOverflow(borderOverflowRect);
+ }
+ }
+
+ // Add overflow from our caption.
+ if (m_caption)
+ addOverflowFromChild(m_caption);
+
+ // Add overflow from our sections.
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection()) {
+ RenderTableSection* section = toRenderTableSection(child);
+ addOverflowFromChild(section);
+ }
+ }
+}
+
+void RenderTable::setCellLogicalWidths()
+{
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ toRenderTableSection(child)->setCellLogicalWidths();
+ }
+}
+
+void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ tx += x();
+ ty += y();
+
+ PaintPhase paintPhase = paintInfo.phase;
+
+ int os = 2 * maximalOutlineSize(paintPhase);
+ if (ty + topVisualOverflow() >= paintInfo.rect.bottom() + os || ty + bottomVisualOverflow() <= paintInfo.rect.y() - os)
+ return;
+ if (tx + leftVisualOverflow() >= paintInfo.rect.right() + os || tx + rightVisualOverflow() <= paintInfo.rect.x() - os)
+ return;
+
+ bool pushedClip = pushContentsClip(paintInfo, tx, ty);
+ paintObject(paintInfo, tx, ty);
+ if (pushedClip)
+ popContentsClip(paintInfo, paintPhase, tx, ty);
+}
+
+void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty)
+{
+ PaintPhase paintPhase = paintInfo.phase;
+ if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
+ paintBoxDecorations(paintInfo, tx, ty);
+
+ if (paintPhase == PaintPhaseMask) {
+ paintMask(paintInfo, tx, ty);
+ return;
+ }
+
+ // We're done. We don't bother painting any children.
+ if (paintPhase == PaintPhaseBlockBackground)
+ return;
+
+ // We don't paint our own background, but we do let the kids paint their backgrounds.
+ if (paintPhase == PaintPhaseChildBlockBackgrounds)
+ paintPhase = PaintPhaseChildBlockBackground;
+
+ PaintInfo info(paintInfo);
+ info.phase = paintPhase;
+ info.updatePaintingRootForChildren(this);
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption)) {
+ IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ child->paint(info, childPoint.x(), childPoint.y());
+ }
+ }
+
+ if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
+ // Collect all the unique border styles that we want to paint in a sorted list. Once we
+ // have all the styles sorted, we then do individual passes, painting each style of border
+ // from lowest precedence to highest precedence.
+ info.phase = PaintPhaseCollapsedTableBorders;
+ RenderTableCell::CollapsedBorderStyles borderStyles;
+ RenderObject* stop = nextInPreOrderAfterChildren();
+ for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder()) {
+ if (o->isTableCell())
+ toRenderTableCell(o)->collectBorderStyles(borderStyles);
+ }
+ RenderTableCell::sortBorderStyles(borderStyles);
+ size_t count = borderStyles.size();
+ for (size_t i = 0; i < count; ++i) {
+ m_currentBorder = &borderStyles[i];
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling())
+ if (child->isTableSection()) {
+ IntPoint childPoint = flipForWritingMode(toRenderTableSection(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ child->paint(info, childPoint.x(), childPoint.y());
+ }
+ }
+ m_currentBorder = 0;
+ }
+}
+
+void RenderTable::subtractCaptionRect(IntRect& rect) const
+{
+ if (!m_caption)
+ return;
+
+ int captionLogicalHeight = m_caption->logicalHeight() + m_caption->marginBefore() + m_caption->marginAfter();
+ bool captionIsBefore = (m_caption->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode();
+ if (style()->isHorizontalWritingMode()) {
+ rect.setHeight(rect.height() - captionLogicalHeight);
+ if (captionIsBefore)
+ rect.move(0, captionLogicalHeight);
+ } else {
+ rect.setWidth(rect.width() - captionLogicalHeight);
+ if (captionIsBefore)
+ rect.move(captionLogicalHeight, 0);
+ }
+}
+
+void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return;
+
+ IntRect rect(tx, ty, width(), height());
+ subtractCaptionRect(rect);
+
+ paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Normal);
+
+ paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), rect.x(), rect.y(), rect.width(), rect.height());
+ paintBoxShadow(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style(), Inset);
+
+ if (style()->hasBorder() && !collapseBorders())
+ paintBorder(paintInfo.context, rect.x(), rect.y(), rect.width(), rect.height(), style());
+}
+
+void RenderTable::paintMask(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
+ return;
+
+ IntRect rect(tx, ty, width(), height());
+ subtractCaptionRect(rect);
+
+ paintMaskImages(paintInfo, rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void RenderTable::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ recalcSectionsIfNeeded();
+ recalcBordersInRowDirection();
+
+ m_tableLayout->computePreferredLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
+
+ if (m_caption)
+ m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_caption->minPreferredLogicalWidth());
+
+ setPreferredLogicalWidthsDirty(false);
+}
+
+void RenderTable::splitColumn(int pos, int firstSpan)
+{
+ // we need to add a new columnStruct
+ int oldSize = m_columns.size();
+ m_columns.grow(oldSize + 1);
+ int oldSpan = m_columns[pos].span;
+ ASSERT(oldSpan > firstSpan);
+ m_columns[pos].span = firstSpan;
+ memmove(m_columns.data() + pos + 1, m_columns.data() + pos, (oldSize - pos) * sizeof(ColumnStruct));
+ m_columns[pos + 1].span = oldSpan - firstSpan;
+
+ // change width of all rows.
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ toRenderTableSection(child)->splitColumn(pos, firstSpan);
+ }
+
+ m_columnPos.grow(numEffCols() + 1);
+ setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+void RenderTable::appendColumn(int span)
+{
+ // easy case.
+ int pos = m_columns.size();
+ int newSize = pos + 1;
+ m_columns.grow(newSize);
+ m_columns[pos].span = span;
+
+ // change width of all rows.
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection())
+ toRenderTableSection(child)->appendColumn(pos);
+ }
+
+ m_columnPos.grow(numEffCols() + 1);
+ setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+RenderTableCol* RenderTable::nextColElement(RenderTableCol* current) const
+{
+ RenderObject* next = current->firstChild();
+ if (!next)
+ next = current->nextSibling();
+ if (!next && current->parent()->isTableCol())
+ next = current->parent()->nextSibling();
+
+ while (next) {
+ if (next->isTableCol())
+ return toRenderTableCol(next);
+ if (next != m_caption)
+ return 0;
+ next = next->nextSibling();
+ }
+
+ return 0;
+}
+
+RenderTableCol* RenderTable::colElement(int col, bool* startEdge, bool* endEdge) const
+{
+ if (!m_hasColElements)
+ return 0;
+ RenderObject* child = firstChild();
+ int cCol = 0;
+
+ while (child) {
+ if (child->isTableCol())
+ break;
+ if (child != m_caption)
+ return 0;
+ child = child->nextSibling();
+ }
+ if (!child)
+ return 0;
+
+ RenderTableCol* colElem = toRenderTableCol(child);
+ while (colElem) {
+ int span = colElem->span();
+ if (!colElem->firstChild()) {
+ int startCol = cCol;
+ int endCol = cCol + span - 1;
+ cCol += span;
+ if (cCol > col) {
+ if (startEdge)
+ *startEdge = startCol == col;
+ if (endEdge)
+ *endEdge = endCol == col;
+ return colElem;
+ }
+ }
+ colElem = nextColElement(colElem);
+ }
+
+ return 0;
+}
+
+void RenderTable::recalcSections() const
+{
+ m_caption = 0;
+ m_head = 0;
+ m_foot = 0;
+ m_firstBody = 0;
+ m_hasColElements = false;
+
+ // We need to get valid pointers to caption, head, foot and first body again
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ switch (child->style()->display()) {
+ case TABLE_CAPTION:
+ if (!m_caption && child->isRenderBlock()) {
+ m_caption = toRenderBlock(child);
+ m_caption->setNeedsLayout(true);
+ }
+ break;
+ case TABLE_COLUMN:
+ case TABLE_COLUMN_GROUP:
+ m_hasColElements = true;
+ break;
+ case TABLE_HEADER_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection* section = toRenderTableSection(child);
+ if (!m_head)
+ m_head = section;
+ else if (!m_firstBody)
+ m_firstBody = section;
+ section->recalcCellsIfNeeded();
+ }
+ break;
+ case TABLE_FOOTER_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection* section = toRenderTableSection(child);
+ if (!m_foot)
+ m_foot = section;
+ else if (!m_firstBody)
+ m_firstBody = section;
+ section->recalcCellsIfNeeded();
+ }
+ break;
+ case TABLE_ROW_GROUP:
+ if (child->isTableSection()) {
+ RenderTableSection* section = toRenderTableSection(child);
+ if (!m_firstBody)
+ m_firstBody = section;
+ section->recalcCellsIfNeeded();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
+ int maxCols = 0;
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableSection()) {
+ RenderTableSection* section = toRenderTableSection(child);
+ int sectionCols = section->numColumns();
+ if (sectionCols > maxCols)
+ maxCols = sectionCols;
+ }
+ }
+
+ m_columns.resize(maxCols);
+ m_columnPos.resize(maxCols + 1);
+
+ ASSERT(selfNeedsLayout());
+
+ m_needsSectionRecalc = false;
+}
+
+int RenderTable::calcBorderStart() const
+{
+ if (collapseBorders()) {
+ // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
+ if (!numEffCols())
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& tb = style()->borderStart();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = tb.width();
+
+ if (RenderTableCol* colGroup = colElement(0)) {
+ const BorderValue& gb = colGroup->style()->borderStart();
+ if (gb.style() == BHIDDEN)
+ return 0;
+ if (gb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(gb.width()));
+ }
+
+ RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+ if (firstNonEmptySection && !firstNonEmptySection->numRows())
+ firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+
+ if (firstNonEmptySection) {
+ const BorderValue& sb = firstNonEmptySection->style()->borderStart();
+ if (sb.style() == BHIDDEN)
+ return 0;
+
+ if (sb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(sb.width()));
+
+ const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, 0);
+
+ if (cs.hasCells()) {
+ const BorderValue& cb = cs.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicualr and flipped cells.
+ if (cb.style() == BHIDDEN)
+ return 0;
+
+ const BorderValue& rb = cs.primaryCell()->parent()->style()->borderStart();
+ if (rb.style() == BHIDDEN)
+ return 0;
+
+ if (cb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(cb.width()));
+ if (rb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(rb.width()));
+ }
+ }
+ return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
+ }
+ return RenderBlock::borderStart();
+}
+
+int RenderTable::calcBorderEnd() const
+{
+ if (collapseBorders()) {
+ // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
+ if (!numEffCols())
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& tb = style()->borderEnd();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = tb.width();
+
+ int endColumn = numEffCols() - 1;
+ if (RenderTableCol* colGroup = colElement(endColumn)) {
+ const BorderValue& gb = colGroup->style()->borderEnd();
+ if (gb.style() == BHIDDEN)
+ return 0;
+ if (gb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(gb.width()));
+ }
+
+ RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+ if (firstNonEmptySection && !firstNonEmptySection->numRows())
+ firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+
+ if (firstNonEmptySection) {
+ const BorderValue& sb = firstNonEmptySection->style()->borderEnd();
+ if (sb.style() == BHIDDEN)
+ return 0;
+
+ if (sb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(sb.width()));
+
+ const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, endColumn);
+
+ if (cs.hasCells()) {
+ const BorderValue& cb = cs.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
+ if (cb.style() == BHIDDEN)
+ return 0;
+
+ const BorderValue& rb = cs.primaryCell()->parent()->style()->borderEnd();
+ if (rb.style() == BHIDDEN)
+ return 0;
+
+ if (cb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(cb.width()));
+ if (rb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<unsigned>(rb.width()));
+ }
+ }
+ return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
+ }
+ return RenderBlock::borderEnd();
+}
+
+void RenderTable::recalcBordersInRowDirection()
+{
+ m_borderStart = calcBorderStart();
+ m_borderEnd = calcBorderEnd();
+}
+
+int RenderTable::borderBefore() const
+{
+ if (collapseBorders())
+ return outerBorderBefore();
+ return RenderBlock::borderBefore();
+}
+
+int RenderTable::borderAfter() const
+{
+ if (collapseBorders())
+ return outerBorderAfter();
+ return RenderBlock::borderAfter();
+}
+
+int RenderTable::outerBorderBefore() const
+{
+ if (!collapseBorders())
+ return 0;
+ int borderWidth = 0;
+ RenderTableSection* topSection;
+ if (m_head)
+ topSection = m_head;
+ else if (m_firstBody)
+ topSection = m_firstBody;
+ else if (m_foot)
+ topSection = m_foot;
+ else
+ topSection = 0;
+ if (topSection) {
+ borderWidth = topSection->outerBorderBefore();
+ if (borderWidth == -1)
+ return 0; // Overridden by hidden
+ }
+ const BorderValue& tb = style()->borderBefore();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<int>(tb.width() / 2));
+ return borderWidth;
+}
+
+int RenderTable::outerBorderAfter() const
+{
+ if (!collapseBorders())
+ return 0;
+ int borderWidth = 0;
+ RenderTableSection* bottomSection;
+ if (m_foot)
+ bottomSection = m_foot;
+ else {
+ RenderObject* child;
+ for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) { }
+ bottomSection = child ? toRenderTableSection(child) : 0;
+ }
+ if (bottomSection) {
+ borderWidth = bottomSection->outerBorderAfter();
+ if (borderWidth == -1)
+ return 0; // Overridden by hidden
+ }
+ const BorderValue& tb = style()->borderAfter();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = max(borderWidth, static_cast<int>((tb.width() + 1) / 2));
+ return borderWidth;
+}
+
+int RenderTable::outerBorderStart() const
+{
+ if (!collapseBorders())
+ return 0;
+
+ int borderWidth = 0;
+
+ const BorderValue& tb = style()->borderStart();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
+
+ bool allHidden = true;
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (!child->isTableSection())
+ continue;
+ int sw = toRenderTableSection(child)->outerBorderStart();
+ if (sw == -1)
+ continue;
+ else
+ allHidden = false;
+ borderWidth = max(borderWidth, sw);
+ }
+ if (allHidden)
+ return 0;
+
+ return borderWidth;
+}
+
+int RenderTable::outerBorderEnd() const
+{
+ if (!collapseBorders())
+ return 0;
+
+ int borderWidth = 0;
+
+ const BorderValue& tb = style()->borderEnd();
+ if (tb.style() == BHIDDEN)
+ return 0;
+ if (tb.style() > BHIDDEN)
+ borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
+
+ bool allHidden = true;
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (!child->isTableSection())
+ continue;
+ int sw = toRenderTableSection(child)->outerBorderEnd();
+ if (sw == -1)
+ continue;
+ else
+ allHidden = false;
+ borderWidth = max(borderWidth, sw);
+ }
+ if (allHidden)
+ return 0;
+
+ return borderWidth;
+}
+
+RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const
+{
+ recalcSectionsIfNeeded();
+
+ if (section == m_head)
+ return 0;
+
+ RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
+ while (prevSection) {
+ if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (!skipEmptySections || toRenderTableSection(prevSection)->numRows()))
+ break;
+ prevSection = prevSection->previousSibling();
+ }
+ if (!prevSection && m_head && (!skipEmptySections || m_head->numRows()))
+ prevSection = m_head;
+ return toRenderTableSection(prevSection);
+}
+
+RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const
+{
+ recalcSectionsIfNeeded();
+
+ if (section == m_foot)
+ return 0;
+
+ RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
+ while (nextSection) {
+ if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (!skipEmptySections || toRenderTableSection(nextSection)->numRows()))
+ break;
+ nextSection = nextSection->nextSibling();
+ }
+ if (!nextSection && m_foot && (!skipEmptySections || m_foot->numRows()))
+ nextSection = m_foot;
+ return toRenderTableSection(nextSection);
+}
+
+RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
+{
+ recalcSectionsIfNeeded();
+
+ // Find the section and row to look in
+ int r = cell->row();
+ RenderTableSection* section = 0;
+ int rAbove = 0;
+ if (r > 0) {
+ // cell is not in the first row, so use the above row in its own section
+ section = cell->section();
+ rAbove = r - 1;
+ } else {
+ section = sectionAbove(cell->section(), true);
+ if (section)
+ rAbove = section->numRows() - 1;
+ }
+
+ // Look up the cell in the section's grid, which requires effective col index
+ if (section) {
+ int effCol = colToEffCol(cell->col());
+ RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
+ return aboveCell.primaryCell();
+ } else
+ return 0;
+}
+
+RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
+{
+ recalcSectionsIfNeeded();
+
+ // Find the section and row to look in
+ int r = cell->row() + cell->rowSpan() - 1;
+ RenderTableSection* section = 0;
+ int rBelow = 0;
+ if (r < cell->section()->numRows() - 1) {
+ // The cell is not in the last row, so use the next row in the section.
+ section = cell->section();
+ rBelow = r + 1;
+ } else {
+ section = sectionBelow(cell->section(), true);
+ if (section)
+ rBelow = 0;
+ }
+
+ // Look up the cell in the section's grid, which requires effective col index
+ if (section) {
+ int effCol = colToEffCol(cell->col());
+ RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
+ return belowCell.primaryCell();
+ } else
+ return 0;
+}
+
+RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
+{
+ recalcSectionsIfNeeded();
+
+ RenderTableSection* section = cell->section();
+ int effCol = colToEffCol(cell->col());
+ if (!effCol)
+ return 0;
+
+ // If we hit a colspan back up to a real cell.
+ RenderTableSection::CellStruct& prevCell = section->cellAt(cell->row(), effCol - 1);
+ return prevCell.primaryCell();
+}
+
+RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
+{
+ recalcSectionsIfNeeded();
+
+ int effCol = colToEffCol(cell->col() + cell->colSpan());
+ if (effCol >= numEffCols())
+ return 0;
+ return cell->section()->primaryCellAt(cell->row(), effCol);
+}
+
+RenderBlock* RenderTable::firstLineBlock() const
+{
+ return 0;
+}
+
+void RenderTable::updateFirstLetter()
+{
+}
+
+int RenderTable::firstLineBoxBaseline() const
+{
+ if (isWritingModeRoot())
+ return -1;
+
+ RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+ if (firstNonEmptySection && !firstNonEmptySection->numRows())
+ firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+
+ if (!firstNonEmptySection)
+ return -1;
+
+ return firstNonEmptySection->logicalTop() + firstNonEmptySection->firstLineBoxBaseline();
+}
+
+IntRect RenderTable::overflowClipRect(int tx, int ty)
+{
+ IntRect rect = RenderBlock::overflowClipRect(tx, ty);
+
+ // If we have a caption, expand the clip to include the caption.
+ // FIXME: Technically this is wrong, but it's virtually impossible to fix this
+ // for real until captions have been re-written.
+ // FIXME: This code assumes (like all our other caption code) that only top/bottom are
+ // supported. When we actually support left/right and stop mapping them to top/bottom,
+ // we might have to hack this code first (depending on what order we do these bug fixes in).
+ if (m_caption) {
+ if (style()->isHorizontalWritingMode()) {
+ rect.setHeight(height());
+ rect.setY(ty);
+ } else {
+ rect.setWidth(width());
+ rect.setX(tx);
+ }
+ }
+
+ return rect;
+}
+
+bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
+{
+ tx += x();
+ ty += y();
+
+ // Check kids first.
+ if (!hasOverflowClip() || overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos))) {
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption)) {
+ IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ if (child->nodeAtPoint(request, result, xPos, yPos, childPoint.x(), childPoint.y(), action)) {
+ updateHitTestResult(result, IntPoint(xPos - childPoint.x(), yPos - childPoint.y()));
+ return true;
+ }
+ }
+ }
+ }
+
+ // Check our bounds next.
+ IntRect boundsRect = IntRect(tx, ty, width(), height());
+ if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectForPoint(xPos, yPos))) {
+ updateHitTestResult(result, flipForWritingMode(IntPoint(xPos - tx, yPos - ty)));
+ if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect))
+ return true;
+ }
+
+ return false;
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderTable.h b/Source/WebCore/rendering/RenderTable.h
new file mode 100644
index 0000000..6afb108
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTable.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 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 RenderTable_h
+#define RenderTable_h
+
+#include "CSSPropertyNames.h"
+#include "RenderBlock.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class RenderTableCol;
+class RenderTableCell;
+class RenderTableSection;
+class TableLayout;
+
+class RenderTable : public RenderBlock {
+public:
+ explicit RenderTable(Node*);
+ virtual ~RenderTable();
+
+ int getColumnPos(int col) const { return m_columnPos[col]; }
+
+ int hBorderSpacing() const { return m_hSpacing; }
+ int vBorderSpacing() const { return m_vSpacing; }
+
+ bool collapseBorders() const { return style()->borderCollapse(); }
+
+ int borderStart() const { return m_borderStart; }
+ int borderEnd() const { return m_borderEnd; }
+ int borderBefore() const;
+ int borderAfter() const;
+
+ int borderLeft() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isLeftToRightDirection() ? borderStart() : borderEnd();
+ return style()->isFlippedBlocksWritingMode() ? borderAfter() : borderBefore();
+ }
+
+ int borderRight() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isLeftToRightDirection() ? borderEnd() : borderStart();
+ return style()->isFlippedBlocksWritingMode() ? borderBefore() : borderAfter();
+ }
+
+ int borderTop() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isFlippedBlocksWritingMode() ? borderAfter() : borderBefore();
+ return style()->isLeftToRightDirection() ? borderStart() : borderEnd();
+ }
+
+ int borderBottom() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isFlippedBlocksWritingMode() ? borderBefore() : borderAfter();
+ return style()->isLeftToRightDirection() ? borderEnd() : borderStart();
+ }
+
+ const Color bgColor() const { return style()->visitedDependentColor(CSSPropertyBackgroundColor); }
+
+ int outerBorderBefore() const;
+ int outerBorderAfter() const;
+ int outerBorderStart() const;
+ int outerBorderEnd() const;
+
+ int outerBorderLeft() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isLeftToRightDirection() ? outerBorderStart() : outerBorderEnd();
+ return style()->isFlippedBlocksWritingMode() ? outerBorderAfter() : outerBorderBefore();
+ }
+
+ int outerBorderRight() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isLeftToRightDirection() ? outerBorderEnd() : outerBorderStart();
+ return style()->isFlippedBlocksWritingMode() ? outerBorderBefore() : outerBorderAfter();
+ }
+
+ int outerBorderTop() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isFlippedBlocksWritingMode() ? outerBorderAfter() : outerBorderBefore();
+ return style()->isLeftToRightDirection() ? outerBorderStart() : outerBorderEnd();
+ }
+
+ int outerBorderBottom() const
+ {
+ if (style()->isHorizontalWritingMode())
+ return style()->isFlippedBlocksWritingMode() ? outerBorderBefore() : outerBorderAfter();
+ return style()->isLeftToRightDirection() ? outerBorderEnd() : outerBorderStart();
+ }
+
+ int calcBorderStart() const;
+ int calcBorderEnd() const;
+ void recalcBordersInRowDirection();
+
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+
+ struct ColumnStruct {
+ enum {
+ WidthUndefined = 0xffff
+ };
+
+ ColumnStruct()
+ : span(1)
+ , width(WidthUndefined)
+ {
+ }
+
+ unsigned span;
+ unsigned width; // the calculated position of the column
+ };
+
+ Vector<ColumnStruct>& columns() { return m_columns; }
+ Vector<int>& columnPositions() { return m_columnPos; }
+ RenderTableSection* header() const { return m_head; }
+ RenderTableSection* footer() const { return m_foot; }
+ RenderTableSection* firstBody() const { return m_firstBody; }
+
+ void splitColumn(int pos, int firstSpan);
+ void appendColumn(int span);
+ int numEffCols() const { return m_columns.size(); }
+ int spanOfEffCol(int effCol) const { return m_columns[effCol].span; }
+
+ int colToEffCol(int col) const
+ {
+ int i = 0;
+ int effCol = numEffCols();
+ for (int c = 0; c < col && i < effCol; ++i)
+ c += m_columns[i].span;
+ return i;
+ }
+
+ int effColToCol(int effCol) const
+ {
+ int c = 0;
+ for (int i = 0; i < effCol; i++)
+ c += m_columns[i].span;
+ return c;
+ }
+
+ int bordersPaddingAndSpacingInRowDirection() const
+ {
+ return borderStart() + borderEnd() +
+ (collapseBorders() ? 0 : (paddingStart() + paddingEnd() + (numEffCols() + 1) * hBorderSpacing()));
+ }
+
+ RenderTableCol* colElement(int col, bool* startEdge = 0, bool* endEdge = 0) const;
+ RenderTableCol* nextColElement(RenderTableCol* current) const;
+
+ bool needsSectionRecalc() const { return m_needsSectionRecalc; }
+ void setNeedsSectionRecalc()
+ {
+ if (documentBeingDestroyed())
+ return;
+ m_needsSectionRecalc = true;
+ setNeedsLayout(true);
+ }
+
+ RenderTableSection* sectionAbove(const RenderTableSection*, bool skipEmptySections = false) const;
+ RenderTableSection* sectionBelow(const RenderTableSection*, bool skipEmptySections = false) const;
+
+ RenderTableCell* cellAbove(const RenderTableCell*) const;
+ RenderTableCell* cellBelow(const RenderTableCell*) const;
+ RenderTableCell* cellBefore(const RenderTableCell*) const;
+ RenderTableCell* cellAfter(const RenderTableCell*) const;
+
+ const CollapsedBorderValue* currentBorderStyle() const { return m_currentBorder; }
+
+ bool hasSections() const { return m_head || m_foot || m_firstBody; }
+
+ void recalcSectionsIfNeeded() const
+ {
+ if (m_needsSectionRecalc)
+ recalcSections();
+ }
+
+#ifdef ANDROID_LAYOUT
+ void clearSingleColumn() { m_singleColumn = false; }
+ bool isSingleColumn() const { return m_singleColumn; }
+#endif
+
+protected:
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+private:
+ virtual const char* renderName() const { return "RenderTable"; }
+
+ virtual bool isTable() const { return true; }
+
+ virtual bool avoidsFloats() const { return true; }
+
+ virtual void removeChild(RenderObject* oldChild);
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual void paintObject(PaintInfo&, int tx, int ty);
+ virtual void paintBoxDecorations(PaintInfo&, int tx, int ty);
+ virtual void paintMask(PaintInfo&, int tx, int ty);
+ virtual void layout();
+ virtual void computePreferredLogicalWidths();
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int xPos, int yPos, int tx, int ty, HitTestAction);
+
+ virtual int firstLineBoxBaseline() const;
+
+ virtual RenderBlock* firstLineBlock() const;
+ virtual void updateFirstLetter();
+
+ virtual void setCellLogicalWidths();
+
+ virtual void computeLogicalWidth();
+
+ virtual IntRect overflowClipRect(int tx, int ty);
+
+ virtual void addOverflowFromChildren();
+
+ void subtractCaptionRect(IntRect&) const;
+
+ void recalcSections() const;
+
+ mutable Vector<int> m_columnPos;
+ mutable Vector<ColumnStruct> m_columns;
+
+ mutable RenderBlock* m_caption;
+ mutable RenderTableSection* m_head;
+ mutable RenderTableSection* m_foot;
+ mutable RenderTableSection* m_firstBody;
+
+ OwnPtr<TableLayout> m_tableLayout;
+
+ const CollapsedBorderValue* m_currentBorder;
+
+ mutable bool m_hasColElements : 1;
+ mutable bool m_needsSectionRecalc : 1;
+
+#ifdef ANDROID_LAYOUT
+ bool m_singleColumn; // BS(Grace): should I use compact version?
+#endif
+ short m_hSpacing;
+ short m_vSpacing;
+ int m_borderStart;
+ int m_borderEnd;
+};
+
+inline RenderTable* toRenderTable(RenderObject* object)
+{
+ ASSERT(!object || object->isTable());
+ return static_cast<RenderTable*>(object);
+}
+
+inline const RenderTable* toRenderTable(const RenderObject* object)
+{
+ ASSERT(!object || object->isTable());
+ return static_cast<const RenderTable*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTable(const RenderTable*);
+
+} // namespace WebCore
+
+#endif // RenderTable_h
diff --git a/Source/WebCore/rendering/RenderTableCell.cpp b/Source/WebCore/rendering/RenderTableCell.cpp
new file mode 100644
index 0000000..0c2cf2f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableCell.cpp
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (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 "RenderTableCell.h"
+
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "HTMLTableCellElement.h"
+#include "RenderTableCol.h"
+#include "RenderView.h"
+#include "TransformState.h"
+
+#ifdef ANDROID_LAYOUT
+#include "Document.h"
+#include "Settings.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTableCell::RenderTableCell(Node* node)
+ : RenderBlock(node)
+ , m_row(-1)
+ , m_column(-1)
+ , m_rowSpan(1)
+ , m_columnSpan(1)
+ , m_intrinsicPaddingBefore(0)
+ , m_intrinsicPaddingAfter(0)
+{
+ updateFromElement();
+}
+
+void RenderTableCell::destroy()
+{
+ RenderTableSection* recalcSection = parent() ? section() : 0;
+
+ RenderBlock::destroy();
+
+ if (recalcSection)
+ recalcSection->setNeedsCellRecalc();
+}
+
+void RenderTableCell::updateFromElement()
+{
+ Node* n = node();
+ if (n && (n->hasTagName(tdTag) || n->hasTagName(thTag))) {
+ HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(n);
+ int oldRSpan = m_rowSpan;
+ int oldCSpan = m_columnSpan;
+
+ m_columnSpan = tc->colSpan();
+ m_rowSpan = tc->rowSpan();
+ if ((oldRSpan != m_rowSpan || oldCSpan != m_columnSpan) && style() && parent()) {
+ setNeedsLayoutAndPrefWidthsRecalc();
+ if (section())
+ section()->setNeedsCellRecalc();
+ }
+ }
+}
+
+Length RenderTableCell::styleOrColLogicalWidth() const
+{
+ Length w = style()->logicalWidth();
+ if (!w.isAuto())
+ return w;
+
+ if (RenderTableCol* tableCol = table()->colElement(col())) {
+ int colSpanCount = colSpan();
+
+ Length colWidthSum = Length(0, Fixed);
+ for (int i = 1; i <= colSpanCount; i++) {
+ Length colWidth = tableCol->style()->logicalWidth();
+
+ // Percentage value should be returned only for colSpan == 1.
+ // Otherwise we return original width for the cell.
+ if (!colWidth.isFixed()) {
+ if (colSpanCount > 1)
+ return w;
+ return colWidth;
+ }
+
+ colWidthSum = Length(colWidthSum.value() + colWidth.value(), Fixed);
+
+ tableCol = table()->nextColElement(tableCol);
+ // If no next <col> tag found for the span we just return what we have for now.
+ if (!tableCol)
+ break;
+ }
+
+ // Column widths specified on <col> apply to the border box of the cell.
+ // Percentages don't need to be handled since they're always treated this way (even when specified on the cells).
+ // See Bugzilla bug 8126 for details.
+ if (colWidthSum.isFixed() && colWidthSum.value() > 0)
+ colWidthSum = Length(max(0, colWidthSum.value() - borderAndPaddingLogicalWidth()), Fixed);
+ return colWidthSum;
+ }
+
+ return w;
+}
+
+void RenderTableCell::computePreferredLogicalWidths()
+{
+ // The child cells rely on the grids up in the sections to do their computePreferredLogicalWidths work. Normally the sections are set up early, as table
+ // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections'
+ // grids. We must refresh those grids before the child cells try to use them.
+ table()->recalcSectionsIfNeeded();
+
+ RenderBlock::computePreferredLogicalWidths();
+ if (node() && style()->autoWrap()) {
+ // See if nowrap was set.
+ Length w = styleOrColLogicalWidth();
+ String nowrap = static_cast<Element*>(node())->getAttribute(nowrapAttr);
+ if (!nowrap.isNull() && w.isFixed())
+ // Nowrap is set, but we didn't actually use it because of the
+ // fixed width set on the cell. Even so, it is a WinIE/Moz trait
+ // to make the minwidth of the cell into the fixed width. They do this
+ // even in strict mode, so do not make this a quirk. Affected the top
+ // of hiptop.com.
+ m_minPreferredLogicalWidth = max(w.value(), m_minPreferredLogicalWidth);
+ }
+}
+
+void RenderTableCell::computeLogicalWidth()
+{
+#ifdef ANDROID_LAYOUT
+ if (view()->frameView())
+ setVisibleWidth(view()->frameView()->textWrapWidth());
+#endif
+}
+
+void RenderTableCell::updateLogicalWidth(int w)
+{
+ if (w == logicalWidth())
+ return;
+
+ setLogicalWidth(w);
+ setCellWidthChanged(true);
+}
+
+void RenderTableCell::layout()
+{
+ layoutBlock(cellWidthChanged());
+ setCellWidthChanged(false);
+}
+
+int RenderTableCell::paddingTop(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingTop();
+ if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode())
+ return result;
+ return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter());
+}
+
+int RenderTableCell::paddingBottom(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingBottom();
+ if (!includeIntrinsicPadding || !style()->isHorizontalWritingMode())
+ return result;
+ return result + (style()->writingMode() == TopToBottomWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore());
+}
+
+int RenderTableCell::paddingLeft(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingLeft();
+ if (!includeIntrinsicPadding || style()->isHorizontalWritingMode())
+ return result;
+ return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter());
+
+}
+
+int RenderTableCell::paddingRight(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingRight();
+ if (!includeIntrinsicPadding || style()->isHorizontalWritingMode())
+ return result;
+ return result + (style()->writingMode() == LeftToRightWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore());
+}
+
+int RenderTableCell::paddingBefore(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingBefore();
+ if (!includeIntrinsicPadding)
+ return result;
+ return result + intrinsicPaddingBefore();
+}
+
+int RenderTableCell::paddingAfter(bool includeIntrinsicPadding) const
+{
+ int result = RenderBlock::paddingAfter();
+ if (!includeIntrinsicPadding)
+ return result;
+ return result + intrinsicPaddingAfter();
+}
+
+void RenderTableCell::setOverrideSize(int size)
+{
+ clearIntrinsicPadding();
+ RenderBlock::setOverrideSize(size);
+}
+
+IntSize RenderTableCell::offsetFromContainer(RenderObject* o, const IntPoint& point) const
+{
+ ASSERT(o == container());
+
+ IntSize offset = RenderBlock::offsetFromContainer(o, point);
+ if (parent())
+ offset.expand(-parentBox()->x(), -parentBox()->y());
+
+ return offset;
+}
+
+IntRect RenderTableCell::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ // If the table grid is dirty, we cannot get reliable information about adjoining cells,
+ // so we ignore outside borders. This should not be a problem because it means that
+ // the table is going to recalculate the grid, relayout and repaint its current rect, which
+ // includes any outside borders of this cell.
+ if (!table()->collapseBorders() || table()->needsSectionRecalc())
+ return RenderBlock::clippedOverflowRectForRepaint(repaintContainer);
+
+ bool rtl = !table()->style()->isLeftToRightDirection();
+ int outlineSize = style()->outlineSize();
+ int left = max(borderHalfLeft(true), outlineSize);
+ int right = max(borderHalfRight(true), outlineSize);
+ int top = max(borderHalfTop(true), outlineSize);
+ int bottom = max(borderHalfBottom(true), outlineSize);
+ if ((left && !rtl) || (right && rtl)) {
+ if (RenderTableCell* before = table()->cellBefore(this)) {
+ top = max(top, before->borderHalfTop(true));
+ bottom = max(bottom, before->borderHalfBottom(true));
+ }
+ }
+ if ((left && rtl) || (right && !rtl)) {
+ if (RenderTableCell* after = table()->cellAfter(this)) {
+ top = max(top, after->borderHalfTop(true));
+ bottom = max(bottom, after->borderHalfBottom(true));
+ }
+ }
+ if (top) {
+ if (RenderTableCell* above = table()->cellAbove(this)) {
+ left = max(left, above->borderHalfLeft(true));
+ right = max(right, above->borderHalfRight(true));
+ }
+ }
+ if (bottom) {
+ if (RenderTableCell* below = table()->cellBelow(this)) {
+ left = max(left, below->borderHalfLeft(true));
+ right = max(right, below->borderHalfRight(true));
+ }
+ }
+ left = max(left, -leftVisualOverflow());
+ top = max(top, -topVisualOverflow());
+ IntRect r(-left, - top, left + max(width() + right, rightVisualOverflow()), top + max(height() + bottom, bottomVisualOverflow()));
+
+ if (RenderView* v = view()) {
+ // 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());
+ }
+ computeRectForRepaint(repaintContainer, r);
+ return r;
+}
+
+void RenderTableCell::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& r, bool fixed)
+{
+ if (repaintContainer == this)
+ return;
+ r.setY(r.y());
+ RenderView* v = view();
+ if ((!v || !v->layoutStateEnabled() || repaintContainer) && parent())
+ r.move(-parentBox()->x(), -parentBox()->y()); // Rows are in the same coordinate space, so don't add their offset in.
+ RenderBlock::computeRectForRepaint(repaintContainer, r, fixed);
+}
+
+int RenderTableCell::baselinePosition() const
+{
+ // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of
+ // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there
+ // is no such line box or table-row, the baseline is the bottom of content edge of the cell box.
+ int firstLineBaseline = firstLineBoxBaseline();
+ if (firstLineBaseline != -1)
+ return firstLineBaseline;
+ return paddingBefore() + borderBefore() + contentLogicalHeight();
+}
+
+void RenderTableCell::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (parent() && section() && style() && style()->height() != newStyle->height())
+ section()->setNeedsCellRecalc();
+
+ ASSERT(newStyle->display() == TABLE_CELL);
+
+ RenderBlock::styleWillChange(diff, newStyle);
+}
+
+void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+ setHasBoxDecorations(true);
+}
+
+// The following rules apply for resolving conflicts and figuring out which border
+// to use.
+// (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting
+// borders. Any border with this value suppresses all borders at this location.
+// (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all
+// the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is
+// the default value for the border style.)
+// (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders
+// are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred
+// in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
+// (4) If border styles differ only in color, then a style set on a cell wins over one on a row,
+// which wins over a row group, column, column group and, lastly, table. It is undefined which color
+// is used when two elements of the same type disagree.
+static int compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2)
+{
+ // Sanity check the values passed in. The null border have lowest priority.
+ if (!border2.exists()) {
+ if (!border1.exists())
+ return 0;
+ return 1;
+ }
+ if (!border1.exists())
+ return -1;
+
+ // Rule #1 above.
+ if (border2.style() == BHIDDEN) {
+ if (border1.style() == BHIDDEN)
+ return 0;
+ return -1;
+ }
+ if (border1.style() == BHIDDEN)
+ return 1;
+
+ // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border.
+ if (border2.style() == BNONE) {
+ if (border1.style() == BNONE)
+ return 0;
+ return 1;
+ }
+ if (border1.style() == BNONE)
+ return -1;
+
+ // The first part of rule #3 above. Wider borders win.
+ if (border1.width() != border2.width())
+ return border1.width() < border2.width() ? -1 : 1;
+
+ // The borders have equal width. Sort by border style.
+ if (border1.style() != border2.style())
+ return border1.style() < border2.style() ? -1 : 1;
+
+ // The border have the same width and style. Rely on precedence (cell over row over row group, etc.)
+ if (border1.precedence() == border2.precedence())
+ return 0;
+ return border1.precedence() < border2.precedence() ? -1 : 1;
+}
+
+static CollapsedBorderValue chooseBorder(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2)
+{
+ const CollapsedBorderValue& border = compareBorders(border1, border2) < 0 ? border2 : border1;
+ return border.style() == BHIDDEN ? CollapsedBorderValue() : border;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedStartBorder() const
+{
+ RenderTable* table = this->table();
+ bool isStartColumn = col() == 0;
+
+ // For the start border, we need to check, in order of precedence:
+ // (1) Our start border.
+ int start = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderStartColor, table->style()->direction(), table->style()->writingMode());
+ int end = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderEndColor, table->style()->direction(), table->style()->writingMode());
+ CollapsedBorderValue result(&style()->borderStart(), style()->visitedDependentColor(start), BCELL);
+
+ // (2) The end border of the preceding cell.
+ if (RenderTableCell* prevCell = table->cellBefore(this)) {
+ CollapsedBorderValue prevCellBorder = CollapsedBorderValue(&prevCell->style()->borderEnd(), prevCell->style()->visitedDependentColor(end), BCELL);
+ result = chooseBorder(prevCellBorder, result);
+ if (!result.exists())
+ return result;
+ } else if (isStartColumn) {
+ // (3) Our row's start border.
+ result = chooseBorder(result, CollapsedBorderValue(&parent()->style()->borderStart(), parent()->style()->visitedDependentColor(start), BROW));
+ if (!result.exists())
+ return result;
+
+ // (4) Our row group's start border.
+ result = chooseBorder(result, CollapsedBorderValue(&section()->style()->borderStart(), section()->style()->visitedDependentColor(start), BROWGROUP));
+ if (!result.exists())
+ return result;
+ }
+
+ // (5) Our column and column group's start borders.
+ bool startColEdge;
+ bool endColEdge;
+ RenderTableCol* colElt = table->colElement(col(), &startColEdge, &endColEdge);
+ if (colElt && startColEdge) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->style()->borderStart(), colElt->style()->visitedDependentColor(start), BCOL));
+ if (!result.exists())
+ return result;
+ if (colElt->parent()->isTableCol() && !colElt->previousSibling()) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->parent()->style()->borderStart(), colElt->parent()->style()->visitedDependentColor(start), BCOLGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ // (6) The end border of the preceding column.
+ if (!isStartColumn) {
+ colElt = table->colElement(col() -1, &startColEdge, &endColEdge);
+ if (colElt && endColEdge) {
+ CollapsedBorderValue endBorder = CollapsedBorderValue(&colElt->style()->borderEnd(), colElt->style()->visitedDependentColor(end), BCOL);
+ result = chooseBorder(endBorder, result);
+ if (!result.exists())
+ return result;
+ }
+ } else {
+ // (7) The table's start border.
+ result = chooseBorder(result, CollapsedBorderValue(&table->style()->borderStart(), table->style()->visitedDependentColor(start), BTABLE));
+ if (!result.exists())
+ return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedEndBorder() const
+{
+ RenderTable* table = this->table();
+ bool isEndColumn = table->colToEffCol(col() + colSpan() - 1) == table->numEffCols() - 1;
+
+ // For end border, we need to check, in order of precedence:
+ // (1) Our end border.
+ int start = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderStartColor, table->style()->direction(), table->style()->writingMode());
+ int end = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderEndColor, table->style()->direction(), table->style()->writingMode());
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderEnd(), style()->visitedDependentColor(end), BCELL);
+
+ // (2) The start border of the following cell.
+ if (!isEndColumn) {
+ RenderTableCell* nextCell = table->cellAfter(this);
+ if (nextCell && nextCell->style()) {
+ CollapsedBorderValue startBorder = CollapsedBorderValue(&nextCell->style()->borderStart(), nextCell->style()->visitedDependentColor(start), BCELL);
+ result = chooseBorder(result, startBorder);
+ if (!result.exists())
+ return result;
+ }
+ } else {
+ // (3) Our row's end border.
+ result = chooseBorder(result, CollapsedBorderValue(&parent()->style()->borderEnd(), parent()->style()->visitedDependentColor(end), BROW));
+ if (!result.exists())
+ return result;
+
+ // (4) Our row group's end border.
+ result = chooseBorder(result, CollapsedBorderValue(&section()->style()->borderEnd(), section()->style()->visitedDependentColor(end), BROWGROUP));
+ if (!result.exists())
+ return result;
+ }
+
+ // (5) Our column and column group's end borders.
+ bool startColEdge;
+ bool endColEdge;
+ RenderTableCol* colElt = table->colElement(col() + colSpan() - 1, &startColEdge, &endColEdge);
+ if (colElt && endColEdge) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->style()->borderEnd(), colElt->style()->visitedDependentColor(end), BCOL));
+ if (!result.exists())
+ return result;
+ if (colElt->parent()->isTableCol() && !colElt->nextSibling()) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->parent()->style()->borderEnd(), colElt->parent()->style()->visitedDependentColor(end), BCOLGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ // (6) The start border of the next column.
+ if (!isEndColumn) {
+ colElt = table->colElement(col() + colSpan(), &startColEdge, &endColEdge);
+ if (colElt && startColEdge) {
+ CollapsedBorderValue startBorder = CollapsedBorderValue(&colElt->style()->borderStart(), colElt->style()->visitedDependentColor(start), BCOL);
+ result = chooseBorder(result, startBorder);
+ if (!result.exists())
+ return result;
+ }
+ } else {
+ // (7) The table's end border.
+ result = chooseBorder(result, CollapsedBorderValue(&table->style()->borderEnd(), table->style()->visitedDependentColor(end), BTABLE));
+ if (!result.exists())
+ return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedBeforeBorder() const
+{
+ RenderTable* table = this->table();
+
+ // For before border, we need to check, in order of precedence:
+ // (1) Our before border.
+ int before = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderBeforeColor, table->style()->direction(), table->style()->writingMode());
+ int after = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderAfterColor, table->style()->direction(), table->style()->writingMode());
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBefore(), style()->visitedDependentColor(before), BCELL);
+
+ RenderTableCell* prevCell = table->cellAbove(this);
+ if (prevCell) {
+ // (2) A before cell's after border.
+ result = chooseBorder(CollapsedBorderValue(&prevCell->style()->borderAfter(), prevCell->style()->visitedDependentColor(after), BCELL), result);
+ if (!result.exists())
+ return result;
+ }
+
+ // (3) Our row's before border.
+ result = chooseBorder(result, CollapsedBorderValue(&parent()->style()->borderBefore(), parent()->style()->visitedDependentColor(before), BROW));
+ if (!result.exists())
+ return result;
+
+ // (4) The previous row's after border.
+ if (prevCell) {
+ RenderObject* prevRow = 0;
+ if (prevCell->section() == section())
+ prevRow = parent()->previousSibling();
+ else
+ prevRow = prevCell->section()->lastChild();
+
+ if (prevRow) {
+ result = chooseBorder(CollapsedBorderValue(&prevRow->style()->borderAfter(), prevRow->style()->visitedDependentColor(after), BROW), result);
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ // Now check row groups.
+ RenderTableSection* currSection = section();
+ if (!row()) {
+ // (5) Our row group's before border.
+ result = chooseBorder(result, CollapsedBorderValue(&currSection->style()->borderBefore(), currSection->style()->visitedDependentColor(before), BROWGROUP));
+ if (!result.exists())
+ return result;
+
+ // (6) Previous row group's after border.
+ currSection = table->sectionAbove(currSection);
+ if (currSection) {
+ result = chooseBorder(CollapsedBorderValue(&currSection->style()->borderAfter(), currSection->style()->visitedDependentColor(after), BROWGROUP), result);
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column and column group's before borders.
+ RenderTableCol* colElt = table->colElement(col());
+ if (colElt) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->style()->borderBefore(), colElt->style()->visitedDependentColor(before), BCOL));
+ if (!result.exists())
+ return result;
+ if (colElt->parent()->isTableCol()) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->parent()->style()->borderBefore(), colElt->parent()->style()->visitedDependentColor(before), BCOLGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ // (9) The table's before border.
+ result = chooseBorder(result, CollapsedBorderValue(&table->style()->borderBefore(), table->style()->visitedDependentColor(before), BTABLE));
+ if (!result.exists())
+ return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedAfterBorder() const
+{
+ RenderTable* table = this->table();
+
+ // For after border, we need to check, in order of precedence:
+ // (1) Our after border.
+ int before = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderBeforeColor, table->style()->direction(), table->style()->writingMode());
+ int after = CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderAfterColor, table->style()->direction(), table->style()->writingMode());
+ CollapsedBorderValue result = CollapsedBorderValue(&style()->borderAfter(), style()->visitedDependentColor(after), BCELL);
+
+ RenderTableCell* nextCell = table->cellBelow(this);
+ if (nextCell) {
+ // (2) An after cell's before border.
+ result = chooseBorder(result, CollapsedBorderValue(&nextCell->style()->borderBefore(), nextCell->style()->visitedDependentColor(before), BCELL));
+ if (!result.exists())
+ return result;
+ }
+
+ // (3) Our row's after border. (FIXME: Deal with rowspan!)
+ result = chooseBorder(result, CollapsedBorderValue(&parent()->style()->borderAfter(), parent()->style()->visitedDependentColor(after), BROW));
+ if (!result.exists())
+ return result;
+
+ // (4) The next row's before border.
+ if (nextCell) {
+ result = chooseBorder(result, CollapsedBorderValue(&nextCell->parent()->style()->borderBefore(), nextCell->parent()->style()->visitedDependentColor(before), BROW));
+ if (!result.exists())
+ return result;
+ }
+
+ // Now check row groups.
+ RenderTableSection* currSection = section();
+ if (row() + rowSpan() >= currSection->numRows()) {
+ // (5) Our row group's after border.
+ result = chooseBorder(result, CollapsedBorderValue(&currSection->style()->borderAfter(), currSection->style()->visitedDependentColor(after), BROWGROUP));
+ if (!result.exists())
+ return result;
+
+ // (6) Following row group's before border.
+ currSection = table->sectionBelow(currSection);
+ if (currSection) {
+ result = chooseBorder(result, CollapsedBorderValue(&currSection->style()->borderBefore(), currSection->style()->visitedDependentColor(before), BROWGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ if (!currSection) {
+ // (8) Our column and column group's after borders.
+ RenderTableCol* colElt = table->colElement(col());
+ if (colElt) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->style()->borderAfter(), colElt->style()->visitedDependentColor(after), BCOL));
+ if (!result.exists()) return result;
+ if (colElt->parent()->isTableCol()) {
+ result = chooseBorder(result, CollapsedBorderValue(&colElt->parent()->style()->borderAfter(), colElt->parent()->style()->visitedDependentColor(after), BCOLGROUP));
+ if (!result.exists())
+ return result;
+ }
+ }
+
+ // (9) The table's after border.
+ result = chooseBorder(result, CollapsedBorderValue(&table->style()->borderAfter(), table->style()->visitedDependentColor(after), BTABLE));
+ if (!result.exists())
+ return result;
+ }
+
+ return result;
+}
+
+CollapsedBorderValue RenderTableCell::collapsedLeftBorder() const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isLeftToRightDirection() ? collapsedStartBorder() : collapsedEndBorder();
+ return tableStyle->isFlippedBlocksWritingMode() ? collapsedAfterBorder() : collapsedBeforeBorder();
+}
+
+CollapsedBorderValue RenderTableCell::collapsedRightBorder() const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isLeftToRightDirection() ? collapsedEndBorder() : collapsedStartBorder();
+ return tableStyle->isFlippedBlocksWritingMode() ? collapsedBeforeBorder() : collapsedAfterBorder();
+}
+
+CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isFlippedBlocksWritingMode() ? collapsedAfterBorder() : collapsedBeforeBorder();
+ return tableStyle->isLeftToRightDirection() ? collapsedStartBorder() : collapsedEndBorder();
+}
+
+CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isFlippedBlocksWritingMode() ? collapsedBeforeBorder() : collapsedAfterBorder();
+ return tableStyle->isLeftToRightDirection() ? collapsedEndBorder() : collapsedStartBorder();
+}
+
+int RenderTableCell::borderLeft() const
+{
+ return table()->collapseBorders() ? borderHalfLeft(false) : RenderBlock::borderLeft();
+}
+
+int RenderTableCell::borderRight() const
+{
+ return table()->collapseBorders() ? borderHalfRight(false) : RenderBlock::borderRight();
+}
+
+int RenderTableCell::borderTop() const
+{
+ return table()->collapseBorders() ? borderHalfTop(false) : RenderBlock::borderTop();
+}
+
+int RenderTableCell::borderBottom() const
+{
+ return table()->collapseBorders() ? borderHalfBottom(false) : RenderBlock::borderBottom();
+}
+
+// FIXME: https://bugs.webkit.org/show_bug.cgi?id=46191, make the collapsed border drawing
+// work with different block flow values instead of being hard-coded to top-to-bottom.
+int RenderTableCell::borderStart() const
+{
+ return table()->collapseBorders() ? borderHalfStart(false) : RenderBlock::borderStart();
+}
+
+int RenderTableCell::borderEnd() const
+{
+ return table()->collapseBorders() ? borderHalfEnd(false) : RenderBlock::borderEnd();
+}
+
+int RenderTableCell::borderBefore() const
+{
+ return table()->collapseBorders() ? borderHalfBefore(false) : RenderBlock::borderBefore();
+}
+
+int RenderTableCell::borderAfter() const
+{
+ return table()->collapseBorders() ? borderHalfAfter(false) : RenderBlock::borderAfter();
+}
+
+int RenderTableCell::borderHalfLeft(bool outer) const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer);
+ return tableStyle->isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer);
+}
+
+int RenderTableCell::borderHalfRight(bool outer) const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer);
+ return tableStyle->isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer);
+}
+
+int RenderTableCell::borderHalfTop(bool outer) const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer);
+ return tableStyle->isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer);
+}
+
+int RenderTableCell::borderHalfBottom(bool outer) const
+{
+ RenderStyle* tableStyle = table()->style();
+ if (tableStyle->isHorizontalWritingMode())
+ return tableStyle->isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer);
+ return tableStyle->isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer);
+}
+
+int RenderTableCell::borderHalfStart(bool outer) const
+{
+ CollapsedBorderValue border = collapsedStartBorder();
+ if (border.exists())
+ return (border.width() + ((table()->style()->isLeftToRightDirection() ^ outer) ? 1 : 0)) / 2; // Give the extra pixel to top and left.
+ return 0;
+}
+
+int RenderTableCell::borderHalfEnd(bool outer) const
+{
+ CollapsedBorderValue border = collapsedEndBorder();
+ if (border.exists())
+ return (border.width() + ((table()->style()->isLeftToRightDirection() ^ outer) ? 0 : 1)) / 2;
+ return 0;
+}
+
+int RenderTableCell::borderHalfBefore(bool outer) const
+{
+ CollapsedBorderValue border = collapsedBeforeBorder();
+ if (border.exists())
+ return (border.width() + ((table()->style()->isFlippedBlocksWritingMode() ^ outer) ? 0 : 1)) / 2; // Give the extra pixel to top and left.
+ return 0;
+}
+
+int RenderTableCell::borderHalfAfter(bool outer) const
+{
+ CollapsedBorderValue border = collapsedAfterBorder();
+ if (border.exists())
+ return (border.width() + ((table()->style()->isFlippedBlocksWritingMode() ^ outer) ? 1 : 0)) / 2;
+ return 0;
+}
+
+void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) {
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return;
+
+ tx += x();
+ ty += y();
+ int os = 2 * maximalOutlineSize(paintInfo.phase);
+ if (ty - table()->outerBorderTop() < paintInfo.rect.bottom() + os &&
+ ty + height() + table()->outerBorderBottom() > paintInfo.rect.y() - os)
+ paintCollapsedBorder(paintInfo.context, tx, ty, width(), height());
+ return;
+ }
+
+ RenderBlock::paint(paintInfo, tx, ty);
+}
+
+static EBorderStyle collapsedBorderStyle(EBorderStyle style)
+{
+ if (style == OUTSET)
+ return GROOVE;
+ if (style == INSET)
+ return RIDGE;
+ return style;
+}
+
+struct CollapsedBorder {
+ CollapsedBorderValue borderValue;
+ BoxSide side;
+ bool shouldPaint;
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+ EBorderStyle style;
+};
+
+class CollapsedBorders {
+public:
+ CollapsedBorders()
+ : m_count(0)
+ {
+ }
+
+ void addBorder(const CollapsedBorderValue& borderValue, BoxSide borderSide, bool shouldPaint,
+ int x1, int y1, int x2, int y2, EBorderStyle borderStyle)
+ {
+ if (borderValue.exists() && shouldPaint) {
+ m_borders[m_count].borderValue = borderValue;
+ m_borders[m_count].side = borderSide;
+ m_borders[m_count].shouldPaint = shouldPaint;
+ m_borders[m_count].x1 = x1;
+ m_borders[m_count].x2 = x2;
+ m_borders[m_count].y1 = y1;
+ m_borders[m_count].y2 = y2;
+ m_borders[m_count].style = borderStyle;
+ m_count++;
+ }
+ }
+
+ CollapsedBorder* nextBorder()
+ {
+ for (int i = 0; i < m_count; i++) {
+ if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) {
+ m_borders[i].shouldPaint = false;
+ return &m_borders[i];
+ }
+ }
+
+ return 0;
+ }
+
+ CollapsedBorder m_borders[4];
+ int m_count;
+};
+
+static void addBorderStyle(RenderTableCell::CollapsedBorderStyles& borderStyles, CollapsedBorderValue borderValue)
+{
+ if (!borderValue.exists())
+ return;
+ size_t count = borderStyles.size();
+ for (size_t i = 0; i < count; ++i)
+ if (borderStyles[i] == borderValue)
+ return;
+ borderStyles.append(borderValue);
+}
+
+void RenderTableCell::collectBorderStyles(CollapsedBorderStyles& borderStyles) const
+{
+ addBorderStyle(borderStyles, collapsedStartBorder());
+ addBorderStyle(borderStyles, collapsedEndBorder());
+ addBorderStyle(borderStyles, collapsedBeforeBorder());
+ addBorderStyle(borderStyles, collapsedAfterBorder());
+}
+
+static int compareBorderStylesForQSort(const void* pa, const void* pb)
+{
+ const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa);
+ const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb);
+ if (*a == *b)
+ return 0;
+ return compareBorders(*a, *b);
+}
+
+void RenderTableCell::sortBorderStyles(CollapsedBorderStyles& borderStyles)
+{
+ qsort(borderStyles.data(), borderStyles.size(), sizeof(CollapsedBorderValue),
+ compareBorderStylesForQSort);
+}
+
+void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h)
+{
+ if (!table()->currentBorderStyle())
+ return;
+
+ CollapsedBorderValue leftVal = collapsedLeftBorder();
+ CollapsedBorderValue rightVal = collapsedRightBorder();
+ CollapsedBorderValue topVal = collapsedTopBorder();
+ CollapsedBorderValue bottomVal = collapsedBottomBorder();
+
+ // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
+ int topWidth = topVal.width();
+ int bottomWidth = bottomVal.width();
+ int leftWidth = leftVal.width();
+ int rightWidth = rightVal.width();
+
+ tx -= leftWidth / 2;
+ ty -= topWidth / 2;
+ w += leftWidth / 2 + (rightWidth + 1) / 2;
+ h += topWidth / 2 + (bottomWidth + 1) / 2;
+
+ EBorderStyle topStyle = collapsedBorderStyle(topVal.style());
+ EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style());
+ EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style());
+ EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style());
+
+ bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent();
+ bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent();
+ bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent();
+ bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent();
+
+ // We never paint diagonals at the joins. We simply let the border with the highest
+ // precedence paint on top of borders with lower precedence.
+ CollapsedBorders borders;
+ borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle);
+ borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle);
+ borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle);
+ borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle);
+
+ for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) {
+ if (border->borderValue == *table()->currentBorderStyle())
+ drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side,
+ border->borderValue.color(), border->style, 0, 0);
+ }
+}
+
+void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject)
+{
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return;
+
+ if (!backgroundObject)
+ return;
+
+ if (style()->visibility() != VISIBLE)
+ return;
+
+ RenderTable* tableElt = table();
+ if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
+ return;
+
+ if (backgroundObject != this) {
+ tx += x();
+ ty += y();
+ }
+
+ int w = width();
+ int h = height();
+
+ Color c = backgroundObject->style()->visitedDependentColor(CSSPropertyBackgroundColor);
+ const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers();
+
+ if (bgLayer->hasImage() || c.isValid()) {
+ // We have to clip here because the background would paint
+ // on top of the borders otherwise. This only matters for cells and rows.
+ bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders();
+ if (shouldClip) {
+ IntRect clipRect(tx + borderLeft(), ty + borderTop(),
+ w - borderLeft() - borderRight(), h - borderTop() - borderBottom());
+ paintInfo.context->save();
+ paintInfo.context->clip(clipRect);
+ }
+ paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h, CompositeSourceOver, backgroundObject);
+ if (shouldClip)
+ paintInfo.context->restore();
+ }
+}
+
+void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (!paintInfo.shouldPaintWithinRoot(this))
+ return;
+
+ RenderTable* tableElt = table();
+ if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
+ return;
+
+ int w = width();
+ int h = height();
+
+ if (style()->boxShadow())
+ paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
+
+ // Paint our cell background.
+ paintBackgroundsBehindCell(paintInfo, tx, ty, this);
+ if (style()->boxShadow())
+ paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
+
+ if (!style()->hasBorder() || tableElt->collapseBorders())
+ return;
+
+ paintBorder(paintInfo.context, tx, ty, w, h, style());
+}
+
+void RenderTableCell::paintMask(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
+ return;
+
+ RenderTable* tableElt = table();
+ if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild())
+ return;
+
+ int w = width();
+ int h = height();
+
+ paintMaskImages(paintInfo, tx, ty, w, h);
+}
+
+void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged)
+{
+ int scrollbarHeight = scrollbarLogicalHeight();
+ if (!scrollbarHeight)
+ return; // Not sure if we should be doing something when a scrollbar goes away or not.
+
+ // We only care if the scrollbar that affects our intrinsic padding has been added.
+ if ((style()->isHorizontalWritingMode() && !horizontalScrollbarChanged) ||
+ (!style()->isHorizontalWritingMode() && !verticalScrollbarChanged))
+ return;
+
+ // Shrink our intrinsic padding as much as possible to accommodate the scrollbar.
+ if (style()->verticalAlign() == MIDDLE) {
+ int totalHeight = logicalHeight();
+ int heightWithoutIntrinsicPadding = totalHeight - intrinsicPaddingBefore() - intrinsicPaddingAfter();
+ totalHeight -= scrollbarHeight;
+ int newBeforePadding = (totalHeight - heightWithoutIntrinsicPadding) / 2;
+ int newAfterPadding = totalHeight - heightWithoutIntrinsicPadding - newBeforePadding;
+ setIntrinsicPaddingBefore(newBeforePadding);
+ setIntrinsicPaddingAfter(newAfterPadding);
+ } else
+ setIntrinsicPaddingAfter(intrinsicPaddingAfter() - scrollbarHeight);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTableCell.h b/Source/WebCore/rendering/RenderTableCell.h
new file mode 100644
index 0000000..5b5992a
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableCell.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 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 RenderTableCell_h
+#define RenderTableCell_h
+
+#include "RenderTableSection.h"
+
+namespace WebCore {
+
+class RenderTableCell : public RenderBlock {
+public:
+ explicit RenderTableCell(Node*);
+
+ // FIXME: need to implement cellIndex
+ int cellIndex() const { return 0; }
+ void setCellIndex(int) { }
+
+ int colSpan() const { return m_columnSpan; }
+ void setColSpan(int c) { m_columnSpan = c; }
+
+ int rowSpan() const { return m_rowSpan; }
+ void setRowSpan(int r) { m_rowSpan = r; }
+
+ int col() const { return m_column; }
+ void setCol(int col) { m_column = col; }
+ int row() const { return m_row; }
+ void setRow(int row) { m_row = row; }
+
+ RenderTableSection* section() const { return toRenderTableSection(parent()->parent()); }
+ RenderTable* table() const { return toRenderTable(parent()->parent()->parent()); }
+
+ Length styleOrColLogicalWidth() const;
+
+ virtual void computePreferredLogicalWidths();
+
+ void updateLogicalWidth(int);
+
+ virtual int borderLeft() const;
+ virtual int borderRight() const;
+ virtual int borderTop() const;
+ virtual int borderBottom() const;
+ virtual int borderStart() const;
+ virtual int borderEnd() const;
+ virtual int borderBefore() const;
+ virtual int borderAfter() const;
+
+ int borderHalfLeft(bool outer) const;
+ int borderHalfRight(bool outer) const;
+ int borderHalfTop(bool outer) const;
+ int borderHalfBottom(bool outer) const;
+
+ int borderHalfStart(bool outer) const;
+ int borderHalfEnd(bool outer) const;
+ int borderHalfBefore(bool outer) const;
+ int borderHalfAfter(bool outer) const;
+
+ CollapsedBorderValue collapsedStartBorder() const;
+ CollapsedBorderValue collapsedEndBorder() const;
+ CollapsedBorderValue collapsedBeforeBorder() const;
+ CollapsedBorderValue collapsedAfterBorder() const;
+
+ CollapsedBorderValue collapsedLeftBorder() const;
+ CollapsedBorderValue collapsedRightBorder() const;
+ CollapsedBorderValue collapsedTopBorder() const;
+ CollapsedBorderValue collapsedBottomBorder() const;
+
+ typedef Vector<CollapsedBorderValue, 100> CollapsedBorderStyles;
+ void collectBorderStyles(CollapsedBorderStyles&) const;
+ static void sortBorderStyles(CollapsedBorderStyles&);
+
+ virtual void updateFromElement();
+
+ virtual void layout();
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ void paintBackgroundsBehindCell(PaintInfo&, int tx, int ty, RenderObject* backgroundObject);
+
+ int baselinePosition() const;
+
+ void setIntrinsicPaddingBefore(int p) { m_intrinsicPaddingBefore = p; }
+ void setIntrinsicPaddingAfter(int p) { m_intrinsicPaddingAfter = p; }
+ void setIntrinsicPadding(int before, int after) { setIntrinsicPaddingBefore(before); setIntrinsicPaddingAfter(after); }
+ void clearIntrinsicPadding() { setIntrinsicPadding(0, 0); }
+
+ int intrinsicPaddingBefore() const { return m_intrinsicPaddingBefore; }
+ int intrinsicPaddingAfter() const { return m_intrinsicPaddingAfter; }
+
+ 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;
+
+ // FIXME: For now we just assume the cell has the same block flow direction as the table. It's likely we'll
+ // create an extra anonymous RenderBlock to handle mixing directionality anyway, in which case we can lock
+ // the block flow directionality of the cells to the table's directionality.
+ virtual int paddingBefore(bool includeIntrinsicPadding = true) const;
+ virtual int paddingAfter(bool includeIntrinsicPadding = true) const;
+
+ virtual void setOverrideSize(int);
+
+ bool hasVisualOverflow() const { return m_overflow && !borderBoxRect().contains(m_overflow->visualOverflowRect()); }
+
+ virtual void scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged);
+
+protected:
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+private:
+ virtual const char* renderName() const { return isAnonymous() ? "RenderTableCell (anonymous)" : "RenderTableCell"; }
+
+ virtual bool isTableCell() const { return true; }
+
+ virtual void destroy();
+
+ virtual bool requiresLayer() const { return isPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); }
+
+ virtual void computeLogicalWidth();
+
+ virtual void paintBoxDecorations(PaintInfo&, int tx, int ty);
+ virtual void paintMask(PaintInfo&, int tx, int ty);
+
+ virtual IntSize offsetFromContainer(RenderObject*, const IntPoint&) const;
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+
+ void paintCollapsedBorder(GraphicsContext*, int x, int y, int w, int h);
+
+ int m_row;
+ int m_column;
+ int m_rowSpan;
+ int m_columnSpan;
+ int m_intrinsicPaddingBefore;
+ int m_intrinsicPaddingAfter;
+};
+
+inline RenderTableCell* toRenderTableCell(RenderObject* object)
+{
+ ASSERT(!object || object->isTableCell());
+ return static_cast<RenderTableCell*>(object);
+}
+
+inline const RenderTableCell* toRenderTableCell(const RenderObject* object)
+{
+ ASSERT(!object || object->isTableCell());
+ return static_cast<const RenderTableCell*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTableCell(const RenderTableCell*);
+
+} // namespace WebCore
+
+#endif // RenderTableCell_h
diff --git a/Source/WebCore/rendering/RenderTableCol.cpp b/Source/WebCore/rendering/RenderTableCol.cpp
new file mode 100644
index 0000000..66d060b
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableCol.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 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 "RenderTableCol.h"
+
+#include "CachedImage.h"
+#include "HTMLNames.h"
+#include "HTMLTableColElement.h"
+#include "RenderTable.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTableCol::RenderTableCol(Node* node)
+ : RenderBox(node)
+ , m_span(1)
+{
+ // init RenderObject attributes
+ setInline(true); // our object is not Inline
+ updateFromElement();
+}
+
+void RenderTableCol::updateFromElement()
+{
+ int oldSpan = m_span;
+ Node* n = node();
+ if (n && (n->hasTagName(colTag) || n->hasTagName(colgroupTag))) {
+ HTMLTableColElement* tc = static_cast<HTMLTableColElement*>(n);
+ m_span = tc->span();
+ } else
+ m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP);
+ if (m_span != oldSpan && style() && parent())
+ setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+bool RenderTableCol::isChildAllowed(RenderObject* child, RenderStyle* style) const
+{
+ return !child->isText() && style && (style->display() == TABLE_COLUMN);
+}
+
+bool RenderTableCol::canHaveChildren() const
+{
+ // Cols cannot have children. This is actually necessary to fix a bug
+ // with libraries.uc.edu, which makes a <p> be a table-column.
+ return style()->display() == TABLE_COLUMN_GROUP;
+}
+
+IntRect RenderTableCol::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ // For now, just repaint the whole table.
+ // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we
+ // might have propagated a background color or borders into.
+ // FIXME: check for repaintContainer each time here?
+
+ RenderTable* parentTable = table();
+ if (!parentTable)
+ return IntRect();
+ return parentTable->clippedOverflowRectForRepaint(repaintContainer);
+}
+
+void RenderTableCol::imageChanged(WrappedImagePtr, const IntRect*)
+{
+ // FIXME: Repaint only the rect the image paints in.
+ repaint();
+}
+
+void RenderTableCol::computePreferredLogicalWidths()
+{
+ setPreferredLogicalWidthsDirty(false);
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling())
+ child->setPreferredLogicalWidthsDirty(false);
+}
+
+RenderTable* RenderTableCol::table() const
+{
+ RenderObject* table = parent();
+ if (table && !table->isTable())
+ table = table->parent();
+ return table && table->isTable() ? toRenderTable(table) : 0;
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderTableCol.h b/Source/WebCore/rendering/RenderTableCol.h
new file mode 100644
index 0000000..8255937
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableCol.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 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 RenderTableCol_h
+#define RenderTableCol_h
+
+#include "RenderBox.h"
+
+namespace WebCore {
+
+class RenderTable;
+
+class RenderTableCol : public RenderBox {
+public:
+ explicit RenderTableCol(Node*);
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ virtual void computePreferredLogicalWidths();
+
+ int span() const { return m_span; }
+ void setSpan(int span) { m_span = span; }
+
+private:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual const char* renderName() const { return "RenderTableCol"; }
+ virtual bool isTableCol() const { return true; }
+ virtual void updateFromElement();
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+ virtual bool canHaveChildren() const;
+ virtual bool requiresLayer() const { return false; }
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+ RenderTable* table() const;
+
+ RenderObjectChildList m_children;
+ int m_span;
+};
+
+inline RenderTableCol* toRenderTableCol(RenderObject* object)
+{
+ ASSERT(!object || object->isTableCol());
+ return static_cast<RenderTableCol*>(object);
+}
+
+inline const RenderTableCol* toRenderTableCol(const RenderObject* object)
+{
+ ASSERT(!object || object->isTableCol());
+ return static_cast<const RenderTableCol*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTableCol(const RenderTableCol*);
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderTableRow.cpp b/Source/WebCore/rendering/RenderTableRow.cpp
new file mode 100644
index 0000000..5bb3ff4
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableRow.cpp
@@ -0,0 +1,217 @@
+/**
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * 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.
+ */
+
+#include "config.h"
+#include "RenderTableRow.h"
+
+#include "CachedImage.h"
+#include "Document.h"
+#include "HTMLNames.h"
+#include "RenderTableCell.h"
+#include "RenderView.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTableRow::RenderTableRow(Node* node)
+ : RenderBox(node)
+{
+ // init RenderObject attributes
+ setInline(false); // our object is not Inline
+}
+
+void RenderTableRow::destroy()
+{
+ RenderTableSection* recalcSection = section();
+
+ RenderBox::destroy();
+
+ if (recalcSection)
+ recalcSection->setNeedsCellRecalc();
+}
+
+void RenderTableRow::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (section() && style() && style()->logicalHeight() != newStyle->logicalHeight())
+ section()->setNeedsCellRecalc();
+
+ ASSERT(newStyle->display() == TABLE_ROW);
+
+ RenderBox::styleWillChange(diff, newStyle);
+}
+
+void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if (!beforeChild && isAfterContent(lastChild()))
+ beforeChild = lastChild();
+
+ if (!child->isTableCell()) {
+ RenderObject* last = beforeChild;
+ if (!last)
+ last = lastChild();
+ if (last && last->isAnonymous() && last->isTableCell()) {
+ if (beforeChild == last)
+ beforeChild = last->firstChild();
+ last->addChild(child, beforeChild);
+ return;
+ }
+
+ // If beforeChild is inside an anonymous cell, insert into the cell.
+ if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous()) {
+ last->parent()->addChild(child, beforeChild);
+ return;
+ }
+
+ RenderTableCell* cell = new (renderArena()) RenderTableCell(document() /* anonymous object */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_CELL);
+ cell->setStyle(newStyle.release());
+ addChild(cell, beforeChild);
+ cell->addChild(child);
+ return;
+ }
+
+ // If the next renderer is actually wrapped in an anonymous table cell, we need to go up and find that.
+ while (beforeChild && beforeChild->parent() != this)
+ beforeChild = beforeChild->parent();
+
+ RenderTableCell* cell = toRenderTableCell(child);
+
+ // Generated content can result in us having a null section so make sure to null check our parent.
+ if (parent())
+ section()->addCell(cell, this);
+
+ ASSERT(!beforeChild || beforeChild->isTableCell());
+ RenderBox::addChild(cell, beforeChild);
+
+ if (beforeChild || nextSibling())
+ section()->setNeedsCellRecalc();
+}
+
+void RenderTableRow::layout()
+{
+ ASSERT(needsLayout());
+
+ // Table rows do not add translation.
+ LayoutStateMaintainer statePusher(view(), this, IntSize(), style()->isFlippedBlocksWritingMode());
+
+ bool paginated = view()->layoutState()->isPaginated();
+
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableCell()) {
+ RenderTableCell* cell = toRenderTableCell(child);
+ if (!cell->needsLayout() && paginated && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell->y()) != cell->pageLogicalOffset())
+ cell->setChildNeedsLayout(true, false);
+
+ if (child->needsLayout()) {
+ cell->computeBlockDirectionMargins(table());
+ cell->layout();
+ }
+ }
+ }
+
+ // We only ever need to repaint if our cells didn't, which menas that they didn't need
+ // layout, so we know that our bounds didn't change. This code is just making up for
+ // the fact that we did not repaint in setStyle() because we had a layout hint.
+ // We cannot call repaint() because our clippedOverflowRectForRepaint() is taken from the
+ // parent table, and being mid-layout, that is invalid. Instead, we repaint our cells.
+ if (selfNeedsLayout() && checkForRepaintDuringLayout()) {
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableCell())
+ child->repaint();
+ }
+ }
+
+ statePusher.pop();
+ // RenderTableSection::layoutRows will set our logical height and width later, so it calls updateLayerTransform().
+ setNeedsLayout(false);
+}
+
+IntRect RenderTableRow::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ ASSERT(parent());
+
+ if (repaintContainer == this)
+ return RenderBox::clippedOverflowRectForRepaint(repaintContainer);
+
+ // For now, just repaint the whole table.
+ // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we
+ // might have propagated a background color into.
+ // FIXME: do repaintContainer checks here
+ if (RenderTable* parentTable = table())
+ return parentTable->clippedOverflowRectForRepaint(repaintContainer);
+
+ return IntRect();
+}
+
+// Hit Testing
+bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action)
+{
+ // Table rows cannot ever be hit tested. Effectively they do not exist.
+ // Just forward to our children always.
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ // FIXME: We have to skip over inline flows, since they can show up inside table rows
+ // at the moment (a demoted inline <form> for example). If we ever implement a
+ // table-specific hit-test method (which we should do for performance reasons anyway),
+ // then we can remove this check.
+ if (child->isTableCell() && !toRenderBox(child)->hasSelfPaintingLayer()) {
+ IntPoint cellPoint = flipForWritingMode(toRenderTableCell(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ if (child->nodeAtPoint(request, result, x, y, cellPoint.x(), cellPoint.y(), action)) {
+ updateHitTestResult(result, IntPoint(x - cellPoint.x(), y - cellPoint.y()));
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void RenderTableRow::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ ASSERT(hasSelfPaintingLayer());
+ if (!layer())
+ return;
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableCell()) {
+ // Paint the row background behind the cell.
+ if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground) {
+ RenderTableCell* cell = toRenderTableCell(child);
+ cell->paintBackgroundsBehindCell(paintInfo, tx, ty, this);
+ }
+ if (!toRenderBox(child)->hasSelfPaintingLayer())
+ child->paint(paintInfo, tx, ty);
+ }
+ }
+}
+
+void RenderTableRow::imageChanged(WrappedImagePtr, const IntRect*)
+{
+ // FIXME: Examine cells and repaint only the rect the image paints in.
+ repaint();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTableRow.h b/Source/WebCore/rendering/RenderTableRow.h
new file mode 100644
index 0000000..20aa424
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableRow.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 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 RenderTableRow_h
+#define RenderTableRow_h
+
+#include "RenderTableSection.h"
+
+namespace WebCore {
+
+class RenderTableRow : public RenderBox {
+public:
+ explicit RenderTableRow(Node*);
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ RenderTableSection* section() const { return toRenderTableSection(parent()); }
+ RenderTable* table() const { return toRenderTable(parent()->parent()); }
+
+private:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual const char* renderName() const { return isAnonymous() ? "RenderTableRow (anonymous)" : "RenderTableRow"; }
+
+ virtual bool isTableRow() const { return true; }
+
+ virtual void destroy();
+
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+ virtual void layout();
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+
+ // The only time rows get a layer is when they have transparency.
+ virtual bool requiresLayer() const { return isTransparent() || hasOverflowClip() || hasTransform() || hasMask(); }
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+
+ RenderObjectChildList m_children;
+};
+
+inline RenderTableRow* toRenderTableRow(RenderObject* object)
+{
+ ASSERT(!object || object->isTableRow());
+ return static_cast<RenderTableRow*>(object);
+}
+
+inline const RenderTableRow* toRenderTableRow(const RenderObject* object)
+{
+ ASSERT(!object || object->isTableRow());
+ return static_cast<const RenderTableRow*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTableRow(const RenderTableRow*);
+
+} // namespace WebCore
+
+#endif // RenderTableRow_h
diff --git a/Source/WebCore/rendering/RenderTableSection.cpp b/Source/WebCore/rendering/RenderTableSection.cpp
new file mode 100644
index 0000000..cb1276d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableSection.cpp
@@ -0,0 +1,1320 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.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.
+ */
+
+#include "config.h"
+#include "RenderTableSection.h"
+#include "CachedImage.h"
+#include "Document.h"
+#include "HitTestResult.h"
+#include "HTMLNames.h"
+#include "RenderTableCell.h"
+#include "RenderTableCol.h"
+#include "RenderTableRow.h"
+#include "RenderView.h"
+#include <limits>
+#include <wtf/HashSet.h>
+#include <wtf/Vector.h>
+#ifdef ANDROID_LAYOUT
+#include "Frame.h"
+#include "Settings.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static inline void setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct* row)
+{
+ ASSERT(row && row->rowRenderer);
+ row->logicalHeight = row->rowRenderer->style()->logicalHeight();
+ if (row->logicalHeight.isRelative())
+ row->logicalHeight = Length();
+}
+
+RenderTableSection::RenderTableSection(Node* node)
+ : RenderBox(node)
+ , m_gridRows(0)
+ , m_cCol(0)
+ , m_cRow(-1)
+ , m_outerBorderStart(0)
+ , m_outerBorderEnd(0)
+ , m_outerBorderBefore(0)
+ , m_outerBorderAfter(0)
+ , m_needsCellRecalc(false)
+ , m_hasOverflowingCell(false)
+ , m_hasMultipleCellLevels(false)
+{
+ // init RenderObject attributes
+ setInline(false); // our object is not Inline
+}
+
+RenderTableSection::~RenderTableSection()
+{
+ clearGrid();
+}
+
+void RenderTableSection::destroy()
+{
+ RenderTable* recalcTable = table();
+
+ RenderBox::destroy();
+
+ // recalc cell info because RenderTable has unguarded pointers
+ // stored that point to this RenderTableSection.
+ if (recalcTable)
+ recalcTable->setNeedsSectionRecalc();
+}
+
+void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+ // Make sure we don't append things after :after-generated content if we have it.
+ if (!beforeChild && isAfterContent(lastChild()))
+ beforeChild = lastChild();
+
+ if (!child->isTableRow()) {
+ RenderObject* last = beforeChild;
+ if (!last)
+ last = lastChild();
+ if (last && last->isAnonymous()) {
+ if (beforeChild == last)
+ beforeChild = last->firstChild();
+ last->addChild(child, beforeChild);
+ return;
+ }
+
+ // If beforeChild is inside an anonymous cell/row, insert into the cell or into
+ // the anonymous row containing it, if there is one.
+ RenderObject* lastBox = last;
+ while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
+ lastBox = lastBox->parent();
+ if (lastBox && lastBox->isAnonymous()) {
+ lastBox->addChild(child, beforeChild);
+ return;
+ }
+
+ RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table row */);
+ RefPtr<RenderStyle> newStyle = RenderStyle::create();
+ newStyle->inheritFrom(style());
+ newStyle->setDisplay(TABLE_ROW);
+ row->setStyle(newStyle.release());
+ addChild(row, beforeChild);
+ row->addChild(child);
+ return;
+ }
+
+ if (beforeChild)
+ setNeedsCellRecalc();
+
+ ++m_cRow;
+ m_cCol = 0;
+
+ // make sure we have enough rows
+ if (!ensureRows(m_cRow + 1))
+ return;
+
+ m_grid[m_cRow].rowRenderer = toRenderTableRow(child);
+
+ if (!beforeChild)
+ setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(&m_grid[m_cRow]);
+
+ // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that.
+ while (beforeChild && beforeChild->parent() != this)
+ beforeChild = beforeChild->parent();
+
+ ASSERT(!beforeChild || beforeChild->isTableRow());
+ RenderBox::addChild(child, beforeChild);
+}
+
+void RenderTableSection::removeChild(RenderObject* oldChild)
+{
+ setNeedsCellRecalc();
+ RenderBox::removeChild(oldChild);
+}
+
+bool RenderTableSection::ensureRows(int numRows)
+{
+ int nRows = m_gridRows;
+ if (numRows > nRows) {
+ if (numRows > static_cast<int>(m_grid.size())) {
+ size_t maxSize = numeric_limits<size_t>::max() / sizeof(RowStruct);
+ if (static_cast<size_t>(numRows) > maxSize)
+ return false;
+ m_grid.grow(numRows);
+ }
+ m_gridRows = numRows;
+ int nCols = max(1, table()->numEffCols());
+ for (int r = nRows; r < numRows; r++) {
+ m_grid[r].row = new Row(nCols);
+ m_grid[r].rowRenderer = 0;
+ m_grid[r].baseline = 0;
+ m_grid[r].logicalHeight = Length();
+ }
+ }
+
+ return true;
+}
+
+void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
+{
+ int rSpan = cell->rowSpan();
+ int cSpan = cell->colSpan();
+ Vector<RenderTable::ColumnStruct>& columns = table()->columns();
+ int nCols = columns.size();
+
+ // ### mozilla still seems to do the old HTML way, even for strict DTD
+ // (see the annotation on table cell layouting in the CSS specs and the testcase below:
+ // <TABLE border>
+ // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
+ // <TR><TD colspan="2">5
+ // </TABLE>
+ while (m_cCol < nCols && (cellAt(m_cRow, m_cCol).hasCells() || cellAt(m_cRow, m_cCol).inColSpan))
+ m_cCol++;
+
+ if (rSpan == 1) {
+ // we ignore height settings on rowspan cells
+ Length logicalHeight = cell->style()->logicalHeight();
+ if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) {
+ Length cRowLogicalHeight = m_grid[m_cRow].logicalHeight;
+ switch (logicalHeight.type()) {
+ case Percent:
+ if (!(cRowLogicalHeight.isPercent()) ||
+ (cRowLogicalHeight.isPercent() && cRowLogicalHeight.rawValue() < logicalHeight.rawValue()))
+ m_grid[m_cRow].logicalHeight = logicalHeight;
+ break;
+ case Fixed:
+ if (cRowLogicalHeight.type() < Percent ||
+ (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
+ m_grid[m_cRow].logicalHeight = logicalHeight;
+ break;
+ case Relative:
+ default:
+ break;
+ }
+ }
+ }
+
+ // make sure we have enough rows
+ if (!ensureRows(m_cRow + rSpan))
+ return;
+
+ m_grid[m_cRow].rowRenderer = row;
+
+ int col = m_cCol;
+ // tell the cell where it is
+ bool inColSpan = false;
+ while (cSpan) {
+ int currentSpan;
+ if (m_cCol >= nCols) {
+ table()->appendColumn(cSpan);
+ currentSpan = cSpan;
+ } else {
+ if (cSpan < (int)columns[m_cCol].span)
+ table()->splitColumn(m_cCol, cSpan);
+ currentSpan = columns[m_cCol].span;
+ }
+ for (int r = 0; r < rSpan; r++) {
+ CellStruct& c = cellAt(m_cRow + r, m_cCol);
+ ASSERT(cell);
+ c.cells.append(cell);
+ // If cells overlap then we take the slow path for painting.
+ if (c.cells.size() > 1)
+ m_hasMultipleCellLevels = true;
+ if (inColSpan)
+ c.inColSpan = true;
+ }
+ m_cCol++;
+ cSpan -= currentSpan;
+ inColSpan = true;
+ }
+ cell->setRow(m_cRow);
+ cell->setCol(table()->effColToCol(col));
+}
+
+void RenderTableSection::setCellLogicalWidths()
+{
+ Vector<int>& columnPos = table()->columnPositions();
+
+ LayoutStateMaintainer statePusher(view());
+
+#ifdef ANDROID_LAYOUT
+ int visibleWidth = 0;
+ if (view()->frameView()) {
+ const Settings* settings = document()->settings();
+ ASSERT(settings);
+ if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen)
+ visibleWidth = view()->frameView()->textWrapWidth();
+ }
+#endif
+
+ for (int i = 0; i < m_gridRows; i++) {
+ Row& row = *m_grid[i].row;
+ int cols = row.size();
+ for (int j = 0; j < cols; j++) {
+ CellStruct& current = row[j];
+ RenderTableCell* cell = current.primaryCell();
+ if (!cell || current.inColSpan)
+ continue;
+ int endCol = j;
+ int cspan = cell->colSpan();
+ while (cspan && endCol < cols) {
+ ASSERT(endCol < (int)table()->columns().size());
+ cspan -= table()->columns()[endCol].span;
+ endCol++;
+ }
+ int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
+#ifdef ANDROID_LAYOUT
+ if (table()->isSingleColumn()) {
+ int b = table()->collapseBorders() ?
+ 0 : table()->paddingLeft() + table()->paddingRight() + 2 * table()->hBorderSpacing();
+ w = table()->width() - (table()->borderLeft() + table()->borderRight() + b);
+ }
+#endif
+ int oldLogicalWidth = cell->logicalWidth();
+#ifdef ANDROID_LAYOUT
+ if (w != oldLogicalWidth || (visibleWidth > 0 && visibleWidth != cell->getVisibleWidth())) {
+#else
+ if (w != oldLogicalWidth) {
+#endif
+ cell->setNeedsLayout(true);
+ if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) {
+ if (!statePusher.didPush()) {
+ // Technically, we should also push state for the row, but since
+ // rows don't push a coordinate transform, that's not necessary.
+ statePusher.push(this, IntSize(x(), y()));
+ }
+ cell->repaint();
+ }
+#ifdef ANDROID_LAYOUT
+ if (w != oldLogicalWidth)
+#endif
+ cell->updateLogicalWidth(w);
+ }
+ }
+ }
+
+ statePusher.pop(); // only pops if we pushed
+}
+
+int RenderTableSection::calcRowLogicalHeight()
+{
+#ifndef NDEBUG
+ setNeedsLayoutIsForbidden(true);
+#endif
+
+ ASSERT(!needsLayout());
+#ifdef ANDROID_LAYOUT
+ if (table()->isSingleColumn()) {
+ int height = 0;
+ int spacing = table()->vBorderSpacing();
+ for (int r = 0; r < m_gridRows; r++)
+ height += m_grid[r].logicalHeight.calcMinValue(0) + (m_grid[r].rowRenderer ? spacing : 0);
+ return height;
+ }
+#endif
+
+ RenderTableCell* cell;
+
+ int spacing = table()->vBorderSpacing();
+
+ LayoutStateMaintainer statePusher(view());
+
+ m_rowPos.resize(m_gridRows + 1);
+ m_rowPos[0] = spacing;
+
+ for (int r = 0; r < m_gridRows; r++) {
+ m_rowPos[r + 1] = 0;
+ m_grid[r].baseline = 0;
+ int baseline = 0;
+ int bdesc = 0;
+ int ch = m_grid[r].logicalHeight.calcMinValue(0);
+ int pos = m_rowPos[r] + ch + (m_grid[r].rowRenderer ? spacing : 0);
+
+ m_rowPos[r + 1] = max(m_rowPos[r + 1], pos);
+
+ Row* row = m_grid[r].row;
+ int totalCols = row->size();
+
+ for (int c = 0; c < totalCols; c++) {
+ CellStruct& current = cellAt(r, c);
+ cell = current.primaryCell();
+
+ if (!cell || current.inColSpan)
+ continue;
+
+ if ((cell->row() + cell->rowSpan() - 1) > r)
+ continue;
+
+ int indx = max(r - cell->rowSpan() + 1, 0);
+
+ if (cell->overrideSize() != -1) {
+ if (!statePusher.didPush()) {
+ // Technically, we should also push state for the row, but since
+ // rows don't push a coordinate transform, that's not necessary.
+ statePusher.push(this, IntSize(x(), y()));
+ }
+ cell->setOverrideSize(-1);
+ cell->setChildNeedsLayout(true, false);
+ cell->layoutIfNeeded();
+ }
+
+ int adjustedPaddingBefore = cell->paddingBefore() - cell->intrinsicPaddingBefore();
+ int adjustedPaddingAfter = cell->paddingAfter() - cell->intrinsicPaddingAfter();
+ int adjustedLogicalHeight = cell->logicalHeight() - (cell->intrinsicPaddingBefore() + cell->intrinsicPaddingAfter());
+
+ // Explicit heights use the border box in quirks mode. In strict mode do the right
+ // thing and actually add in the border and padding.
+ ch = cell->style()->logicalHeight().calcValue(0) +
+ (document()->inQuirksMode() ? 0 : (adjustedPaddingBefore + adjustedPaddingAfter +
+ cell->borderBefore() + cell->borderAfter()));
+ ch = max(ch, adjustedLogicalHeight);
+
+ pos = m_rowPos[indx] + ch + (m_grid[r].rowRenderer ? spacing : 0);
+
+ m_rowPos[r + 1] = max(m_rowPos[r + 1], pos);
+
+ // find out the baseline
+ EVerticalAlign va = cell->style()->verticalAlign();
+ if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) {
+ int b = cell->baselinePosition();
+ if (b > cell->borderBefore() + cell->paddingBefore()) {
+ baseline = max(baseline, b - cell->intrinsicPaddingBefore());
+ bdesc = max(bdesc, m_rowPos[indx] + ch - (b - cell->intrinsicPaddingBefore()));
+ }
+ }
+ }
+
+ // do we have baseline aligned elements?
+ if (baseline) {
+ // increase rowheight if baseline requires
+ m_rowPos[r + 1] = max(m_rowPos[r + 1], baseline + bdesc + (m_grid[r].rowRenderer ? spacing : 0));
+ m_grid[r].baseline = baseline;
+ }
+
+ m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]);
+ }
+
+#ifndef NDEBUG
+ setNeedsLayoutIsForbidden(false);
+#endif
+
+ ASSERT(!needsLayout());
+
+ statePusher.pop();
+
+ return m_rowPos[m_gridRows];
+}
+
+void RenderTableSection::layout()
+{
+ ASSERT(needsLayout());
+
+ LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
+ for (RenderObject* child = children()->firstChild(); child; child = child->nextSibling()) {
+ if (child->isTableRow()) {
+ child->layoutIfNeeded();
+ ASSERT(!child->needsLayout());
+ }
+ }
+ statePusher.pop();
+ setNeedsLayout(false);
+}
+
+int RenderTableSection::layoutRows(int toAdd)
+{
+#ifndef NDEBUG
+ setNeedsLayoutIsForbidden(true);
+#endif
+
+ ASSERT(!needsLayout());
+#ifdef ANDROID_LAYOUT
+ if (table()->isSingleColumn()) {
+ int totalRows = m_gridRows;
+ int hspacing = table()->hBorderSpacing();
+ int vspacing = table()->vBorderSpacing();
+ int rHeight = vspacing;
+
+ int leftOffset = hspacing;
+
+ int nEffCols = table()->numEffCols();
+ for (int r = 0; r < totalRows; r++) {
+ for (int c = 0; c < nEffCols; c++) {
+ CellStruct current = cellAt(r, c);
+ RenderTableCell* cell = current.primaryCell();
+
+ if (!cell || current.inColSpan)
+ continue;
+ if (r > 0 && (primaryCellAt(r-1, c) == cell))
+ continue;
+
+// cell->setCellTopExtra(0);
+// cell->setCellBottomExtra(0);
+
+ int oldCellX = cell->x();
+ int oldCellY = cell->y();
+
+ if (style()->direction() == RTL) {
+ cell->setX(table()->width());
+ cell->setY(rHeight);
+ } else {
+ cell->setX(leftOffset);
+ cell->setY(rHeight);
+ }
+
+ // If the cell 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 cell) anyway.
+ if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) {
+// IntRect cellRect(oldCellX, oldCellY - cell->borderTopExtra() , cell->width(), cell->height());
+ IntRect cellRect(oldCellX, oldCellY, cell->width(), cell->height());
+ cell->repaintDuringLayoutIfMoved(cellRect);
+ }
+ rHeight += cell->height() + vspacing;
+ }
+ }
+
+ setHeight(rHeight);
+ return height();
+ }
+#endif
+
+ int rHeight;
+ int rindx;
+ int totalRows = m_gridRows;
+
+ // Set the width of our section now. The rows will also be this width.
+ setLogicalWidth(table()->contentLogicalWidth());
+ m_overflow.clear();
+ m_hasOverflowingCell = false;
+
+ if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) {
+ int totalHeight = m_rowPos[totalRows] + toAdd;
+
+ int dh = toAdd;
+ int totalPercent = 0;
+ int numAuto = 0;
+ for (int r = 0; r < totalRows; r++) {
+ if (m_grid[r].logicalHeight.isAuto())
+ numAuto++;
+ else if (m_grid[r].logicalHeight.isPercent())
+ totalPercent += m_grid[r].logicalHeight.rawValue();
+ }
+ if (totalPercent) {
+ // try to satisfy percent
+ int add = 0;
+ totalPercent = min(totalPercent, 100 * percentScaleFactor);
+ int rh = m_rowPos[1] - m_rowPos[0];
+ for (int r = 0; r < totalRows; r++) {
+ if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) {
+ int toAdd = min(dh, (totalHeight * m_grid[r].logicalHeight.rawValue() / (100 * percentScaleFactor)) - rh);
+ // If toAdd is negative, then we don't want to shrink the row (this bug
+ // affected Outlook Web Access).
+ toAdd = max(0, toAdd);
+ add += toAdd;
+ dh -= toAdd;
+ totalPercent -= m_grid[r].logicalHeight.rawValue();
+ }
+ if (r < totalRows - 1)
+ rh = m_rowPos[r + 2] - m_rowPos[r + 1];
+ m_rowPos[r + 1] += add;
+ }
+ }
+ if (numAuto) {
+ // distribute over variable cols
+ int add = 0;
+ for (int r = 0; r < totalRows; r++) {
+ if (numAuto > 0 && m_grid[r].logicalHeight.isAuto()) {
+ int toAdd = dh / numAuto;
+ add += toAdd;
+ dh -= toAdd;
+ numAuto--;
+ }
+ m_rowPos[r + 1] += add;
+ }
+ }
+ if (dh > 0 && m_rowPos[totalRows]) {
+ // if some left overs, distribute equally.
+ int tot = m_rowPos[totalRows];
+ int add = 0;
+ int prev = m_rowPos[0];
+ for (int r = 0; r < totalRows; r++) {
+ // weight with the original height
+ add += dh * (m_rowPos[r + 1] - prev) / tot;
+ prev = m_rowPos[r + 1];
+ m_rowPos[r + 1] += add;
+ }
+ }
+ }
+
+ int hspacing = table()->hBorderSpacing();
+ int vspacing = table()->vBorderSpacing();
+ int nEffCols = table()->numEffCols();
+
+ LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
+
+ for (int r = 0; r < totalRows; r++) {
+ // Set the row's x/y position and width/height.
+ if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
+ rowRenderer->setLocation(0, m_rowPos[r]);
+ rowRenderer->setLogicalWidth(logicalWidth());
+ rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
+ rowRenderer->updateLayerTransform();
+ }
+
+ for (int c = 0; c < nEffCols; c++) {
+ CellStruct& cs = cellAt(r, c);
+ RenderTableCell* cell = cs.primaryCell();
+
+ if (!cell || cs.inColSpan)
+ continue;
+
+ rindx = cell->row();
+ rHeight = m_rowPos[rindx + cell->rowSpan()] - m_rowPos[rindx] - vspacing;
+
+ // Force percent height children to lay themselves out again.
+ // This will cause these children to grow to fill the cell.
+ // FIXME: There is still more work to do here to fully match WinIE (should
+ // it become necessary to do so). In quirks mode, WinIE behaves like we
+ // do, but it will clip the cells that spill out of the table section. In
+ // strict mode, Mozilla and WinIE both regrow the table to accommodate the
+ // new height of the cell (thus letting the percentages cause growth one
+ // time only). We may also not be handling row-spanning cells correctly.
+ //
+ // Note also the oddity where replaced elements always flex, and yet blocks/tables do
+ // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to
+ // match the behavior perfectly, but we'll continue to refine it as we discover new
+ // bugs. :)
+ bool cellChildrenFlex = false;
+ bool flexAllChildren = cell->style()->logicalHeight().isFixed()
+ || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight());
+
+ for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
+ if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) {
+ // Tables with no sections do not flex.
+ if (!o->isTable() || toRenderTable(o)->hasSections()) {
+ o->setNeedsLayout(true, false);
+ cellChildrenFlex = true;
+ }
+ }
+ }
+
+ if (HashSet<RenderBox*>* percentHeightDescendants = cell->percentHeightDescendants()) {
+ HashSet<RenderBox*>::iterator end = percentHeightDescendants->end();
+ for (HashSet<RenderBox*>::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
+ RenderBox* box = *it;
+ if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren)
+ continue;
+
+ while (box != cell) {
+ if (box->normalChildNeedsLayout())
+ break;
+ box->setChildNeedsLayout(true, false);
+ box = box->containingBlock();
+ ASSERT(box);
+ if (!box)
+ break;
+ }
+ cellChildrenFlex = true;
+ }
+ }
+
+ if (cellChildrenFlex) {
+ cell->setChildNeedsLayout(true, false);
+ // Alignment within a cell is based off the calculated
+ // height, which becomes irrelevant once the cell has
+ // been resized based off its percentage.
+ cell->setOverrideSize(max(0,
+ rHeight - cell->borderBefore() - cell->paddingBefore() -
+ cell->borderAfter() - cell->paddingAfter()));
+ cell->layoutIfNeeded();
+
+ // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
+ EVerticalAlign va = cell->style()->verticalAlign();
+ if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) {
+ int b = cell->baselinePosition();
+ if (b > cell->borderBefore() + cell->paddingBefore())
+ m_grid[r].baseline = max(m_grid[r].baseline, b);
+ }
+ }
+
+ int oldIntrinsicPaddingBefore = cell->intrinsicPaddingBefore();
+ int oldIntrinsicPaddingAfter = cell->intrinsicPaddingAfter();
+ int logicalHeightWithoutIntrinsicPadding = cell->logicalHeight() - oldIntrinsicPaddingBefore - oldIntrinsicPaddingAfter;
+
+ int intrinsicPaddingBefore = 0;
+ switch (cell->style()->verticalAlign()) {
+ case SUB:
+ case SUPER:
+ case TEXT_TOP:
+ case TEXT_BOTTOM:
+ case BASELINE: {
+ int b = cell->baselinePosition();
+ if (b > cell->borderBefore() + cell->paddingBefore())
+ intrinsicPaddingBefore = getBaseline(r) - (b - oldIntrinsicPaddingBefore);
+ break;
+ }
+ case TOP:
+ break;
+ case MIDDLE:
+ intrinsicPaddingBefore = (rHeight - logicalHeightWithoutIntrinsicPadding) / 2;
+ break;
+ case BOTTOM:
+ intrinsicPaddingBefore = rHeight - logicalHeightWithoutIntrinsicPadding;
+ break;
+ default:
+ break;
+ }
+
+ int intrinsicPaddingAfter = rHeight - logicalHeightWithoutIntrinsicPadding - intrinsicPaddingBefore;
+ cell->setIntrinsicPaddingBefore(intrinsicPaddingBefore);
+ cell->setIntrinsicPaddingAfter(intrinsicPaddingAfter);
+
+ IntRect oldCellRect(cell->x(), cell->y() , cell->width(), cell->height());
+
+ if (!style()->isLeftToRightDirection())
+ cell->setLogicalLocation(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]);
+ else
+ cell->setLogicalLocation(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]);
+ view()->addLayoutDelta(IntSize(oldCellRect.x() - cell->x(), oldCellRect.y() - cell->y()));
+
+ if (intrinsicPaddingBefore != oldIntrinsicPaddingBefore || intrinsicPaddingAfter != oldIntrinsicPaddingAfter)
+ cell->setNeedsLayout(true, false);
+
+ if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell->y()) != cell->pageLogicalOffset())
+ cell->setChildNeedsLayout(true, false);
+
+ cell->layoutIfNeeded();
+
+ // FIXME: Make pagination work with vertical tables.
+ if (style()->isHorizontalWritingMode() && view()->layoutState()->pageLogicalHeight() && cell->height() != rHeight)
+ cell->setHeight(rHeight); // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
+
+ IntSize childOffset(cell->x() - oldCellRect.x(), cell->y() - oldCellRect.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 (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
+ cell->repaintDuringLayoutIfMoved(oldCellRect);
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ setNeedsLayoutIsForbidden(false);
+#endif
+
+ ASSERT(!needsLayout());
+
+ setLogicalHeight(m_rowPos[totalRows]);
+
+ // Now that our height has been determined, add in overflow from cells.
+ for (int r = 0; r < totalRows; r++) {
+ for (int c = 0; c < nEffCols; c++) {
+ CellStruct& cs = cellAt(r, c);
+ RenderTableCell* cell = cs.primaryCell();
+ if (!cell || cs.inColSpan)
+ continue;
+ if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
+ continue;
+ addOverflowFromChild(cell);
+ m_hasOverflowingCell |= cell->hasVisualOverflow();
+ }
+ }
+
+ statePusher.pop();
+ return height();
+}
+
+int RenderTableSection::calcOuterBorderBefore() const
+{
+ int totalCols = table()->numEffCols();
+ if (!m_gridRows || !totalCols)
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& sb = style()->borderBefore();
+ if (sb.style() == BHIDDEN)
+ return -1;
+ if (sb.style() > BHIDDEN)
+ borderWidth = sb.width();
+
+ const BorderValue& rb = firstChild()->style()->borderBefore();
+ if (rb.style() == BHIDDEN)
+ return -1;
+ if (rb.style() > BHIDDEN && rb.width() > borderWidth)
+ borderWidth = rb.width();
+
+ bool allHidden = true;
+ for (int c = 0; c < totalCols; c++) {
+ const CellStruct& current = cellAt(0, c);
+ if (current.inColSpan || !current.hasCells())
+ continue;
+ const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
+ // FIXME: Don't repeat for the same col group
+ RenderTableCol* colGroup = table()->colElement(c);
+ if (colGroup) {
+ const BorderValue& gb = colGroup->style()->borderBefore();
+ if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (gb.style() > BHIDDEN && gb.width() > borderWidth)
+ borderWidth = gb.width();
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ } else {
+ if (cb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ }
+ }
+ if (allHidden)
+ return -1;
+
+ return borderWidth / 2;
+}
+
+int RenderTableSection::calcOuterBorderAfter() const
+{
+ int totalCols = table()->numEffCols();
+ if (!m_gridRows || !totalCols)
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& sb = style()->borderAfter();
+ if (sb.style() == BHIDDEN)
+ return -1;
+ if (sb.style() > BHIDDEN)
+ borderWidth = sb.width();
+
+ const BorderValue& rb = lastChild()->style()->borderAfter();
+ if (rb.style() == BHIDDEN)
+ return -1;
+ if (rb.style() > BHIDDEN && rb.width() > borderWidth)
+ borderWidth = rb.width();
+
+ bool allHidden = true;
+ for (int c = 0; c < totalCols; c++) {
+ const CellStruct& current = cellAt(m_gridRows - 1, c);
+ if (current.inColSpan || !current.hasCells())
+ continue;
+ const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
+ // FIXME: Don't repeat for the same col group
+ RenderTableCol* colGroup = table()->colElement(c);
+ if (colGroup) {
+ const BorderValue& gb = colGroup->style()->borderAfter();
+ if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (gb.style() > BHIDDEN && gb.width() > borderWidth)
+ borderWidth = gb.width();
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ } else {
+ if (cb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ }
+ }
+ if (allHidden)
+ return -1;
+
+ return (borderWidth + 1) / 2;
+}
+
+int RenderTableSection::calcOuterBorderStart() const
+{
+ int totalCols = table()->numEffCols();
+ if (!m_gridRows || !totalCols)
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& sb = style()->borderStart();
+ if (sb.style() == BHIDDEN)
+ return -1;
+ if (sb.style() > BHIDDEN)
+ borderWidth = sb.width();
+
+ if (RenderTableCol* colGroup = table()->colElement(0)) {
+ const BorderValue& gb = colGroup->style()->borderStart();
+ if (gb.style() == BHIDDEN)
+ return -1;
+ if (gb.style() > BHIDDEN && gb.width() > borderWidth)
+ borderWidth = gb.width();
+ }
+
+ bool allHidden = true;
+ for (int r = 0; r < m_gridRows; r++) {
+ const CellStruct& current = cellAt(r, 0);
+ if (!current.hasCells())
+ continue;
+ // FIXME: Don't repeat for the same cell
+ const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
+ const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart();
+ if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ if (rb.style() > BHIDDEN && rb.width() > borderWidth)
+ borderWidth = rb.width();
+ }
+ if (allHidden)
+ return -1;
+
+ return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2;
+}
+
+int RenderTableSection::calcOuterBorderEnd() const
+{
+ int totalCols = table()->numEffCols();
+ if (!m_gridRows || !totalCols)
+ return 0;
+
+ unsigned borderWidth = 0;
+
+ const BorderValue& sb = style()->borderEnd();
+ if (sb.style() == BHIDDEN)
+ return -1;
+ if (sb.style() > BHIDDEN)
+ borderWidth = sb.width();
+
+ if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) {
+ const BorderValue& gb = colGroup->style()->borderEnd();
+ if (gb.style() == BHIDDEN)
+ return -1;
+ if (gb.style() > BHIDDEN && gb.width() > borderWidth)
+ borderWidth = gb.width();
+ }
+
+ bool allHidden = true;
+ for (int r = 0; r < m_gridRows; r++) {
+ const CellStruct& current = cellAt(r, totalCols - 1);
+ if (!current.hasCells())
+ continue;
+ // FIXME: Don't repeat for the same cell
+ const BorderValue& cb = current.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
+ const BorderValue& rb = current.primaryCell()->parent()->style()->borderEnd();
+ if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
+ continue;
+ allHidden = false;
+ if (cb.style() > BHIDDEN && cb.width() > borderWidth)
+ borderWidth = cb.width();
+ if (rb.style() > BHIDDEN && rb.width() > borderWidth)
+ borderWidth = rb.width();
+ }
+ if (allHidden)
+ return -1;
+
+ return (borderWidth + (table()->style()->isLeftToRightDirection() ? 1 : 0)) / 2;
+}
+
+void RenderTableSection::recalcOuterBorder()
+{
+ m_outerBorderBefore = calcOuterBorderBefore();
+ m_outerBorderAfter = calcOuterBorderAfter();
+ m_outerBorderStart = calcOuterBorderStart();
+ m_outerBorderEnd = calcOuterBorderEnd();
+}
+
+int RenderTableSection::firstLineBoxBaseline() const
+{
+ if (!m_gridRows)
+ return -1;
+
+ int firstLineBaseline = m_grid[0].baseline;
+ if (firstLineBaseline)
+ return firstLineBaseline + m_rowPos[0];
+
+ firstLineBaseline = -1;
+ Row* firstRow = m_grid[0].row;
+ for (size_t i = 0; i < firstRow->size(); ++i) {
+ CellStruct& cs = firstRow->at(i);
+ RenderTableCell* cell = cs.primaryCell();
+ if (cell)
+ firstLineBaseline = max(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight());
+ }
+
+ return firstLineBaseline;
+}
+
+void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ // put this back in when all layout tests can handle it
+ // ASSERT(!needsLayout());
+ // avoid crashing on bugs that cause us to paint with dirty layout
+ if (needsLayout())
+ return;
+
+ unsigned totalRows = m_gridRows;
+ unsigned totalCols = table()->columns().size();
+
+ if (!totalRows || !totalCols)
+ return;
+
+ tx += x();
+ ty += y();
+
+ PaintPhase phase = paintInfo.phase;
+ bool pushedClip = pushContentsClip(paintInfo, tx, ty);
+ paintObject(paintInfo, tx, ty);
+ if (pushedClip)
+ popContentsClip(paintInfo, phase, tx, ty);
+}
+
+static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
+{
+ return elem1->row() < elem2->row();
+}
+
+void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, int tx, int ty)
+{
+ IntPoint cellPoint = flipForWritingMode(cell, IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ PaintPhase paintPhase = paintInfo.phase;
+ RenderTableRow* row = toRenderTableRow(cell->parent());
+
+ if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
+ // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
+ // the column group, column, row group, row, and then the cell.
+ RenderObject* col = table()->colElement(cell->col());
+ RenderObject* colGroup = 0;
+ if (col && col->parent()->style()->display() == TABLE_COLUMN_GROUP)
+ colGroup = col->parent();
+
+ // Column groups and columns first.
+ // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
+ // the stack, since we have already opened a transparency layer (potentially) for the table row group.
+ // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
+ // cell.
+ cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), colGroup);
+ cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), col);
+
+ // Paint the row group next.
+ cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), this);
+
+ // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
+ // painting the row background for the cell.
+ if (!row->hasSelfPaintingLayer())
+ cell->paintBackgroundsBehindCell(paintInfo, cellPoint.x(), cellPoint.y(), row);
+ }
+ if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders)
+ cell->paint(paintInfo, cellPoint.x(), cellPoint.y());
+}
+
+void RenderTableSection::paintObject(PaintInfo& paintInfo, int tx, int ty)
+{
+ // Check which rows and cols are visible and only paint these.
+ // FIXME: Could use a binary search here.
+ unsigned totalRows = m_gridRows;
+ unsigned totalCols = table()->columns().size();
+
+ PaintPhase paintPhase = paintInfo.phase;
+
+#ifdef ANDROID_LAYOUT
+ unsigned int startrow = 0;
+ unsigned int endrow = totalRows;
+ unsigned int startcol = 0;
+ unsigned int endcol = totalCols;
+ if (table()->isSingleColumn()) {
+ // FIXME: should we be smarter too?
+ } else {
+ // FIXME: possible to rollback to the common tree.
+ // rowPos size is set in calcRowHeight(), which is called from table layout().
+ // BUT RenderTableSection is init through parsing. On a slow device, paint() as
+ // the result of layout() can come after the next parse() as everything is triggered
+ // by timer. So we have to check rowPos before using it.
+ if (m_rowPos.size() != (totalRows + 1))
+ return;
+#endif
+
+ int os = 2 * maximalOutlineSize(paintPhase);
+ unsigned startrow = 0;
+ unsigned endrow = totalRows;
+
+ IntRect localRepaintRect = paintInfo.rect;
+ localRepaintRect.move(-tx, -ty);
+ if (style()->isFlippedBlocksWritingMode()) {
+ if (style()->isHorizontalWritingMode())
+ localRepaintRect.setY(height() - localRepaintRect.bottom());
+ else
+ localRepaintRect.setX(width() - localRepaintRect.right());
+ }
+
+ // If some cell overflows, just paint all of them.
+ if (!m_hasOverflowingCell) {
+ int before = (style()->isHorizontalWritingMode() ? localRepaintRect.y() : localRepaintRect.x()) - os;
+ // binary search to find a row
+ startrow = std::lower_bound(m_rowPos.begin(), m_rowPos.end(), before) - m_rowPos.begin();
+
+ // The binary search above gives us the first row with
+ // a y position >= the top of the paint rect. Thus, the previous
+ // may need to be repainted as well.
+ if (startrow == m_rowPos.size() || (startrow > 0 && (m_rowPos[startrow] > before)))
+ --startrow;
+
+ int after = (style()->isHorizontalWritingMode() ? localRepaintRect.bottom() : localRepaintRect.right()) + os;
+ endrow = std::lower_bound(m_rowPos.begin(), m_rowPos.end(), after) - m_rowPos.begin();
+ if (endrow == m_rowPos.size())
+ --endrow;
+
+ if (!endrow && m_rowPos[0] - table()->outerBorderBefore() <= after)
+ ++endrow;
+ }
+
+ unsigned startcol = 0;
+ unsigned endcol = totalCols;
+ // FIXME: Implement RTL.
+ if (!m_hasOverflowingCell && style()->isLeftToRightDirection()) {
+ int start = (style()->isHorizontalWritingMode() ? localRepaintRect.x() : localRepaintRect.y()) - os;
+ Vector<int>& columnPos = table()->columnPositions();
+ startcol = std::lower_bound(columnPos.begin(), columnPos.end(), start) - columnPos.begin();
+ if ((startcol == columnPos.size()) || (startcol > 0 && (columnPos[startcol] > start)))
+ --startcol;
+
+ int end = (style()->isHorizontalWritingMode() ? localRepaintRect.right() : localRepaintRect.bottom()) + os;
+ endcol = std::lower_bound(columnPos.begin(), columnPos.end(), end) - columnPos.begin();
+ if (endcol == columnPos.size())
+ --endcol;
+
+ if (!endcol && columnPos[0] - table()->outerBorderStart() <= end)
+ ++endcol;
+ }
+
+#ifdef ANDROID_LAYOUT
+ }
+#endif
+
+ if (startcol < endcol) {
+ if (!m_hasMultipleCellLevels) {
+ // Draw the dirty cells in the order that they appear.
+ for (unsigned r = startrow; r < endrow; r++) {
+ for (unsigned c = startcol; c < endcol; c++) {
+ CellStruct& current = cellAt(r, c);
+ RenderTableCell* cell = current.primaryCell();
+ if (!cell || (r > startrow && primaryCellAt(r - 1, c) == cell) || (c > startcol && primaryCellAt(r, c - 1) == cell))
+ continue;
+ paintCell(cell, paintInfo, tx, ty);
+ }
+ }
+ } else {
+ // Draw the cells in the correct paint order.
+ Vector<RenderTableCell*> cells;
+ HashSet<RenderTableCell*> spanningCells;
+ for (unsigned r = startrow; r < endrow; r++) {
+ for (unsigned c = startcol; c < endcol; c++) {
+ CellStruct& current = cellAt(r, c);
+ if (!current.hasCells())
+ continue;
+ for (unsigned i = 0; i < current.cells.size(); ++i) {
+ if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
+ if (spanningCells.contains(current.cells[i]))
+ continue;
+ spanningCells.add(current.cells[i]);
+ }
+ cells.append(current.cells[i]);
+ }
+ }
+ }
+ // Sort the dirty cells by paint order.
+ std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
+ int size = cells.size();
+ // Paint the cells.
+ for (int i = 0; i < size; ++i)
+ paintCell(cells[i], paintInfo, tx, ty);
+ }
+ }
+}
+
+void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
+{
+ // FIXME: Examine cells and repaint only the rect the image paints in.
+ repaint();
+}
+
+void RenderTableSection::recalcCells()
+{
+ m_cCol = 0;
+ m_cRow = -1;
+ clearGrid();
+ m_gridRows = 0;
+
+ for (RenderObject* row = firstChild(); row; row = row->nextSibling()) {
+ if (row->isTableRow()) {
+ m_cRow++;
+ m_cCol = 0;
+ if (!ensureRows(m_cRow + 1))
+ break;
+
+ RenderTableRow* tableRow = toRenderTableRow(row);
+ m_grid[m_cRow].rowRenderer = tableRow;
+ setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(&m_grid[m_cRow]);
+
+ for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
+ if (cell->isTableCell())
+ addCell(toRenderTableCell(cell), tableRow);
+ }
+ }
+ }
+ m_needsCellRecalc = false;
+ setNeedsLayout(true);
+}
+
+void RenderTableSection::clearGrid()
+{
+ int rows = m_gridRows;
+ while (rows--)
+ delete m_grid[rows].row;
+}
+
+int RenderTableSection::numColumns() const
+{
+ int result = 0;
+
+ for (int r = 0; r < m_gridRows; ++r) {
+ for (int c = result; c < table()->numEffCols(); ++c) {
+ const CellStruct& cell = cellAt(r, c);
+ if (cell.hasCells() || cell.inColSpan)
+ result = c;
+ }
+ }
+
+ return result + 1;
+}
+
+void RenderTableSection::appendColumn(int pos)
+{
+ for (int row = 0; row < m_gridRows; ++row)
+ m_grid[row].row->resize(pos + 1);
+}
+
+void RenderTableSection::splitColumn(int pos, int first)
+{
+ if (m_cCol > pos)
+ m_cCol++;
+ for (int row = 0; row < m_gridRows; ++row) {
+ Row& r = *m_grid[row].row;
+ r.insert(pos + 1, CellStruct());
+ if (r[pos].hasCells()) {
+ r[pos + 1].cells.append(r[pos].cells);
+ RenderTableCell* cell = r[pos].primaryCell();
+ ASSERT(cell);
+ int colleft = cell->colSpan() - r[pos].inColSpan;
+ if (first > colleft)
+ r[pos + 1].inColSpan = 0;
+ else
+ r[pos + 1].inColSpan = first + r[pos].inColSpan;
+ } else {
+ r[pos + 1].inColSpan = 0;
+ }
+ }
+}
+
+// Hit Testing
+bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
+{
+ // If we have no children then we have nothing to do.
+ if (!firstChild())
+ return false;
+
+ // Table sections cannot ever be hit tested. Effectively they do not exist.
+ // Just forward to our children always.
+ tx += x();
+ ty += y();
+
+ if (hasOverflowClip() && !overflowClipRect(tx, ty).intersects(result.rectForPoint(xPos, yPos)))
+ return false;
+
+ if (m_hasOverflowingCell) {
+ for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+ // FIXME: We have to skip over inline flows, since they can show up inside table rows
+ // at the moment (a demoted inline <form> for example). If we ever implement a
+ // table-specific hit-test method (which we should do for performance reasons anyway),
+ // then we can remove this check.
+ if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) {
+ IntPoint childPoint = flipForWritingMode(toRenderBox(child), IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ if (child->nodeAtPoint(request, result, xPos, yPos, childPoint.x(), childPoint.y(), action)) {
+ updateHitTestResult(result, IntPoint(xPos - childPoint.x(), yPos - childPoint.y()));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ IntPoint location = IntPoint(xPos - tx, yPos - ty);
+ if (style()->isFlippedBlocksWritingMode()) {
+ if (style()->isHorizontalWritingMode())
+ location.setY(height() - location.y());
+ else
+ location.setX(width() - location.x());
+ }
+
+ int offsetInColumnDirection = style()->isHorizontalWritingMode() ? location.y() : location.x();
+ // Find the first row that starts after offsetInColumnDirection.
+ unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), offsetInColumnDirection) - m_rowPos.begin();
+ if (nextRow == m_rowPos.size())
+ return false;
+ // Now set hitRow to the index of the hit row, or 0.
+ unsigned hitRow = nextRow > 0 ? nextRow - 1 : 0;
+
+ Vector<int>& columnPos = table()->columnPositions();
+ int offsetInRowDirection = style()->isHorizontalWritingMode() ? location.x() : location.y();
+ if (!style()->isLeftToRightDirection())
+ offsetInRowDirection = columnPos[columnPos.size() - 1] - offsetInRowDirection;
+
+ unsigned nextColumn = std::lower_bound(columnPos.begin(), columnPos.end(), offsetInRowDirection) - columnPos.begin();
+ if (nextColumn == columnPos.size())
+ return false;
+ unsigned hitColumn = nextColumn > 0 ? nextColumn - 1 : 0;
+
+ CellStruct& current = cellAt(hitRow, hitColumn);
+
+ // If the cell is empty, there's nothing to do
+ if (!current.hasCells())
+ return false;
+
+ for (int i = current.cells.size() - 1; i >= 0; --i) {
+ RenderTableCell* cell = current.cells[i];
+ IntPoint cellPoint = flipForWritingMode(cell, IntPoint(tx, ty), ParentToChildFlippingAdjustment);
+ if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, xPos, yPos, cellPoint.x(), cellPoint.y(), action)) {
+ updateHitTestResult(result, IntPoint(xPos - cellPoint.x(), yPos - cellPoint.y()));
+ return true;
+ }
+ }
+ return false;
+
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTableSection.h b/Source/WebCore/rendering/RenderTableSection.h
new file mode 100644
index 0000000..fac6a84
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTableSection.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ * (C) 1997 Torben Weis (weis@kde.org)
+ * (C) 1998 Waldo Bastian (bastian@kde.org)
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 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 RenderTableSection_h
+#define RenderTableSection_h
+
+#include "RenderTable.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class RenderTableCell;
+class RenderTableRow;
+
+class RenderTableSection : public RenderBox {
+public:
+ RenderTableSection(Node*);
+ virtual ~RenderTableSection();
+
+ const RenderObjectChildList* children() const { return &m_children; }
+ RenderObjectChildList* children() { return &m_children; }
+
+ virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0);
+
+ virtual int firstLineBoxBaseline() const;
+
+ void addCell(RenderTableCell*, RenderTableRow* row);
+
+ void setCellLogicalWidths();
+ int calcRowLogicalHeight();
+ int layoutRows(int logicalHeight);
+
+ RenderTable* table() const { return toRenderTable(parent()); }
+
+ struct CellStruct {
+ Vector<RenderTableCell*, 1> cells;
+ bool inColSpan; // true for columns after the first in a colspan
+
+ CellStruct():
+ inColSpan(false) {}
+
+ RenderTableCell* primaryCell()
+ {
+ return hasCells() ? cells[cells.size() - 1] : 0;
+ }
+
+ const RenderTableCell* primaryCell() const
+ {
+ return hasCells() ? cells[cells.size() - 1] : 0;
+ }
+
+ bool hasCells() const { return cells.size() > 0; }
+ };
+
+ typedef Vector<CellStruct> Row;
+
+ struct RowStruct {
+ Row* row;
+ RenderTableRow* rowRenderer;
+ int baseline;
+ Length logicalHeight;
+ };
+
+ CellStruct& cellAt(int row, int col) { return (*m_grid[row].row)[col]; }
+ const CellStruct& cellAt(int row, int col) const { return (*m_grid[row].row)[col]; }
+ RenderTableCell* primaryCellAt(int row, int col)
+ {
+ CellStruct& c = (*m_grid[row].row)[col];
+ return c.primaryCell();
+ }
+
+ void appendColumn(int pos);
+ void splitColumn(int pos, int first);
+
+ int calcOuterBorderBefore() const;
+ int calcOuterBorderAfter() const;
+ int calcOuterBorderStart() const;
+ int calcOuterBorderEnd() const;
+ void recalcOuterBorder();
+
+ int outerBorderBefore() const { return m_outerBorderBefore; }
+ int outerBorderAfter() const { return m_outerBorderAfter; }
+ int outerBorderStart() const { return m_outerBorderStart; }
+ int outerBorderEnd() const { return m_outerBorderEnd; }
+
+ int numRows() const { return m_gridRows; }
+ int numColumns() const;
+ void recalcCells();
+ void recalcCellsIfNeeded()
+ {
+ if (m_needsCellRecalc)
+ recalcCells();
+ }
+
+ bool needsCellRecalc() const { return m_needsCellRecalc; }
+ void setNeedsCellRecalc()
+ {
+ m_needsCellRecalc = true;
+ table()->setNeedsSectionRecalc();
+ }
+
+ int getBaseline(int row) { return m_grid[row].baseline; }
+
+private:
+ virtual RenderObjectChildList* virtualChildren() { return children(); }
+ virtual const RenderObjectChildList* virtualChildren() const { return children(); }
+
+ virtual const char* renderName() const { return isAnonymous() ? "RenderTableSection (anonymous)" : "RenderTableSection"; }
+
+ virtual bool isTableSection() const { return true; }
+
+ virtual void destroy();
+
+ virtual void layout();
+
+ virtual void removeChild(RenderObject* oldChild);
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual void paintCell(RenderTableCell*, PaintInfo&, int tx, int ty);
+ virtual void paintObject(PaintInfo&, int tx, int ty);
+
+ virtual void imageChanged(WrappedImagePtr, const IntRect* = 0);
+
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+
+ bool ensureRows(int);
+ void clearGrid();
+
+ RenderObjectChildList m_children;
+
+ Vector<RowStruct> m_grid;
+ Vector<int> m_rowPos;
+
+ int m_gridRows;
+
+ // the current insertion position
+ int m_cCol;
+ int m_cRow;
+
+ int m_outerBorderStart;
+ int m_outerBorderEnd;
+ int m_outerBorderBefore;
+ int m_outerBorderAfter;
+
+ bool m_needsCellRecalc;
+ bool m_hasOverflowingCell;
+
+ bool m_hasMultipleCellLevels;
+};
+
+inline RenderTableSection* toRenderTableSection(RenderObject* object)
+{
+ ASSERT(!object || object->isTableSection());
+ return static_cast<RenderTableSection*>(object);
+}
+
+inline const RenderTableSection* toRenderTableSection(const RenderObject* object)
+{
+ ASSERT(!object || object->isTableSection());
+ return static_cast<const RenderTableSection*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTableSection(const RenderTableSection*);
+
+} // namespace WebCore
+
+#endif // RenderTableSection_h
diff --git a/Source/WebCore/rendering/RenderText.cpp b/Source/WebCore/rendering/RenderText.cpp
new file mode 100644
index 0000000..78c5684
--- /dev/null
+++ b/Source/WebCore/rendering/RenderText.cpp
@@ -0,0 +1,1539 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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.
+ *
+ */
+
+#include "config.h"
+#include "RenderText.h"
+
+#include "AXObjectCache.h"
+#include "CharacterNames.h"
+#include "EllipsisBox.h"
+#include "FloatQuad.h"
+#include "FontTranscoder.h"
+#include "FrameView.h"
+#include "InlineTextBox.h"
+#include "Range.h"
+#include "RenderArena.h"
+#include "RenderBlock.h"
+#include "RenderLayer.h"
+#include "RenderView.h"
+#include "Text.h"
+#include "TextBreakIterator.h"
+#include "TextResourceDecoder.h"
+#include "VisiblePosition.h"
+#include "break_lines.h"
+#include <wtf/AlwaysInline.h>
+#include <wtf/text/StringBuffer.h>
+
+using namespace std;
+using namespace WTF;
+using namespace Unicode;
+
+namespace WebCore {
+
+static void makeCapitalized(String* string, UChar previous)
+{
+ if (string->isNull())
+ return;
+
+ unsigned length = string->length();
+ const UChar* characters = string->characters();
+
+ if (length >= numeric_limits<unsigned>::max())
+ CRASH();
+
+ StringBuffer stringWithPrevious(length + 1);
+ stringWithPrevious[0] = previous == noBreakSpace ? ' ' : previous;
+ for (unsigned i = 1; i < length + 1; i++) {
+ // Replace &nbsp with a real space since ICU no longer treats &nbsp as a word separator.
+ if (characters[i - 1] == noBreakSpace)
+ stringWithPrevious[i] = ' ';
+ else
+ stringWithPrevious[i] = characters[i - 1];
+ }
+
+ TextBreakIterator* boundary = wordBreakIterator(stringWithPrevious.characters(), length + 1);
+ if (!boundary)
+ return;
+
+ StringBuffer data(length);
+
+ int32_t endOfWord;
+ int32_t startOfWord = textBreakFirst(boundary);
+ for (endOfWord = textBreakNext(boundary); endOfWord != TextBreakDone; startOfWord = endOfWord, endOfWord = textBreakNext(boundary)) {
+ if (startOfWord != 0) // Ignore first char of previous string
+ data[startOfWord - 1] = characters[startOfWord - 1] == noBreakSpace ? noBreakSpace : toTitleCase(stringWithPrevious[startOfWord]);
+ for (int i = startOfWord + 1; i < endOfWord; i++)
+ data[i - 1] = characters[i - 1];
+ }
+
+ *string = String::adopt(data);
+}
+
+RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str)
+ : RenderObject(node)
+ , m_minWidth(-1)
+ , m_text(str)
+ , m_firstTextBox(0)
+ , m_lastTextBox(0)
+ , m_maxWidth(-1)
+ , m_beginMinWidth(0)
+ , m_endMinWidth(0)
+ , m_hasTab(false)
+ , m_linesDirty(false)
+ , m_containsReversedText(false)
+ , m_isAllASCII(m_text.containsOnlyASCII())
+ , m_knownToHaveNoOverflowAndNoFallbackFonts(false)
+ , m_needsTranscoding(false)
+{
+ ASSERT(m_text);
+
+ setIsText();
+
+ // FIXME: It would be better to call this only if !m_text->containsOnlyWhitespace().
+ // But that might slow things down, and maybe should only be done if visuallyNonEmpty
+ // is still false. Not making any change for now, but should consider in the future.
+ view()->frameView()->setIsVisuallyNonEmpty();
+}
+
+#ifndef NDEBUG
+
+RenderText::~RenderText()
+{
+ ASSERT(!m_firstTextBox);
+ ASSERT(!m_lastTextBox);
+}
+
+#endif
+
+const char* RenderText::renderName() const
+{
+ return "RenderText";
+}
+
+bool RenderText::isTextFragment() const
+{
+ return false;
+}
+
+bool RenderText::isWordBreak() const
+{
+ return false;
+}
+
+void RenderText::updateNeedsTranscoding()
+{
+ const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
+ m_needsTranscoding = fontTranscoder().needsTranscoding(style()->font().fontDescription(), encoding);
+}
+
+void RenderText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ // There is no need to ever schedule repaints from a style change of a text run, since
+ // we already did this for the parent of the text run.
+ // We do have to schedule layouts, though, since a style change can force us to
+ // need to relayout.
+ if (diff == StyleDifferenceLayout) {
+ setNeedsLayoutAndPrefWidthsRecalc();
+ m_knownToHaveNoOverflowAndNoFallbackFonts = false;
+ }
+
+ bool needsResetText = false;
+ if (!oldStyle) {
+ updateNeedsTranscoding();
+ needsResetText = m_needsTranscoding;
+ } else if (oldStyle->font().needsTranscoding() != style()->font().needsTranscoding() || (style()->font().needsTranscoding() && oldStyle->font().family().family() != style()->font().family().family())) {
+ updateNeedsTranscoding();
+ needsResetText = true;
+ }
+
+ ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE;
+ ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE;
+ if (needsResetText || oldTransform != style()->textTransform() || oldSecurity != style()->textSecurity()) {
+ if (RefPtr<StringImpl> textToTransform = originalText())
+ setText(textToTransform.release(), true);
+ }
+}
+
+void RenderText::destroy()
+{
+ if (!documentBeingDestroyed()) {
+ if (firstTextBox()) {
+ if (isBR()) {
+ RootInlineBox* next = firstTextBox()->root()->nextRootBox();
+ if (next)
+ next->markDirty();
+ }
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ box->remove();
+ } else if (parent())
+ parent()->dirtyLinesFromChangedChild(this);
+ }
+ deleteTextBoxes();
+ RenderObject::destroy();
+}
+
+void RenderText::extractTextBox(InlineTextBox* box)
+{
+ checkConsistency();
+
+ m_lastTextBox = box->prevTextBox();
+ if (box == m_firstTextBox)
+ m_firstTextBox = 0;
+ if (box->prevTextBox())
+ box->prevTextBox()->setNextTextBox(0);
+ box->setPreviousTextBox(0);
+ for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox())
+ curr->setExtracted();
+
+ checkConsistency();
+}
+
+void RenderText::attachTextBox(InlineTextBox* box)
+{
+ checkConsistency();
+
+ if (m_lastTextBox) {
+ m_lastTextBox->setNextTextBox(box);
+ box->setPreviousTextBox(m_lastTextBox);
+ } else
+ m_firstTextBox = box;
+ InlineTextBox* last = box;
+ for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
+ curr->setExtracted(false);
+ last = curr;
+ }
+ m_lastTextBox = last;
+
+ checkConsistency();
+}
+
+void RenderText::removeTextBox(InlineTextBox* box)
+{
+ checkConsistency();
+
+ if (box == m_firstTextBox)
+ m_firstTextBox = box->nextTextBox();
+ if (box == m_lastTextBox)
+ m_lastTextBox = box->prevTextBox();
+ if (box->nextTextBox())
+ box->nextTextBox()->setPreviousTextBox(box->prevTextBox());
+ if (box->prevTextBox())
+ box->prevTextBox()->setNextTextBox(box->nextTextBox());
+
+ checkConsistency();
+}
+
+void RenderText::deleteTextBoxes()
+{
+ if (firstTextBox()) {
+ RenderArena* arena = renderArena();
+ InlineTextBox* next;
+ for (InlineTextBox* curr = firstTextBox(); curr; curr = next) {
+ next = curr->nextTextBox();
+ curr->destroy(arena);
+ }
+ m_firstTextBox = m_lastTextBox = 0;
+ }
+}
+
+PassRefPtr<StringImpl> RenderText::originalText() const
+{
+ Node* e = node();
+ return (e && e->isTextNode()) ? static_cast<Text*>(e)->dataImpl() : 0;
+}
+
+void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
+{
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ rects.append(IntRect(tx + box->x(), ty + box->y(), box->logicalWidth(), box->logicalHeight()));
+}
+
+void RenderText::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight)
+{
+ // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
+ // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
+ // function to take ints causes various internal mismatches. But selectionRect takes ints, and
+ // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
+ // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
+ ASSERT(end == UINT_MAX || end <= INT_MAX);
+ ASSERT(start <= INT_MAX);
+ start = min(start, static_cast<unsigned>(INT_MAX));
+ end = min(end, static_cast<unsigned>(INT_MAX));
+
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ // Note: box->end() returns the index of the last character, not the index past it
+ if (start <= box->start() && box->end() < end) {
+ IntRect r = IntRect(box->x(), box->y(), box->logicalWidth(), box->logicalHeight());
+ if (useSelectionHeight) {
+ IntRect selectionRect = box->selectionRect(0, 0, start, end);
+ r.setHeight(selectionRect.height());
+ r.setY(selectionRect.y());
+ }
+ FloatPoint origin = localToAbsolute(r.location());
+ r.setX(origin.x());
+ r.setY(origin.y());
+ rects.append(r);
+ } else {
+ unsigned realEnd = min(box->end() + 1, end);
+ IntRect r = box->selectionRect(0, 0, start, realEnd);
+ if (!r.isEmpty()) {
+ if (!useSelectionHeight) {
+ // change the height and y position because selectionRect uses selection-specific values
+ r.setHeight(box->logicalHeight());
+ r.setY(box->y());
+ }
+ FloatPoint origin = localToAbsolute(r.location());
+ localToAbsolute(origin);
+ r.setX(origin.x());
+ r.setY(origin.y());
+ rects.append(r);
+ }
+ }
+ }
+}
+
+static IntRect ellipsisRectForBox(InlineTextBox* box, unsigned startPos, unsigned endPos)
+{
+ if (!box)
+ return IntRect();
+
+ unsigned short truncation = box->truncation();
+ if (truncation == cNoTruncation)
+ return IntRect();
+
+ IntRect rect;
+ if (EllipsisBox* ellipsis = box->root()->ellipsisBox()) {
+ int ellipsisStartPosition = max<int>(startPos - box->start(), 0);
+ int ellipsisEndPosition = min<int>(endPos - box->start(), box->len());
+
+ // 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.
+ if (ellipsisEndPosition >= truncation && ellipsisStartPosition <= truncation)
+ return ellipsis->selectionRect(0, 0);
+ }
+
+ return IntRect();
+}
+
+void RenderText::absoluteQuads(Vector<FloatQuad>& quads, ClippingOption option)
+{
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ IntRect boundaries = box->calculateBoundaries();
+
+ // Shorten the width of this text box if it ends in an ellipsis.
+ IntRect ellipsisRect = (option == ClipToEllipsis) ? ellipsisRectForBox(box, 0, textLength()) : IntRect();
+ if (!ellipsisRect.isEmpty())
+ boundaries.setWidth(ellipsisRect.right() - boundaries.x());
+ quads.append(localToAbsoluteQuad(FloatRect(boundaries)));
+ }
+}
+
+void RenderText::absoluteQuads(Vector<FloatQuad>& quads)
+{
+ absoluteQuads(quads, NoClipping);
+}
+
+void RenderText::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight)
+{
+ // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX
+ // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this
+ // function to take ints causes various internal mismatches. But selectionRect takes ints, and
+ // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but
+ // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX.
+ ASSERT(end == UINT_MAX || end <= INT_MAX);
+ ASSERT(start <= INT_MAX);
+ start = min(start, static_cast<unsigned>(INT_MAX));
+ end = min(end, static_cast<unsigned>(INT_MAX));
+
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ // Note: box->end() returns the index of the last character, not the index past it
+ if (start <= box->start() && box->end() < end) {
+ IntRect r(box->calculateBoundaries());
+ if (useSelectionHeight) {
+ IntRect selectionRect = box->selectionRect(0, 0, start, end);
+ r.setHeight(selectionRect.height());
+ r.setY(selectionRect.y());
+ }
+ quads.append(localToAbsoluteQuad(FloatRect(r)));
+ } else {
+ unsigned realEnd = min(box->end() + 1, end);
+ IntRect r = box->selectionRect(0, 0, start, realEnd);
+ if (r.height()) {
+ if (!useSelectionHeight) {
+ // change the height and y position because selectionRect uses selection-specific values
+ r.setHeight(box->logicalHeight());
+ r.setY(box->y());
+ }
+ quads.append(localToAbsoluteQuad(FloatRect(r)));
+ }
+ }
+ }
+}
+
+InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const
+{
+ // The text runs point to parts of the RenderText's m_text
+ // (they don't include '\n')
+ // Find the text run that includes the character at offset
+ // and return pos, which is the position of the char in the run.
+
+ if (!m_firstTextBox)
+ return 0;
+
+ InlineTextBox* s = m_firstTextBox;
+ int off = s->len();
+ while (offset > off && s->nextTextBox()) {
+ s = s->nextTextBox();
+ off = s->start() + s->len();
+ }
+ // we are now in the correct text run
+ pos = (offset > off ? s->len() : s->len() - (off - offset) );
+ return s;
+}
+
+VisiblePosition RenderText::positionForPoint(const IntPoint& point)
+{
+ if (!firstTextBox() || textLength() == 0)
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ // Get the offset for the position, since this will take rtl text into account.
+ int offset;
+
+ int pointLineDirection = firstTextBox()->isHorizontal() ? point.x() : point.y();
+ int pointBlockDirection = firstTextBox()->isHorizontal() ? point.y() : point.x();
+
+ // FIXME: We should be able to roll these special cases into the general cases in the loop below.
+ if (firstTextBox() && pointBlockDirection < firstTextBox()->root()->selectionBottom() && pointLineDirection < firstTextBox()->logicalLeft()) {
+ // at the y coordinate of the first line or above
+ // and the x coordinate is to the left of the first text box left edge
+ offset = firstTextBox()->offsetForPosition(pointLineDirection);
+ return createVisiblePosition(offset + firstTextBox()->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+ }
+ if (lastTextBox() && pointBlockDirection >= lastTextBox()->root()->selectionTop() && pointLineDirection >= lastTextBox()->logicalRight()) {
+ // at the y coordinate of the last line or below
+ // and the x coordinate is to the right of the last text box right edge
+ offset = lastTextBox()->offsetForPosition(pointLineDirection);
+ return createVisiblePosition(offset + lastTextBox()->start(), VP_UPSTREAM_IF_POSSIBLE);
+ }
+
+ InlineTextBox* lastBoxAbove = 0;
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ RootInlineBox* rootBox = box->root();
+ if (pointBlockDirection >= rootBox->selectionTop()) {
+ int bottom = rootBox->selectionBottom();
+ if (rootBox->nextRootBox())
+ bottom = min(bottom, rootBox->nextRootBox()->lineTop());
+ if (pointBlockDirection < bottom) {
+ offset = box->offsetForPosition(pointLineDirection);
+
+ if (pointLineDirection == box->logicalLeft())
+ // the x coordinate is equal to the left edge of this box
+ // the affinity must be downstream so the position doesn't jump back to the previous line
+ return createVisiblePosition(offset + box->start(), DOWNSTREAM);
+
+ if (pointLineDirection < box->logicalRight())
+ // and the x coordinate is to the left of the right edge of this box
+ // check to see if position goes in this box
+ return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+
+ if (!box->prevOnLine() && pointLineDirection < box->logicalLeft())
+ // box is first on line
+ // and the x coordinate is to the left of the first text box left edge
+ return createVisiblePosition(offset + box->start(), DOWNSTREAM);
+
+ if (!box->nextOnLine())
+ // box is last on line
+ // and the x coordinate is to the right of the last text box right edge
+ // generate VisiblePosition, use UPSTREAM affinity if possible
+ return createVisiblePosition(offset + box->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+ }
+ lastBoxAbove = box;
+ }
+ }
+
+ return createVisiblePosition(lastBoxAbove ? lastBoxAbove->start() + lastBoxAbove->len() : 0, DOWNSTREAM);
+}
+
+IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
+{
+ if (!inlineBox)
+ return IntRect();
+
+ ASSERT(inlineBox->isInlineTextBox());
+ if (!inlineBox->isInlineTextBox())
+ return IntRect();
+
+ InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox);
+
+ int height = box->root()->selectionHeight();
+ int top = box->root()->selectionTop();
+
+ int left = box->positionForOffset(caretOffset);
+
+ // Distribute the caret's width to either side of the offset.
+ int caretWidthLeftOfOffset = caretWidth / 2;
+ left -= caretWidthLeftOfOffset;
+ int caretWidthRightOfOffset = caretWidth - caretWidthLeftOfOffset;
+
+ int rootLeft = box->root()->logicalLeft();
+ int rootRight = rootLeft + box->root()->logicalWidth();
+ // FIXME: should we use the width of the root inline box or the
+ // width of the containing block for this?
+ if (extraWidthToEndOfLine)
+ *extraWidthToEndOfLine = (box->root()->logicalWidth() + rootLeft) - (left + 1);
+
+ RenderBlock* cb = containingBlock();
+ RenderStyle* cbStyle = cb->style();
+ int leftEdge;
+ int rightEdge;
+ if (style()->autoWrap()) {
+ leftEdge = cb->logicalLeft();
+ rightEdge = cb->logicalRight();
+ } else {
+ leftEdge = min(cb->logicalLeft(), rootLeft);
+ rightEdge = max(cb->logicalRight(), rootRight);
+ }
+
+ bool rightAligned = false;
+ switch (cbStyle->textAlign()) {
+ case TAAUTO:
+ case JUSTIFY:
+ rightAligned = !cbStyle->isLeftToRightDirection();
+ break;
+ case RIGHT:
+ case WEBKIT_RIGHT:
+ rightAligned = true;
+ break;
+ case LEFT:
+ case WEBKIT_LEFT:
+ case CENTER:
+ case WEBKIT_CENTER:
+ break;
+ }
+
+ if (rightAligned) {
+ left = max(left, leftEdge);
+ left = min(left, rootRight - caretWidth);
+ } else {
+ left = min(left, rightEdge - caretWidthRightOfOffset);
+ left = max(left, rootLeft);
+ }
+
+ return style()->isHorizontalWritingMode() ? IntRect(left, top, caretWidth, height) : IntRect(top, left, height, caretWidth);
+}
+
+ALWAYS_INLINE int RenderText::widthFromCache(const Font& f, int start, int len, int xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII) {
+ int monospaceCharacterWidth = f.spaceWidth();
+ int tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0;
+ int w = 0;
+ bool isSpace;
+ bool previousCharWasSpace = true; // FIXME: Preserves historical behavior, but seems wrong for start > 0.
+ ASSERT(m_text);
+ StringImpl& text = *m_text.impl();
+ for (int i = start; i < start + len; i++) {
+ char c = text[i];
+ if (c <= ' ') {
+ if (c == ' ' || c == '\n') {
+ w += monospaceCharacterWidth;
+ isSpace = true;
+ } else if (c == '\t') {
+ w += tabWidth ? tabWidth - ((xPos + w) % tabWidth) : monospaceCharacterWidth;
+ isSpace = true;
+ } else
+ isSpace = false;
+ } else {
+ w += monospaceCharacterWidth;
+ isSpace = false;
+ }
+ if (isSpace && !previousCharWasSpace)
+ w += f.wordSpacing();
+ previousCharWasSpace = isSpace;
+ }
+ return w;
+ }
+
+ return f.width(TextRun(text()->characters() + start, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
+}
+
+void RenderText::trimmedPrefWidths(int leadWidth,
+ int& beginMinW, bool& beginWS,
+ int& endMinW, bool& endWS,
+ bool& hasBreakableChar, bool& hasBreak,
+ int& beginMaxW, int& endMaxW,
+ int& minW, int& maxW, bool& stripFrontSpaces)
+{
+ bool collapseWhiteSpace = style()->collapseWhiteSpace();
+ if (!collapseWhiteSpace)
+ stripFrontSpaces = false;
+
+ if (m_hasTab || preferredLogicalWidthsDirty())
+ computePreferredLogicalWidths(leadWidth);
+
+ beginWS = !stripFrontSpaces && m_hasBeginWS;
+ endWS = m_hasEndWS;
+
+ int len = textLength();
+
+ if (!len || (stripFrontSpaces && text()->containsOnlyWhitespace())) {
+ beginMinW = 0;
+ endMinW = 0;
+ beginMaxW = 0;
+ endMaxW = 0;
+ minW = 0;
+ maxW = 0;
+ hasBreak = false;
+ return;
+ }
+
+ minW = m_minWidth;
+ maxW = m_maxWidth;
+
+ beginMinW = m_beginMinWidth;
+ endMinW = m_endMinWidth;
+
+ hasBreakableChar = m_hasBreakableChar;
+ hasBreak = m_hasBreak;
+
+ ASSERT(m_text);
+ StringImpl& text = *m_text.impl();
+ if (text[0] == ' ' || (text[0] == '\n' && !style()->preserveNewline()) || text[0] == '\t') {
+ const Font& f = style()->font(); // FIXME: This ignores first-line.
+ if (stripFrontSpaces) {
+ const UChar space = ' ';
+ int spaceWidth = f.width(TextRun(&space, 1));
+ maxW -= spaceWidth;
+ } else
+ maxW += f.wordSpacing();
+ }
+
+ stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
+
+ if (!style()->autoWrap() || minW > maxW)
+ minW = maxW;
+
+ // Compute our max widths by scanning the string for newlines.
+ if (hasBreak) {
+ const Font& f = style()->font(); // FIXME: This ignores first-line.
+ bool firstLine = true;
+ beginMaxW = maxW;
+ endMaxW = maxW;
+ for (int i = 0; i < len; i++) {
+ int linelen = 0;
+ while (i + linelen < len && text[i + linelen] != '\n')
+ linelen++;
+
+ if (linelen) {
+ endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW, 0, 0);
+ if (firstLine) {
+ firstLine = false;
+ leadWidth = 0;
+ beginMaxW = endMaxW;
+ }
+ i += linelen;
+ } else if (firstLine) {
+ beginMaxW = 0;
+ firstLine = false;
+ leadWidth = 0;
+ }
+
+ if (i == len - 1)
+ // A <pre> run that ends with a newline, as in, e.g.,
+ // <pre>Some text\n\n<span>More text</pre>
+ endMaxW = 0;
+ }
+ }
+}
+
+static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style)
+{
+ return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE);
+}
+
+int RenderText::minPreferredLogicalWidth() const
+{
+ if (preferredLogicalWidthsDirty())
+ const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
+
+ return m_minWidth;
+}
+
+int RenderText::maxPreferredLogicalWidth() const
+{
+ if (preferredLogicalWidthsDirty())
+ const_cast<RenderText*>(this)->computePreferredLogicalWidths(0);
+
+ return m_maxWidth;
+}
+
+void RenderText::computePreferredLogicalWidths(int leadWidth)
+{
+ HashSet<const SimpleFontData*> fallbackFonts;
+ GlyphOverflow glyphOverflow;
+ computePreferredLogicalWidths(leadWidth, fallbackFonts, glyphOverflow);
+ if (fallbackFonts.isEmpty() && !glyphOverflow.left && !glyphOverflow.right && !glyphOverflow.top && !glyphOverflow.bottom)
+ m_knownToHaveNoOverflowAndNoFallbackFonts = true;
+}
+
+void RenderText::computePreferredLogicalWidths(int leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow& glyphOverflow)
+{
+ ASSERT(m_hasTab || preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts);
+
+ m_minWidth = 0;
+ m_beginMinWidth = 0;
+ m_endMinWidth = 0;
+ m_maxWidth = 0;
+
+ if (isBR())
+ return;
+
+ int currMinWidth = 0;
+ int currMaxWidth = 0;
+ m_hasBreakableChar = false;
+ m_hasBreak = false;
+ m_hasTab = false;
+ m_hasBeginWS = false;
+ m_hasEndWS = false;
+
+ const Font& f = style()->font(); // FIXME: This ignores first-line.
+ int wordSpacing = style()->wordSpacing();
+ int len = textLength();
+ const UChar* txt = characters();
+ bool needsWordSpacing = false;
+ bool ignoringSpaces = false;
+ bool isSpace = false;
+ bool firstWord = true;
+ bool firstLine = true;
+ int nextBreakable = -1;
+ int lastWordBoundary = 0;
+
+ int firstGlyphLeftOverflow = -1;
+
+ bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE;
+ bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap();
+
+ for (int i = 0; i < len; i++) {
+ UChar c = txt[i];
+
+ bool previousCharacterIsSpace = isSpace;
+
+ bool isNewline = false;
+ if (c == '\n') {
+ if (style()->preserveNewline()) {
+ m_hasBreak = true;
+ isNewline = true;
+ isSpace = false;
+ } else
+ isSpace = true;
+ } else if (c == '\t') {
+ if (!style()->collapseWhiteSpace()) {
+ m_hasTab = true;
+ isSpace = false;
+ } else
+ isSpace = true;
+ } else
+ isSpace = c == ' ';
+
+ if ((isSpace || isNewline) && !i)
+ m_hasBeginWS = true;
+ if ((isSpace || isNewline) && i == len - 1)
+ m_hasEndWS = true;
+
+ if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
+ ignoringSpaces = true;
+
+ if (ignoringSpaces && !isSpace)
+ ignoringSpaces = false;
+
+ // Ignore spaces and soft hyphens
+ if (ignoringSpaces) {
+ ASSERT(lastWordBoundary == i);
+ lastWordBoundary++;
+ continue;
+ } else if (c == softHyphen) {
+ currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
+ if (firstGlyphLeftOverflow < 0)
+ firstGlyphLeftOverflow = glyphOverflow.left;
+ lastWordBoundary = i + 1;
+ continue;
+ }
+
+ bool hasBreak = breakAll || isBreakable(txt, i, len, nextBreakable, breakNBSP);
+ bool betweenWords = true;
+ int j = i;
+ while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) {
+ j++;
+ if (j == len)
+ break;
+ c = txt[j];
+ if (isBreakable(txt, j, len, nextBreakable, breakNBSP))
+ break;
+ if (breakAll) {
+ betweenWords = false;
+ break;
+ }
+ }
+
+ int wordLen = j - i;
+ if (wordLen) {
+ int w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
+ if (firstGlyphLeftOverflow < 0)
+ firstGlyphLeftOverflow = glyphOverflow.left;
+ currMinWidth += w;
+ if (betweenWords) {
+ if (lastWordBoundary == i)
+ currMaxWidth += w;
+ else
+ currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth, &fallbackFonts, &glyphOverflow);
+ lastWordBoundary = j;
+ }
+
+ bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style());
+ bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
+ if (j < len && style()->autoWrap())
+ m_hasBreakableChar = true;
+
+ // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
+ // last word in the run.
+ if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
+ currMaxWidth += wordSpacing;
+
+ if (firstWord) {
+ firstWord = false;
+ // If the first character in the run is breakable, then we consider ourselves to have a beginning
+ // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
+ // being appended to a previous text run when considering the total minimum width of the containing block.
+ if (hasBreak)
+ m_hasBreakableChar = true;
+ m_beginMinWidth = hasBreak ? 0 : w;
+ }
+ m_endMinWidth = w;
+
+ if (currMinWidth > m_minWidth)
+ m_minWidth = currMinWidth;
+ currMinWidth = 0;
+
+ i += wordLen - 1;
+ } else {
+ // Nowrap can never be broken, so don't bother setting the
+ // breakable character boolean. Pre can only be broken if we encounter a newline.
+ if (style()->autoWrap() || isNewline)
+ m_hasBreakableChar = true;
+
+ if (currMinWidth > m_minWidth)
+ m_minWidth = currMinWidth;
+ currMinWidth = 0;
+
+ if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
+ if (firstLine) {
+ firstLine = false;
+ leadWidth = 0;
+ if (!style()->autoWrap())
+ m_beginMinWidth = currMaxWidth;
+ }
+
+ if (currMaxWidth > m_maxWidth)
+ m_maxWidth = currMaxWidth;
+ currMaxWidth = 0;
+ } else {
+ currMaxWidth += f.width(TextRun(txt + i, 1, allowTabs(), leadWidth + currMaxWidth));
+ glyphOverflow.right = 0;
+ needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1;
+ }
+ ASSERT(lastWordBoundary == i);
+ lastWordBoundary++;
+ }
+ }
+
+ if (firstGlyphLeftOverflow > 0)
+ glyphOverflow.left = firstGlyphLeftOverflow;
+
+ if ((needsWordSpacing && len > 1) || (ignoringSpaces && !firstWord))
+ currMaxWidth += wordSpacing;
+
+ m_minWidth = max(currMinWidth, m_minWidth);
+ m_maxWidth = max(currMaxWidth, m_maxWidth);
+
+ if (!style()->autoWrap())
+ m_minWidth = m_maxWidth;
+
+ if (style()->whiteSpace() == PRE) {
+ if (firstLine)
+ m_beginMinWidth = m_maxWidth;
+ m_endMinWidth = currMaxWidth;
+ }
+
+ setPreferredLogicalWidthsDirty(false);
+}
+
+bool RenderText::isAllCollapsibleWhitespace()
+{
+ int length = textLength();
+ const UChar* text = characters();
+ for (int i = 0; i < length; i++) {
+ if (!style()->isCollapsibleWhiteSpace(text[i]))
+ return false;
+ }
+ return true;
+}
+
+bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
+{
+ ASSERT(m_text);
+ StringImpl& text = *m_text.impl();
+ unsigned currPos;
+ for (currPos = from;
+ currPos < from + len && (text[currPos] == '\n' || text[currPos] == ' ' || text[currPos] == '\t');
+ currPos++) { }
+ return currPos >= (from + len);
+}
+
+IntPoint RenderText::firstRunOrigin() const
+{
+ return IntPoint(firstRunX(), firstRunY());
+}
+
+int RenderText::firstRunX() const
+{
+ return m_firstTextBox ? m_firstTextBox->m_x : 0;
+}
+
+int RenderText::firstRunY() const
+{
+ return m_firstTextBox ? m_firstTextBox->m_y : 0;
+}
+
+void RenderText::setSelectionState(SelectionState state)
+{
+ InlineTextBox* box;
+
+ RenderObject::setSelectionState(state);
+ if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) {
+ int startPos, endPos;
+ selectionStartEnd(startPos, endPos);
+ if (selectionState() == SelectionStart) {
+ endPos = textLength();
+
+ // to handle selection from end of text to end of line
+ if (startPos != 0 && startPos == endPos)
+ startPos = endPos - 1;
+ } else if (selectionState() == SelectionEnd)
+ startPos = 0;
+
+ for (box = firstTextBox(); box; box = box->nextTextBox()) {
+ if (box->isSelected(startPos, endPos)) {
+ RootInlineBox* line = box->root();
+ if (line)
+ line->setHasSelectedChildren(true);
+ }
+ }
+ } else {
+ for (box = firstTextBox(); box; box = box->nextTextBox()) {
+ RootInlineBox* line = box->root();
+ if (line)
+ line->setHasSelectedChildren(state == SelectionInside);
+ }
+ }
+
+ // The returned value can be null in case of an orphaned tree.
+ if (RenderBlock* cb = containingBlock())
+ cb->setSelectionState(state);
+}
+
+void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force)
+{
+ unsigned oldLen = textLength();
+ unsigned newLen = text->length();
+ int delta = newLen - oldLen;
+ unsigned end = len ? offset + len - 1 : offset;
+
+ RootInlineBox* firstRootBox = 0;
+ RootInlineBox* lastRootBox = 0;
+
+ bool dirtiedLines = false;
+
+ // Dirty all text boxes that include characters in between offset and offset+len.
+ for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
+ // Text run is entirely before the affected range.
+ if (curr->end() < offset)
+ continue;
+
+ // Text run is entirely after the affected range.
+ if (curr->start() > end) {
+ curr->offsetRun(delta);
+ RootInlineBox* root = curr->root();
+ if (!firstRootBox) {
+ firstRootBox = root;
+ if (!dirtiedLines) {
+ // The affected area was in between two runs. Go ahead and mark the root box of
+ // the run after the affected area as dirty.
+ firstRootBox->markDirty();
+ dirtiedLines = true;
+ }
+ }
+ lastRootBox = root;
+ } else if (curr->end() >= offset && curr->end() <= end) {
+ // Text run overlaps with the left end of the affected range.
+ curr->dirtyLineBoxes();
+ dirtiedLines = true;
+ } else if (curr->start() <= offset && curr->end() >= end) {
+ // Text run subsumes the affected range.
+ curr->dirtyLineBoxes();
+ dirtiedLines = true;
+ } else if (curr->start() <= end && curr->end() >= end) {
+ // Text run overlaps with right end of the affected range.
+ curr->dirtyLineBoxes();
+ dirtiedLines = true;
+ }
+ }
+
+ // Now we have to walk all of the clean lines and adjust their cached line break information
+ // to reflect our updated offsets.
+ if (lastRootBox)
+ lastRootBox = lastRootBox->nextRootBox();
+ if (firstRootBox) {
+ RootInlineBox* prev = firstRootBox->prevRootBox();
+ if (prev)
+ firstRootBox = prev;
+ } else if (lastTextBox()) {
+ ASSERT(!lastRootBox);
+ firstRootBox = lastTextBox()->root();
+ firstRootBox->markDirty();
+ dirtiedLines = true;
+ }
+ for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
+ if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
+ curr->setLineBreakPos(curr->lineBreakPos() + delta);
+ }
+
+ // If the text node is empty, dirty the line where new text will be inserted.
+ if (!firstTextBox() && parent()) {
+ parent()->dirtyLinesFromChangedChild(this);
+ dirtiedLines = true;
+ }
+
+ m_linesDirty = dirtiedLines;
+ setText(text, force);
+}
+
+static inline bool isInlineFlowOrEmptyText(const RenderObject* o)
+{
+ if (o->isRenderInline())
+ return true;
+ if (!o->isText())
+ return false;
+ StringImpl* text = toRenderText(o)->text();
+ if (!text)
+ return true;
+ return !text->length();
+}
+
+UChar RenderText::previousCharacter() const
+{
+ // find previous text renderer if one exists
+ const RenderObject* previousText = this;
+ while ((previousText = previousText->previousInPreOrder()))
+ if (!isInlineFlowOrEmptyText(previousText))
+ break;
+ UChar prev = ' ';
+ if (previousText && previousText->isText())
+ if (StringImpl* previousString = toRenderText(previousText)->text())
+ prev = (*previousString)[previousString->length() - 1];
+ return prev;
+}
+
+void RenderText::transformText(String& text) const
+{
+ ASSERT(style());
+ switch (style()->textTransform()) {
+ case TTNONE:
+ break;
+ case CAPITALIZE:
+ makeCapitalized(&text, previousCharacter());
+ break;
+ case UPPERCASE:
+ text.makeUpper();
+ break;
+ case LOWERCASE:
+ text.makeLower();
+ break;
+ }
+}
+
+void RenderText::setTextInternal(PassRefPtr<StringImpl> text)
+{
+ ASSERT(text);
+ m_text = text;
+ if (m_needsTranscoding) {
+ const TextEncoding* encoding = document()->decoder() ? &document()->decoder()->encoding() : 0;
+ fontTranscoder().convert(m_text, style()->font().fontDescription(), encoding);
+ }
+ ASSERT(m_text);
+
+ if (style()) {
+ transformText(m_text);
+
+ // We use the same characters here as for list markers.
+ // See the listMarkerText function in RenderListMarker.cpp.
+ switch (style()->textSecurity()) {
+ case TSNONE:
+ break;
+ case TSCIRCLE:
+ m_text.makeSecure(whiteBullet);
+ break;
+ case TSDISC:
+ m_text.makeSecure(bullet);
+ break;
+ case TSSQUARE:
+ m_text.makeSecure(blackSquare);
+ }
+ }
+
+ ASSERT(m_text);
+ ASSERT(!isBR() || (textLength() == 1 && m_text[0] == '\n'));
+
+ m_isAllASCII = m_text.containsOnlyASCII();
+}
+
+void RenderText::setText(PassRefPtr<StringImpl> text, bool force)
+{
+ ASSERT(text);
+
+ if (!force && equal(m_text.impl(), text.get()))
+ return;
+
+ setTextInternal(text);
+ setNeedsLayoutAndPrefWidthsRecalc();
+ m_knownToHaveNoOverflowAndNoFallbackFonts = false;
+
+ AXObjectCache* axObjectCache = document()->axObjectCache();
+ if (axObjectCache->accessibilityEnabled())
+ axObjectCache->contentChanged(this);
+}
+
+String RenderText::textWithoutTranscoding() const
+{
+ // If m_text isn't transcoded or is secure, we can just return the modified text.
+ if (!m_needsTranscoding || style()->textSecurity() != TSNONE)
+ return text();
+
+ // Otherwise, we should use original text. If text-transform is
+ // specified, we should transform the text on the fly.
+ String text = originalText();
+ if (style())
+ transformText(text);
+ return text;
+}
+
+void RenderText::dirtyLineBoxes(bool fullLayout)
+{
+ if (fullLayout)
+ deleteTextBoxes();
+ else if (!m_linesDirty) {
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ box->dirtyLineBoxes();
+ }
+ m_linesDirty = false;
+}
+#ifdef ANDROID_DISABLE_ROUNDING_HACKS
+static bool disableRoundingHacks(RenderText* renderText)
+{
+ RenderObject* renderer = renderText;
+ while (renderer) {
+ if (renderer->isTextControl()) {
+ return true;
+ }
+ renderer = renderer->parent();
+ }
+ return false;
+}
+#endif
+
+InlineTextBox* RenderText::createTextBox()
+{
+#ifdef ANDROID_DISABLE_ROUNDING_HACKS
+ return new (renderArena()) InlineTextBox(this, disableRoundingHacks(this));
+#else
+ return new (renderArena()) InlineTextBox(this);
+#endif
+}
+
+InlineTextBox* RenderText::createInlineTextBox()
+{
+ InlineTextBox* textBox = createTextBox();
+ if (!m_firstTextBox)
+ m_firstTextBox = m_lastTextBox = textBox;
+ else {
+ m_lastTextBox->setNextTextBox(textBox);
+ textBox->setPreviousTextBox(m_lastTextBox);
+ m_lastTextBox = textBox;
+ }
+ textBox->setIsText(true);
+ return textBox;
+}
+
+void RenderText::positionLineBox(InlineBox* box)
+{
+ InlineTextBox* s = static_cast<InlineTextBox*>(box);
+
+ // FIXME: should not be needed!!!
+ if (!s->len()) {
+ // We want the box to be destroyed.
+ s->remove();
+ if (m_firstTextBox == s)
+ m_firstTextBox = s->nextTextBox();
+ else
+ s->prevTextBox()->setNextTextBox(s->nextTextBox());
+ if (m_lastTextBox == s)
+ m_lastTextBox = s->prevTextBox();
+ else
+ s->nextTextBox()->setPreviousTextBox(s->prevTextBox());
+ s->destroy(renderArena());
+ return;
+ }
+
+ m_containsReversedText |= !s->isLeftToRightDirection();
+}
+
+unsigned RenderText::width(unsigned from, unsigned len, int xPos, bool firstLine, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ if (from >= textLength())
+ return 0;
+
+ if (from + len > textLength())
+ len = textLength() - from;
+
+ return width(from, len, style(firstLine)->font(), xPos, fallbackFonts, glyphOverflow);
+}
+
+unsigned RenderText::width(unsigned from, unsigned len, const Font& f, int xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const
+{
+ ASSERT(from + len <= textLength());
+ if (!characters())
+ return 0;
+
+ int w;
+ if (&f == &style()->font()) {
+ if (!style()->preserveNewline() && !from && len == textLength()) {
+ if (fallbackFonts) {
+ ASSERT(glyphOverflow);
+ if (preferredLogicalWidthsDirty() || !m_knownToHaveNoOverflowAndNoFallbackFonts) {
+ const_cast<RenderText*>(this)->computePreferredLogicalWidths(0, *fallbackFonts, *glyphOverflow);
+ if (fallbackFonts->isEmpty() && !glyphOverflow->left && !glyphOverflow->right && !glyphOverflow->top && !glyphOverflow->bottom)
+ m_knownToHaveNoOverflowAndNoFallbackFonts = true;
+ }
+ w = m_maxWidth;
+ } else
+ w = maxPreferredLogicalWidth();
+ } else
+ w = widthFromCache(f, from, len, xPos, fallbackFonts, glyphOverflow);
+ } else
+ w = f.width(TextRun(text()->characters() + from, len, allowTabs(), xPos), fallbackFonts, glyphOverflow);
+
+ return w;
+}
+
+IntRect RenderText::linesBoundingBox() const
+{
+ IntRect result;
+
+ ASSERT(!firstTextBox() == !lastTextBox()); // Either both are null or both exist.
+ if (firstTextBox() && lastTextBox()) {
+ // Return the width of the minimal left side and the maximal right side.
+ int logicalLeftSide = 0;
+ int logicalRightSide = 0;
+ for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
+ if (curr == firstTextBox() || curr->logicalLeft() < logicalLeftSide)
+ logicalLeftSide = curr->logicalLeft();
+ if (curr == firstTextBox() || curr->logicalRight() > logicalRightSide)
+ logicalRightSide = curr->logicalRight();
+ }
+
+ bool isHorizontal = style()->isHorizontalWritingMode();
+
+ int x = isHorizontal ? logicalLeftSide : firstTextBox()->x();
+ int y = isHorizontal ? firstTextBox()->y() : logicalLeftSide;
+ int width = isHorizontal ? logicalRightSide - logicalLeftSide : lastTextBox()->logicalBottom() - x;
+ int height = isHorizontal ? lastTextBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
+ result = IntRect(x, y, width, height);
+ }
+
+ return result;
+}
+
+IntRect RenderText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ RenderObject* cb = containingBlock();
+ // The containing block may be an ancestor of repaintContainer, but we need to do a repaintContainer-relative repaint.
+ if (repaintContainer && repaintContainer != cb) {
+ if (!cb->isDescendantOf(repaintContainer))
+ return repaintContainer->clippedOverflowRectForRepaint(repaintContainer);
+ }
+ return cb->clippedOverflowRectForRepaint(repaintContainer);
+}
+
+IntRect RenderText::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
+{
+ ASSERT(!needsLayout());
+
+ if (selectionState() == SelectionNone)
+ return IntRect();
+ RenderBlock* cb = containingBlock();
+ if (!cb)
+ return IntRect();
+
+ // Now calculate startPos and endPos for painting selection.
+ // We include a selection while endPos > 0
+ int startPos, endPos;
+ if (selectionState() == SelectionInside) {
+ // We are fully selected.
+ startPos = 0;
+ endPos = textLength();
+ } else {
+ selectionStartEnd(startPos, endPos);
+ if (selectionState() == SelectionStart)
+ endPos = textLength();
+ else if (selectionState() == SelectionEnd)
+ startPos = 0;
+ }
+
+ if (startPos == endPos)
+ return IntRect();
+
+ IntRect rect;
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ rect.unite(box->selectionRect(0, 0, startPos, endPos));
+ rect.unite(ellipsisRectForBox(box, startPos, endPos));
+ }
+
+ if (clipToVisibleContent)
+ computeRectForRepaint(repaintContainer, rect);
+ else {
+ if (cb->hasColumns())
+ cb->adjustRectForColumns(rect);
+
+ rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
+ }
+
+ return rect;
+}
+
+int RenderText::caretMinOffset() const
+{
+ InlineTextBox* box = firstTextBox();
+ if (!box)
+ return 0;
+ int minOffset = box->start();
+ for (box = box->nextTextBox(); box; box = box->nextTextBox())
+ minOffset = min<int>(minOffset, box->start());
+ return minOffset;
+}
+
+int RenderText::caretMaxOffset() const
+{
+ InlineTextBox* box = lastTextBox();
+ if (!box)
+ return textLength();
+ int maxOffset = box->start() + box->len();
+ for (box = box->prevTextBox(); box; box = box->prevTextBox())
+ maxOffset = max<int>(maxOffset, box->start() + box->len());
+ return maxOffset;
+}
+
+unsigned RenderText::caretMaxRenderedOffset() const
+{
+ int l = 0;
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ l += box->len();
+ return l;
+}
+
+int RenderText::previousOffset(int current) const
+{
+ StringImpl* si = m_text.impl();
+ TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
+ if (!iterator)
+ return current - 1;
+
+ long result = textBreakPreceding(iterator, current);
+ if (result == TextBreakDone)
+ result = current - 1;
+
+#ifdef BUILDING_ON_TIGER
+ // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
+ if (static_cast<unsigned>(result) < si->length()) {
+ UChar character = (*si)[result];
+ if (character == 0xFF9E || character == 0xFF9F)
+ --result;
+ }
+#endif
+
+ return result;
+}
+
+#define HANGUL_CHOSEONG_START (0x1100)
+#define HANGUL_CHOSEONG_END (0x115F)
+#define HANGUL_JUNGSEONG_START (0x1160)
+#define HANGUL_JUNGSEONG_END (0x11A2)
+#define HANGUL_JONGSEONG_START (0x11A8)
+#define HANGUL_JONGSEONG_END (0x11F9)
+#define HANGUL_SYLLABLE_START (0xAC00)
+#define HANGUL_SYLLABLE_END (0xD7AF)
+#define HANGUL_JONGSEONG_COUNT (28)
+
+enum HangulState {
+ HangulStateL,
+ HangulStateV,
+ HangulStateT,
+ HangulStateLV,
+ HangulStateLVT,
+ HangulStateBreak
+};
+
+inline bool isHangulLVT(UChar32 character)
+{
+ return (character - HANGUL_SYLLABLE_START) % HANGUL_JONGSEONG_COUNT;
+}
+
+int RenderText::previousOffsetForBackwardDeletion(int current) const
+{
+#if PLATFORM(MAC)
+ ASSERT(m_text);
+ StringImpl& text = *m_text.impl();
+ UChar32 character;
+ while (current > 0) {
+ if (U16_IS_TRAIL(text[--current]))
+ --current;
+ if (current < 0)
+ break;
+
+ UChar32 character = text.characterStartingAt(current);
+
+ // We don't combine characters in Armenian ... Limbu range for backward deletion.
+ if ((character >= 0x0530) && (character < 0x1950))
+ break;
+
+ if (u_isbase(character) && (character != 0xFF9E) && (character != 0xFF9F))
+ break;
+ }
+
+ if (current <= 0)
+ return current;
+
+ // Hangul
+ character = text.characterStartingAt(current);
+ if (((character >= HANGUL_CHOSEONG_START) && (character <= HANGUL_JONGSEONG_END)) || ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))) {
+ HangulState state;
+ HangulState initialState;
+
+ if (character < HANGUL_JUNGSEONG_START)
+ state = HangulStateL;
+ else if (character < HANGUL_JONGSEONG_START)
+ state = HangulStateV;
+ else if (character < HANGUL_SYLLABLE_START)
+ state = HangulStateT;
+ else
+ state = isHangulLVT(character) ? HangulStateLVT : HangulStateLV;
+
+ initialState = state;
+
+ while (current > 0 && ((character = text.characterStartingAt(current - 1)) >= HANGUL_CHOSEONG_START) && (character <= HANGUL_SYLLABLE_END) && ((character <= HANGUL_JONGSEONG_END) || (character >= HANGUL_SYLLABLE_START))) {
+ switch (state) {
+ case HangulStateV:
+ if (character <= HANGUL_CHOSEONG_END)
+ state = HangulStateL;
+ else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END) && !isHangulLVT(character))
+ state = HangulStateLV;
+ else if (character > HANGUL_JUNGSEONG_END)
+ state = HangulStateBreak;
+ break;
+ case HangulStateT:
+ if ((character >= HANGUL_JUNGSEONG_START) && (character <= HANGUL_JUNGSEONG_END))
+ state = HangulStateV;
+ else if ((character >= HANGUL_SYLLABLE_START) && (character <= HANGUL_SYLLABLE_END))
+ state = (isHangulLVT(character) ? HangulStateLVT : HangulStateLV);
+ else if (character < HANGUL_JUNGSEONG_START)
+ state = HangulStateBreak;
+ break;
+ default:
+ state = (character < HANGUL_JUNGSEONG_START) ? HangulStateL : HangulStateBreak;
+ break;
+ }
+ if (state == HangulStateBreak)
+ break;
+
+ --current;
+ }
+ }
+
+ return current;
+#else
+ // Platforms other than Mac delete by one code point.
+ return current - 1;
+#endif
+}
+
+int RenderText::nextOffset(int current) const
+{
+ StringImpl* si = m_text.impl();
+ TextBreakIterator* iterator = cursorMovementIterator(si->characters(), si->length());
+ if (!iterator)
+ return current + 1;
+
+ long result = textBreakFollowing(iterator, current);
+ if (result == TextBreakDone)
+ result = current + 1;
+
+#ifdef BUILDING_ON_TIGER
+ // ICU 3.2 allows character breaks before a half-width Katakana voiced mark.
+ if (static_cast<unsigned>(result) < si->length()) {
+ UChar character = (*si)[result];
+ if (character == 0xFF9E || character == 0xFF9F)
+ ++result;
+ }
+#endif
+
+ return result;
+}
+
+#ifndef NDEBUG
+
+void RenderText::checkConsistency() const
+{
+#ifdef CHECK_CONSISTENCY
+ const InlineTextBox* prev = 0;
+ for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) {
+ ASSERT(child->renderer() == this);
+ ASSERT(child->prevTextBox() == prev);
+ prev = child;
+ }
+ ASSERT(prev == m_lastTextBox);
+#endif
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderText.h b/Source/WebCore/rendering/RenderText.h
new file mode 100644
index 0000000..964a1d3
--- /dev/null
+++ b/Source/WebCore/rendering/RenderText.h
@@ -0,0 +1,206 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 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 RenderText_h
+#define RenderText_h
+
+#include "RenderObject.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class InlineTextBox;
+
+class RenderText : public RenderObject {
+public:
+ RenderText(Node*, PassRefPtr<StringImpl>);
+#ifndef NDEBUG
+ virtual ~RenderText();
+#endif
+
+ virtual const char* renderName() const;
+
+ virtual bool isTextFragment() const;
+ virtual bool isWordBreak() const;
+
+ virtual PassRefPtr<StringImpl> originalText() const;
+
+ void extractTextBox(InlineTextBox*);
+ void attachTextBox(InlineTextBox*);
+ void removeTextBox(InlineTextBox*);
+
+ virtual void destroy();
+
+ StringImpl* text() const { return m_text.impl(); }
+ String textWithoutTranscoding() const;
+
+ InlineTextBox* createInlineTextBox();
+ void dirtyLineBoxes(bool fullLayout);
+
+ virtual void absoluteRects(Vector<IntRect>&, int tx, int ty);
+ void absoluteRectsForRange(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false);
+
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+ void absoluteQuadsForRange(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false);
+
+ enum ClippingOption { NoClipping, ClipToEllipsis };
+ void absoluteQuads(Vector<FloatQuad>&, ClippingOption option = NoClipping);
+
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+
+ const UChar* characters() const { return m_text.characters(); }
+ unsigned textLength() const { return m_text.length(); } // non virtual implementation of length()
+ void positionLineBox(InlineBox*);
+
+ virtual unsigned width(unsigned from, unsigned len, const Font&, int xPos, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
+ virtual unsigned width(unsigned from, unsigned len, int xPos, bool firstLine = false, HashSet<const SimpleFontData*>* fallbackFonts = 0, GlyphOverflow* = 0) const;
+
+ virtual int minPreferredLogicalWidth() const;
+ virtual int maxPreferredLogicalWidth() const;
+
+ void trimmedPrefWidths(int leadWidth,
+ int& beginMinW, bool& beginWS,
+ int& endMinW, bool& endWS,
+ bool& hasBreakableChar, bool& hasBreak,
+ int& beginMaxW, int& endMaxW,
+ int& minW, int& maxW, bool& stripFrontSpaces);
+
+ virtual IntRect linesBoundingBox() const;
+
+ IntPoint firstRunOrigin() const;
+ int firstRunX() const;
+ int firstRunY() const;
+
+ void setText(PassRefPtr<StringImpl>, bool force = false);
+ void setTextWithOffset(PassRefPtr<StringImpl>, unsigned offset, unsigned len, bool force = false);
+
+ virtual bool canBeSelectionLeaf() const { return true; }
+ virtual void setSelectionState(SelectionState s);
+ virtual IntRect selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent = true);
+ virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
+
+ virtual int marginLeft() const { return style()->marginLeft().calcMinValue(0); }
+ virtual int marginRight() const { return style()->marginRight().calcMinValue(0); }
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+
+ InlineTextBox* firstTextBox() const { return m_firstTextBox; }
+ InlineTextBox* lastTextBox() const { return m_lastTextBox; }
+
+ virtual int caretMinOffset() const;
+ virtual int caretMaxOffset() const;
+ virtual unsigned caretMaxRenderedOffset() const;
+
+ virtual int previousOffset(int current) const;
+ virtual int previousOffsetForBackwardDeletion(int current) const;
+ virtual int nextOffset(int current) const;
+
+ bool containsReversedText() const { return m_containsReversedText; }
+
+ InlineTextBox* findNextInlineTextBox(int offset, int& pos) const;
+
+ bool allowTabs() const { return !style()->collapseWhiteSpace(); }
+
+ void checkConsistency() const;
+
+ virtual void computePreferredLogicalWidths(int leadWidth);
+ bool isAllCollapsibleWhitespace();
+
+protected:
+ virtual void styleWillChange(StyleDifference, const RenderStyle*) { }
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual void setTextInternal(PassRefPtr<StringImpl>);
+ virtual UChar previousCharacter() const;
+
+ virtual InlineTextBox* createTextBox(); // Subclassed by SVG.
+
+private:
+ void computePreferredLogicalWidths(int leadWidth, HashSet<const SimpleFontData*>& fallbackFonts, GlyphOverflow&);
+
+ // Make length() private so that callers that have a RenderText*
+ // will use the more efficient textLength() instead, while
+ // callers with a RenderObject* can continue to use length().
+ virtual unsigned length() const { return textLength(); }
+
+ virtual void paint(PaintInfo&, int, int) { ASSERT_NOT_REACHED(); }
+ virtual void layout() { ASSERT_NOT_REACHED(); }
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction) { ASSERT_NOT_REACHED(); return false; }
+
+ void deleteTextBoxes();
+ bool containsOnlyWhitespace(unsigned from, unsigned len) const;
+ int widthFromCache(const Font&, int start, int len, int xPos, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow*) const;
+ bool isAllASCII() const { return m_isAllASCII; }
+ void updateNeedsTranscoding();
+
+ inline void transformText(String&) const;
+
+ int m_minWidth; // here to minimize padding in 64-bit.
+
+ String m_text;
+
+ InlineTextBox* m_firstTextBox;
+ InlineTextBox* m_lastTextBox;
+
+ int m_maxWidth;
+ int m_beginMinWidth;
+ int m_endMinWidth;
+
+ bool m_hasBreakableChar : 1; // Whether or not we can be broken into multiple lines.
+ bool m_hasBreak : 1; // Whether or not we have a hard break (e.g., <pre> with '\n').
+ bool m_hasTab : 1; // Whether or not we have a variable width tab character (e.g., <pre> with '\t').
+ bool m_hasBeginWS : 1; // Whether or not we begin with WS (only true if we aren't pre)
+ bool m_hasEndWS : 1; // Whether or not we end with WS (only true if we aren't pre)
+ bool m_linesDirty : 1; // This bit indicates that the text run has already dirtied specific
+ // line boxes, and this hint will enable layoutInlineChildren to avoid
+ // just dirtying everything when character data is modified (e.g., appended/inserted
+ // or removed).
+ bool m_containsReversedText : 1;
+ bool m_isAllASCII : 1;
+ mutable bool m_knownToHaveNoOverflowAndNoFallbackFonts : 1;
+ bool m_needsTranscoding : 1;
+};
+
+inline RenderText* toRenderText(RenderObject* object)
+{
+ ASSERT(!object || object->isText());
+ return static_cast<RenderText*>(object);
+}
+
+inline const RenderText* toRenderText(const RenderObject* object)
+{
+ ASSERT(!object || object->isText());
+ return static_cast<const RenderText*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderText(const RenderText*);
+
+#ifdef NDEBUG
+inline void RenderText::checkConsistency() const
+{
+}
+#endif
+
+} // namespace WebCore
+
+#endif // RenderText_h
diff --git a/Source/WebCore/rendering/RenderTextControl.cpp b/Source/WebCore/rendering/RenderTextControl.cpp
new file mode 100644
index 0000000..0192ee4
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControl.cpp
@@ -0,0 +1,629 @@
+/**
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2008 Torch Mobile Inc. All rights reserved. (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.
+ *
+ */
+
+#include "config.h"
+#include "RenderTextControl.h"
+
+#include "AXObjectCache.h"
+#include "CharacterNames.h"
+#include "Editor.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLBRElement.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HitTestResult.h"
+#include "RenderLayer.h"
+#include "RenderText.h"
+#include "ScrollbarTheme.h"
+#include "SelectionController.h"
+#include "Text.h"
+#include "TextControlInnerElements.h"
+#include "TextIterator.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// Value chosen by observation. This can be tweaked.
+static const int minColorContrastValue = 1300;
+
+static Color disabledTextColor(const Color& textColor, const Color& backgroundColor)
+{
+ // The explicit check for black is an optimization for the 99% case (black on white).
+ // This also means that black on black will turn into grey on black when disabled.
+ Color disabledColor;
+ if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white))
+ disabledColor = textColor.light();
+ else
+ disabledColor = textColor.dark();
+
+ // If there's not very much contrast between the disabled color and the background color,
+ // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast.
+ // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
+ if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue)
+ return textColor;
+
+ return disabledColor;
+}
+
+RenderTextControl::RenderTextControl(Node* node, bool placeholderVisible)
+ : RenderBlock(node)
+ , m_placeholderVisible(placeholderVisible)
+ , m_wasChangedSinceLastChangeEvent(false)
+ , m_lastChangeWasUserEdit(false)
+{
+}
+
+RenderTextControl::~RenderTextControl()
+{
+ // The children renderers have already been destroyed by destroyLeftoverChildren
+ if (m_innerText)
+ m_innerText->detach();
+}
+
+void RenderTextControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderBlock::styleDidChange(diff, oldStyle);
+
+ if (m_innerText) {
+ RenderBlock* textBlockRenderer = toRenderBlock(m_innerText->renderer());
+ RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style());
+ // We may have set the width and the height in the old style in layout().
+ // Reset them now to avoid getting a spurious layout hint.
+ textBlockRenderer->style()->setHeight(Length());
+ textBlockRenderer->style()->setWidth(Length());
+ setInnerTextStyle(textBlockStyle);
+ }
+
+ setReplaced(isInline());
+}
+
+void RenderTextControl::setInnerTextStyle(PassRefPtr<RenderStyle> style)
+{
+ if (m_innerText) {
+ RefPtr<RenderStyle> textStyle = style;
+ m_innerText->renderer()->setStyle(textStyle);
+ for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->renderer())
+ n->renderer()->setStyle(textStyle);
+ }
+ }
+}
+
+static inline bool updateUserModifyProperty(Node* node, RenderStyle* style)
+{
+ bool isEnabled = true;
+ bool isReadOnlyControl = false;
+
+ if (node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ isEnabled = element->isEnabledFormControl();
+ isReadOnlyControl = element->isReadOnlyFormControl();
+ }
+
+ style->setUserModify((isReadOnlyControl || !isEnabled) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY);
+ return !isEnabled;
+}
+
+void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const
+{
+ // The inner block, if present, always has its direction set to LTR,
+ // so we need to inherit the direction from the element.
+ textBlockStyle->setDirection(style()->direction());
+
+ bool disabled = updateUserModifyProperty(node(), textBlockStyle);
+ if (disabled)
+ textBlockStyle->setColor(disabledTextColor(textBlockStyle->visitedDependentColor(CSSPropertyColor), startStyle->visitedDependentColor(CSSPropertyBackgroundColor)));
+}
+
+void RenderTextControl::createSubtreeIfNeeded(TextControlInnerElement* innerBlock)
+{
+ if (!m_innerText) {
+ // Create the text block element
+ // For non-search fields, there is no intermediate innerBlock as the shadow node.
+ // m_innerText will be the shadow node in that case.
+ RenderStyle* parentStyle = innerBlock ? innerBlock->renderer()->style() : style();
+ m_innerText = TextControlInnerTextElement::create(document(), innerBlock ? 0 : static_cast<HTMLElement*>(node()));
+ m_innerText->attachInnerElement(innerBlock ? innerBlock : node(), createInnerTextStyle(parentStyle), renderArena());
+ }
+}
+
+int RenderTextControl::textBlockHeight() const
+{
+ return height() - borderAndPaddingHeight();
+}
+
+int RenderTextControl::textBlockWidth() const
+{
+ return width() - borderAndPaddingWidth() - m_innerText->renderBox()->paddingLeft() - m_innerText->renderBox()->paddingRight();
+}
+
+void RenderTextControl::updateFromElement()
+{
+ updateUserModifyProperty(node(), m_innerText->renderer()->style());
+}
+
+void RenderTextControl::setInnerTextValue(const String& innerTextValue)
+{
+ String value = innerTextValue;
+ if (value != text() || !m_innerText->hasChildNodes()) {
+ if (value != text()) {
+ if (Frame* frame = this->frame()) {
+ frame->editor()->clearUndoRedoOperations();
+
+ if (AXObjectCache::accessibilityEnabled())
+ document()->axObjectCache()->postNotification(this, AXObjectCache::AXValueChanged, false);
+ }
+ }
+
+ ExceptionCode ec = 0;
+ m_innerText->setInnerText(value, ec);
+ ASSERT(!ec);
+
+ if (value.endsWith("\n") || value.endsWith("\r")) {
+ m_innerText->appendChild(HTMLBRElement::create(document()), ec);
+ ASSERT(!ec);
+ }
+
+ // We set m_lastChangeWasUserEdit to false since this change was not explicitly made by the user (say, via typing on the keyboard), see <rdar://problem/5359921>.
+ m_lastChangeWasUserEdit = false;
+ }
+
+ static_cast<Element*>(node())->setFormControlValueMatchesRenderer(true);
+}
+
+void RenderTextControl::setLastChangeWasUserEdit(bool lastChangeWasUserEdit)
+{
+ m_lastChangeWasUserEdit = lastChangeWasUserEdit;
+ document()->setIgnoreAutofocus(lastChangeWasUserEdit);
+}
+
+int RenderTextControl::selectionStart() const
+{
+ Frame* frame = this->frame();
+ if (!frame)
+ return 0;
+ return indexForVisiblePosition(frame->selection()->start());
+}
+
+int RenderTextControl::selectionEnd() const
+{
+ Frame* frame = this->frame();
+ if (!frame)
+ return 0;
+ return indexForVisiblePosition(frame->selection()->end());
+}
+
+bool RenderTextControl::hasVisibleTextArea() const
+{
+ return style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderBox()->height();
+}
+
+void setSelectionRange(Node* node, int start, int end)
+{
+ ASSERT(node);
+ node->document()->updateLayoutIgnorePendingStylesheets();
+
+ if (!node->renderer() || !node->renderer()->isTextControl())
+ return;
+
+ end = max(end, 0);
+ start = min(max(start, 0), end);
+
+ RenderTextControl* control = toRenderTextControl(node->renderer());
+
+ if (control->hasVisibleTextArea()) {
+ control->cacheSelection(start, end);
+ return;
+ }
+ VisiblePosition startPosition = control->visiblePositionForIndex(start);
+ VisiblePosition endPosition;
+ if (start == end)
+ endPosition = startPosition;
+ else
+ endPosition = control->visiblePositionForIndex(end);
+
+ // startPosition and endPosition can be null position for example when
+ // "-webkit-user-select: none" style attribute is specified.
+ if (startPosition.isNotNull() && endPosition.isNotNull()) {
+ ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node && endPosition.deepEquivalent().node()->shadowAncestorNode() == node);
+ }
+ VisibleSelection newSelection = VisibleSelection(startPosition, endPosition);
+
+ if (Frame* frame = node->document()->frame())
+ frame->selection()->setSelection(newSelection);
+}
+
+bool RenderTextControl::isSelectableElement(Node* node) const
+{
+ if (!node || !m_innerText)
+ return false;
+
+ if (node->rootEditableElement() == m_innerText)
+ return true;
+
+ if (!m_innerText->contains(node))
+ return false;
+
+ Node* shadowAncestor = node->shadowAncestorNode();
+ return shadowAncestor && (shadowAncestor->hasTagName(textareaTag)
+ || (shadowAncestor->hasTagName(inputTag) && static_cast<HTMLInputElement*>(shadowAncestor)->isTextField()));
+}
+
+PassRefPtr<Range> RenderTextControl::selection(int start, int end) const
+{
+ if (!m_innerText)
+ return 0;
+
+ return Range::create(document(), m_innerText, start, m_innerText, end);
+}
+
+VisiblePosition RenderTextControl::visiblePositionForIndex(int index) const
+{
+ if (index <= 0)
+ return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM);
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(document());
+ range->selectNodeContents(m_innerText.get(), ec);
+ ASSERT(!ec);
+ CharacterIterator it(range.get());
+ it.advance(index - 1);
+ Node* endContainer = it.range()->endContainer(ec);
+ ASSERT(!ec);
+ int endOffset = it.range()->endOffset(ec);
+ ASSERT(!ec);
+ return VisiblePosition(endContainer, endOffset, UPSTREAM);
+}
+
+int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) const
+{
+ Position indexPosition = pos.deepEquivalent();
+ if (!isSelectableElement(indexPosition.node()))
+ return 0;
+ ExceptionCode ec = 0;
+ RefPtr<Range> range = Range::create(document());
+ range->setStart(m_innerText.get(), 0, ec);
+ ASSERT(!ec);
+ range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
+ ASSERT(!ec);
+ return TextIterator::rangeLength(range.get());
+}
+
+void RenderTextControl::subtreeHasChanged()
+{
+ m_wasChangedSinceLastChangeEvent = true;
+ m_lastChangeWasUserEdit = true;
+}
+
+String RenderTextControl::finishText(Vector<UChar>& result) const
+{
+ // Remove one trailing newline; there's always one that's collapsed out by rendering.
+ size_t size = result.size();
+ if (size && result[size - 1] == '\n')
+ result.shrink(--size);
+
+ return String::adopt(result);
+}
+
+String RenderTextControl::text()
+{
+ if (!m_innerText)
+ return "";
+
+ Vector<UChar> result;
+
+ for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->hasTagName(brTag))
+ result.append(&newlineCharacter, 1);
+ else if (n->isTextNode()) {
+ String data = static_cast<Text*>(n)->data();
+ result.append(data.characters(), data.length());
+ }
+ }
+
+ return finishText(result);
+}
+
+static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset)
+{
+ RootInlineBox* next;
+ for (; line; line = next) {
+ next = line->nextRootBox();
+ if (next && !line->endsWithBreak()) {
+ ASSERT(line->lineBreakObj());
+ breakNode = line->lineBreakObj()->node();
+ breakOffset = line->lineBreakPos();
+ line = next;
+ return;
+ }
+ }
+ breakNode = 0;
+ breakOffset = 0;
+}
+
+String RenderTextControl::textWithHardLineBreaks()
+{
+ if (!m_innerText)
+ return "";
+
+ RenderBlock* renderer = toRenderBlock(m_innerText->renderer());
+ if (!renderer)
+ return "";
+
+ Node* breakNode;
+ unsigned breakOffset;
+ RootInlineBox* line = renderer->firstRootBox();
+ if (!line)
+ return "";
+
+ getNextSoftBreak(line, breakNode, breakOffset);
+
+ Vector<UChar> result;
+
+ for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) {
+ if (n->hasTagName(brTag))
+ result.append(&newlineCharacter, 1);
+ else if (n->isTextNode()) {
+ Text* text = static_cast<Text*>(n);
+ String data = text->data();
+ unsigned length = data.length();
+ unsigned position = 0;
+ while (breakNode == n && breakOffset <= length) {
+ if (breakOffset > position) {
+ result.append(data.characters() + position, breakOffset - position);
+ position = breakOffset;
+ result.append(&newlineCharacter, 1);
+ }
+ getNextSoftBreak(line, breakNode, breakOffset);
+ }
+ result.append(data.characters() + position, length - position);
+ }
+ while (breakNode == n)
+ getNextSoftBreak(line, breakNode, breakOffset);
+ }
+
+ return finishText(result);
+}
+
+int RenderTextControl::scrollbarThickness() const
+{
+ // FIXME: We should get the size of the scrollbar from the RenderTheme instead.
+ return ScrollbarTheme::nativeTheme()->scrollbarThickness();
+}
+
+void RenderTextControl::computeLogicalHeight()
+{
+ setHeight(m_innerText->renderBox()->borderTop() + m_innerText->renderBox()->borderBottom() +
+ m_innerText->renderBox()->paddingTop() + m_innerText->renderBox()->paddingBottom() +
+ m_innerText->renderBox()->marginTop() + m_innerText->renderBox()->marginBottom());
+
+ adjustControlHeightBasedOnLineHeight(m_innerText->renderBox()->lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes));
+ setHeight(height() + borderAndPaddingHeight());
+
+ // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap.
+ if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap))
+ setHeight(height() + scrollbarThickness());
+
+ RenderBlock::computeLogicalHeight();
+}
+
+void RenderTextControl::hitInnerTextElement(HitTestResult& result, int xPos, int yPos, int tx, int ty)
+{
+ result.setInnerNode(m_innerText.get());
+ result.setInnerNonSharedNode(m_innerText.get());
+ result.setLocalPoint(IntPoint(xPos - tx - x() - m_innerText->renderBox()->x(),
+ yPos - ty - y() - m_innerText->renderBox()->y()));
+}
+
+void RenderTextControl::forwardEvent(Event* event)
+{
+ if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)
+ return;
+ m_innerText->defaultEventHandler(event);
+}
+
+static const char* fontFamiliesWithInvalidCharWidth[] = {
+ "American Typewriter",
+ "Arial Hebrew",
+ "Chalkboard",
+ "Cochin",
+ "Corsiva Hebrew",
+ "Courier",
+ "Euphemia UCAS",
+ "Geneva",
+ "Gill Sans",
+ "Hei",
+ "Helvetica",
+ "Hoefler Text",
+ "InaiMathi",
+ "Kai",
+ "Lucida Grande",
+ "Marker Felt",
+ "Monaco",
+ "Mshtakan",
+ "New Peninim MT",
+ "Osaka",
+ "Raanana",
+ "STHeiti",
+ "Symbol",
+ "Times",
+ "Apple Braille",
+ "Apple LiGothic",
+ "Apple LiSung",
+ "Apple Symbols",
+ "AppleGothic",
+ "AppleMyungjo",
+ "#GungSeo",
+ "#HeadLineA",
+ "#PCMyungjo",
+ "#PilGi",
+};
+
+// For font families where any of the fonts don't have a valid entry in the OS/2 table
+// for avgCharWidth, fallback to the legacy webkit behavior of getting the avgCharWidth
+// from the width of a '0'. This only seems to apply to a fixed number of Mac fonts,
+// but, in order to get similar rendering across platforms, we do this check for
+// all platforms.
+bool RenderTextControl::hasValidAvgCharWidth(AtomicString family)
+{
+ static HashSet<AtomicString>* fontFamiliesWithInvalidCharWidthMap = 0;
+
+ if (!fontFamiliesWithInvalidCharWidthMap) {
+ fontFamiliesWithInvalidCharWidthMap = new HashSet<AtomicString>;
+
+ for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontFamiliesWithInvalidCharWidth); ++i)
+ fontFamiliesWithInvalidCharWidthMap->add(AtomicString(fontFamiliesWithInvalidCharWidth[i]));
+ }
+
+ return !fontFamiliesWithInvalidCharWidthMap->contains(family);
+}
+
+float RenderTextControl::getAvgCharWidth(AtomicString family)
+{
+ if (hasValidAvgCharWidth(family))
+ return roundf(style()->font().primaryFont()->avgCharWidth());
+
+ const UChar ch = '0';
+ return style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false));
+}
+
+float RenderTextControl::scaleEmToUnits(int x) const
+{
+ // This matches the unitsPerEm value for MS Shell Dlg and Courier New from the "head" font table.
+ float unitsPerEm = 2048.0f;
+ return roundf(style()->font().size() * x / unitsPerEm);
+}
+
+void RenderTextControl::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 {
+ // Use average character width. Matches IE.
+ AtomicString family = style()->font().family().family();
+ m_maxPreferredLogicalWidth = preferredContentWidth(getAvgCharWidth(family)) + m_innerText->renderBox()->paddingLeft() + m_innerText->renderBox()->paddingRight();
+ }
+
+ 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 RenderTextControl::selectionChanged(bool userTriggered)
+{
+ cacheSelection(selectionStart(), selectionEnd());
+
+ if (Frame* frame = this->frame()) {
+ if (frame->selection()->isRange() && userTriggered)
+ node()->dispatchEvent(Event::create(eventNames().selectEvent, true, false));
+ }
+}
+
+void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
+{
+ if (width() && height())
+ rects.append(IntRect(tx, ty, width(), height()));
+}
+
+HTMLElement* RenderTextControl::innerTextElement() const
+{
+ return m_innerText.get();
+}
+
+void RenderTextControl::updatePlaceholderVisibility(bool placeholderShouldBeVisible, bool placeholderValueChanged)
+{
+ bool oldPlaceholderVisible = m_placeholderVisible;
+ m_placeholderVisible = placeholderShouldBeVisible;
+ if (oldPlaceholderVisible != m_placeholderVisible || placeholderValueChanged)
+ repaint();
+}
+
+void RenderTextControl::paintPlaceholder(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (style()->visibility() != VISIBLE)
+ return;
+
+ IntRect clipRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop());
+ if (clipRect.isEmpty())
+ return;
+
+ paintInfo.context->save();
+
+ paintInfo.context->clip(clipRect);
+
+ RefPtr<RenderStyle> placeholderStyle = getCachedPseudoStyle(INPUT_PLACEHOLDER);
+ if (!placeholderStyle)
+ placeholderStyle = style();
+
+ paintInfo.context->setFillColor(placeholderStyle->visitedDependentColor(CSSPropertyColor), placeholderStyle->colorSpace());
+
+ String placeholderText = static_cast<HTMLTextFormControlElement*>(node())->strippedPlaceholder();
+ TextRun textRun(placeholderText.characters(), placeholderText.length(), 0, 0, 0, !placeholderStyle->isLeftToRightDirection(), placeholderStyle->unicodeBidi() == Override, false, false);
+
+ RenderBox* textRenderer = innerTextElement() ? innerTextElement()->renderBox() : 0;
+ if (textRenderer) {
+ IntPoint textPoint;
+ textPoint.setY(ty + textBlockInsetTop() + placeholderStyle->font().ascent());
+ if (placeholderStyle->isLeftToRightDirection())
+ textPoint.setX(tx + textBlockInsetLeft());
+ else
+ textPoint.setX(tx + width() - textBlockInsetRight() - style()->font().width(textRun));
+
+ paintInfo.context->drawBidiText(placeholderStyle->font(), textRun, textPoint);
+ }
+ paintInfo.context->restore();
+}
+
+void RenderTextControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (m_placeholderVisible && paintInfo.phase == PaintPhaseForeground)
+ paintPlaceholder(paintInfo, tx, ty);
+
+ RenderBlock::paintObject(paintInfo, tx, ty);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTextControl.h b/Source/WebCore/rendering/RenderTextControl.h
new file mode 100644
index 0000000..fdb7fdc
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControl.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2008 Torch Mobile Inc. All rights reserved. (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 RenderTextControl_h
+#define RenderTextControl_h
+
+#include "RenderBlock.h"
+
+namespace WebCore {
+
+class VisibleSelection;
+class TextControlInnerElement;
+class TextControlInnerTextElement;
+
+class RenderTextControl : public RenderBlock {
+public:
+ virtual ~RenderTextControl();
+
+ bool wasChangedSinceLastChangeEvent() const { return m_wasChangedSinceLastChangeEvent; }
+ void setChangedSinceLastChangeEvent(bool wasChangedSinceLastChangeEvent) { m_wasChangedSinceLastChangeEvent = wasChangedSinceLastChangeEvent; }
+
+ bool lastChangeWasUserEdit() const { return m_lastChangeWasUserEdit; }
+ void setLastChangeWasUserEdit(bool lastChangeWasUserEdit);
+
+ int selectionStart() const;
+ int selectionEnd() const;
+ PassRefPtr<Range> selection(int start, int end) const;
+
+ virtual void subtreeHasChanged();
+ String text();
+ String textWithHardLineBreaks();
+ void selectionChanged(bool userTriggered);
+
+ VisiblePosition visiblePositionForIndex(int index) const;
+ int indexForVisiblePosition(const VisiblePosition&) const;
+
+ void updatePlaceholderVisibility(bool, bool);
+
+protected:
+ RenderTextControl(Node*, bool);
+
+ int scrollbarThickness() const;
+ void adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const;
+ void setInnerTextValue(const String&);
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ void createSubtreeIfNeeded(TextControlInnerElement* innerBlock);
+ void hitInnerTextElement(HitTestResult&, int x, int y, int tx, int ty);
+ void forwardEvent(Event*);
+
+ int textBlockWidth() const;
+ int textBlockHeight() const;
+
+ float scaleEmToUnits(int x) const;
+
+ static bool hasValidAvgCharWidth(AtomicString family);
+ virtual float getAvgCharWidth(AtomicString family);
+ virtual int preferredContentWidth(float charWidth) const = 0;
+ virtual void adjustControlHeightBasedOnLineHeight(int lineHeight) = 0;
+ virtual void cacheSelection(int start, int end) = 0;
+ virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const = 0;
+ virtual RenderStyle* textBaseStyle() const = 0;
+
+ virtual void updateFromElement();
+ virtual void computeLogicalHeight();
+
+ friend class TextIterator;
+ HTMLElement* innerTextElement() const;
+
+ bool m_placeholderVisible;
+
+private:
+ virtual const char* renderName() const { return "RenderTextControl"; }
+ virtual bool isTextControl() const { return true; }
+ virtual void computePreferredLogicalWidths();
+ virtual void removeLeftoverAnonymousBlock(RenderBlock*) { }
+ virtual bool canHaveChildren() const { return false; }
+ virtual bool avoidsFloats() const { return true; }
+ void setInnerTextStyle(PassRefPtr<RenderStyle>);
+ virtual void paintObject(PaintInfo&, int tx, int ty);
+
+ virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
+
+ virtual bool canBeProgramaticallyScrolled(bool) const { return true; }
+
+ virtual bool requiresForcedStyleRecalcPropagation() const { return true; }
+
+ String finishText(Vector<UChar>&) const;
+
+ bool hasVisibleTextArea() const;
+ friend void setSelectionRange(Node*, int start, int end);
+ bool isSelectableElement(Node*) const;
+
+ virtual int textBlockInsetLeft() const = 0;
+ virtual int textBlockInsetRight() const = 0;
+ virtual int textBlockInsetTop() const = 0;
+
+ void paintPlaceholder(PaintInfo&, int tx, int ty);
+
+ bool m_wasChangedSinceLastChangeEvent;
+ bool m_lastChangeWasUserEdit;
+ RefPtr<TextControlInnerTextElement> m_innerText;
+};
+
+void setSelectionRange(Node*, int start, int end);
+
+inline RenderTextControl* toRenderTextControl(RenderObject* object)
+{
+ ASSERT(!object || object->isTextControl());
+ return static_cast<RenderTextControl*>(object);
+}
+
+inline const RenderTextControl* toRenderTextControl(const RenderObject* object)
+{
+ ASSERT(!object || object->isTextControl());
+ return static_cast<const RenderTextControl*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTextControl(const RenderTextControl*);
+
+} // namespace WebCore
+
+#endif // RenderTextControl_h
diff --git a/Source/WebCore/rendering/RenderTextControlMultiLine.cpp b/Source/WebCore/rendering/RenderTextControlMultiLine.cpp
new file mode 100644
index 0000000..d0b0cbc
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControlMultiLine.cpp
@@ -0,0 +1,163 @@
+/**
+ * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * (C) 2008 Torch Mobile Inc. All rights reserved. (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.
+ *
+ */
+
+#include "config.h"
+#include "RenderTextControlMultiLine.h"
+
+#include "Event.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLNames.h"
+#include "HTMLTextAreaElement.h"
+#include "HitTestResult.h"
+#ifdef ANDROID_LAYOUT
+#include "Settings.h"
+#endif
+
+namespace WebCore {
+
+RenderTextControlMultiLine::RenderTextControlMultiLine(Node* node, bool placeholderVisible)
+ : RenderTextControl(node, placeholderVisible)
+{
+}
+
+RenderTextControlMultiLine::~RenderTextControlMultiLine()
+{
+ if (node())
+ static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed();
+}
+
+void RenderTextControlMultiLine::subtreeHasChanged()
+{
+ RenderTextControl::subtreeHasChanged();
+ HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(node());
+ textArea->setFormControlValueMatchesRenderer(false);
+ textArea->setNeedsValidityCheck();
+
+ if (!node()->focused())
+ return;
+
+ if (Frame* frame = this->frame())
+ frame->editor()->textDidChangeInTextArea(textArea);
+}
+
+bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
+{
+ if (!RenderTextControl::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction))
+ return false;
+
+ if (result.innerNode() == node() || result.innerNode() == innerTextElement())
+ hitInnerTextElement(result, x, y, tx, ty);
+
+ return true;
+}
+
+void RenderTextControlMultiLine::forwardEvent(Event* event)
+{
+ RenderTextControl::forwardEvent(event);
+}
+
+float RenderTextControlMultiLine::getAvgCharWidth(AtomicString family)
+{
+ // Since Lucida Grande is the default font, we want this to match the width
+ // of Courier New, the default font for textareas in IE, Firefox and Safari Win.
+ // 1229 is the avgCharWidth value in the OS/2 table for Courier New.
+ if (family == AtomicString("Lucida Grande"))
+ return scaleEmToUnits(1229);
+
+ return RenderTextControl::getAvgCharWidth(family);
+}
+
+int RenderTextControlMultiLine::preferredContentWidth(float charWidth) const
+{
+ int factor = static_cast<HTMLTextAreaElement*>(node())->cols();
+ return static_cast<int>(ceilf(charWidth * factor)) + scrollbarThickness();
+}
+
+void RenderTextControlMultiLine::adjustControlHeightBasedOnLineHeight(int lineHeight)
+{
+ setHeight(height() + lineHeight * static_cast<HTMLTextAreaElement*>(node())->rows());
+}
+
+int RenderTextControlMultiLine::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
+{
+ return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
+}
+
+void RenderTextControlMultiLine::updateFromElement()
+{
+ createSubtreeIfNeeded(0);
+ RenderTextControl::updateFromElement();
+
+ setInnerTextValue(static_cast<HTMLTextAreaElement*>(node())->value());
+}
+
+void RenderTextControlMultiLine::cacheSelection(int start, int end)
+{
+ static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end);
+}
+
+PassRefPtr<RenderStyle> RenderTextControlMultiLine::createInnerTextStyle(const RenderStyle* startStyle) const
+{
+ RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
+ textBlockStyle->inheritFrom(startStyle);
+ adjustInnerTextStyle(startStyle, textBlockStyle.get());
+ textBlockStyle->setDisplay(BLOCK);
+
+ return textBlockStyle.release();
+}
+
+RenderStyle* RenderTextControlMultiLine::textBaseStyle() const
+{
+ return style();
+}
+
+int RenderTextControlMultiLine::textBlockInsetLeft() const
+{
+ int inset = borderLeft() + paddingLeft();
+ if (HTMLElement* innerText = innerTextElement()) {
+ if (RenderBox* innerTextRenderer = innerText->renderBox())
+ inset += innerTextRenderer->paddingLeft();
+ }
+ return inset;
+}
+
+int RenderTextControlMultiLine::textBlockInsetRight() const
+{
+ int inset = borderRight() + paddingRight();
+ if (HTMLElement* innerText = innerTextElement()) {
+ if (RenderBox* innerTextRenderer = innerText->renderBox())
+ inset += innerTextRenderer->paddingRight();
+ }
+ return inset;
+}
+
+int RenderTextControlMultiLine::textBlockInsetTop() const
+{
+ int inset = borderTop() + paddingTop();
+ if (HTMLElement* innerText = innerTextElement()) {
+ if (RenderBox* innerTextRenderer = innerText->renderBox())
+ inset += innerTextRenderer->paddingTop();
+ }
+ return inset;
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderTextControlMultiLine.h b/Source/WebCore/rendering/RenderTextControlMultiLine.h
new file mode 100644
index 0000000..0bb8c8f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControlMultiLine.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ * Copyright (C) 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 RenderTextControlMultiLine_h
+#define RenderTextControlMultiLine_h
+
+#include "RenderTextControl.h"
+
+namespace WebCore {
+
+class RenderTextControlMultiLine : public RenderTextControl {
+public:
+ RenderTextControlMultiLine(Node*, bool);
+ virtual ~RenderTextControlMultiLine();
+
+ void forwardEvent(Event*);
+
+private:
+ virtual bool isTextArea() const { return true; }
+
+ virtual void subtreeHasChanged();
+
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+
+ virtual float getAvgCharWidth(AtomicString family);
+ virtual int preferredContentWidth(float charWidth) const;
+ virtual void adjustControlHeightBasedOnLineHeight(int lineHeight);
+ virtual int baselinePosition(FontBaseline, bool firstLine, LineDirectionMode, LinePositionMode = PositionOnContainingLine) const;
+
+ virtual void updateFromElement();
+ virtual void cacheSelection(int start, int end);
+
+ virtual RenderStyle* textBaseStyle() const;
+ virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const;
+ virtual int textBlockInsetLeft() const;
+ virtual int textBlockInsetRight() const;
+ virtual int textBlockInsetTop() const;
+};
+
+inline RenderTextControlMultiLine* toRenderTextControlMultiLine(RenderObject* object)
+{
+ ASSERT(!object || object->isTextArea());
+ return static_cast<RenderTextControlMultiLine*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTextControlMultiLine(const RenderTextControlMultiLine*);
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderTextControlSingleLine.cpp b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp
new file mode 100644
index 0000000..29538ce
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControlSingleLine.cpp
@@ -0,0 +1,1151 @@
+/**
+ * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved.
+ * (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * 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 "RenderTextControlSingleLine.h"
+
+#include "Chrome.h"
+#include "CSSStyleSelector.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HitTestResult.h"
+#include "InputElement.h"
+#include "LocalizedStrings.h"
+#include "MouseEvent.h"
+#include "PlatformKeyboardEvent.h"
+#include "RenderLayer.h"
+#include "RenderScrollbar.h"
+#include "RenderTheme.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SimpleFontData.h"
+#include "TextControlInnerElements.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node, bool placeholderVisible)
+ : RenderTextControl(node, placeholderVisible)
+ , m_searchPopupIsVisible(false)
+ , m_shouldDrawCapsLockIndicator(false)
+ , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired)
+ , m_searchPopup(0)
+{
+}
+
+RenderTextControlSingleLine::~RenderTextControlSingleLine()
+{
+ if (m_searchPopup) {
+ m_searchPopup->popupMenu()->disconnectClient();
+ m_searchPopup = 0;
+ }
+
+ if (m_innerBlock) {
+ m_innerBlock->detach();
+ m_innerBlock = 0;
+ }
+
+ if (m_innerSpinButton)
+ m_innerSpinButton->detach();
+ if (m_outerSpinButton)
+ m_outerSpinButton->detach();
+#if ENABLE(INPUT_SPEECH)
+ if (m_speechButton)
+ m_speechButton->detach();
+#endif
+}
+
+RenderStyle* RenderTextControlSingleLine::textBaseStyle() const
+{
+ return m_innerBlock ? m_innerBlock->renderer()->style() : style();
+}
+
+void RenderTextControlSingleLine::addSearchResult()
+{
+ ASSERT(node()->isHTMLElement());
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (input->maxResults() <= 0)
+ return;
+
+ String value = input->value();
+ if (value.isEmpty())
+ return;
+
+ Settings* settings = document()->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ int size = static_cast<int>(m_recentSearches.size());
+ for (int i = size - 1; i >= 0; --i) {
+ if (m_recentSearches[i] == value)
+ m_recentSearches.remove(i);
+ }
+
+ m_recentSearches.insert(0, value);
+ while (static_cast<int>(m_recentSearches.size()) > input->maxResults())
+ m_recentSearches.removeLast();
+
+ const AtomicString& name = autosaveName();
+ if (!m_searchPopup)
+ m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this);
+
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+}
+
+void RenderTextControlSingleLine::stopSearchEventTimer()
+{
+ ASSERT(node()->isHTMLElement());
+ m_searchEventTimer.stop();
+}
+
+void RenderTextControlSingleLine::showPopup()
+{
+ ASSERT(node()->isHTMLElement());
+ if (m_searchPopupIsVisible)
+ return;
+
+ if (!m_searchPopup)
+ m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this);
+
+ if (!m_searchPopup->enabled())
+ return;
+
+ m_searchPopupIsVisible = true;
+
+ const AtomicString& name = autosaveName();
+ m_searchPopup->loadRecentSearches(name, m_recentSearches);
+
+ // Trim the recent searches list if the maximum size has changed since we last saved.
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) {
+ do {
+ m_recentSearches.removeLast();
+ } while (static_cast<int>(m_recentSearches.size()) > input->maxResults());
+
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+ }
+
+ m_searchPopup->popupMenu()->show(absoluteBoundingBoxRect(true), document()->view(), -1);
+}
+
+void RenderTextControlSingleLine::hidePopup()
+{
+ ASSERT(node()->isHTMLElement());
+ if (m_searchPopup)
+ m_searchPopup->popupMenu()->hide();
+}
+
+void RenderTextControlSingleLine::subtreeHasChanged()
+{
+ bool wasChanged = wasChangedSinceLastChangeEvent();
+ RenderTextControl::subtreeHasChanged();
+
+ InputElement* input = inputElement();
+ // We don't need to call sanitizeUserInputValue() function here because
+ // InputElement::handleBeforeTextInsertedEvent() has already called
+ // sanitizeUserInputValue().
+ // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent.
+ String value = text();
+ if (input->isAcceptableValue(value))
+ input->setValueFromRenderer(input->sanitizeValue(value));
+ if (node()->isHTMLElement()) {
+ // Recalc for :invalid and hasUnacceptableValue() change.
+ static_cast<HTMLInputElement*>(input)->setNeedsStyleRecalc();
+ }
+
+ if (m_cancelButton)
+ updateCancelButtonVisibility();
+
+ // If the incremental attribute is set, then dispatch the search event
+ if (input->searchEventsShouldBeDispatched())
+ startSearchEventTimer();
+
+ if (!wasChanged && node()->focused()) {
+ if (Frame* frame = this->frame())
+ frame->editor()->textFieldDidBeginEditing(static_cast<Element*>(node()));
+ }
+
+ if (node()->focused()) {
+ if (Frame* frame = document()->frame())
+ frame->editor()->textDidChangeInTextField(static_cast<Element*>(node()));
+ }
+}
+
+void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ RenderTextControl::paint(paintInfo, tx, ty);
+
+ if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) {
+ IntRect contentsRect = contentBoxRect();
+
+ // Center vertically like the text.
+ contentsRect.setY((height() - contentsRect.height()) / 2);
+
+ // Convert the rect into the coords used for painting the content
+ contentsRect.move(tx + x(), ty + y());
+ theme()->paintCapsLockIndicator(this, paintInfo, contentsRect);
+ }
+}
+
+void RenderTextControlSingleLine::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
+{
+ paintBoxDecorationsWithSize(paintInfo, tx, ty, width() - decorationWidthRight(), height());
+}
+
+void RenderTextControlSingleLine::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
+{
+ int w = width() - decorationWidthRight();
+ if (w && height())
+ rects.append(IntRect(tx, ty, w, height()));
+}
+
+void RenderTextControlSingleLine::layout()
+{
+ int oldHeight = height();
+ computeLogicalHeight();
+
+ int oldWidth = width();
+ computeLogicalWidth();
+
+ bool relayoutChildren = oldHeight != height() || oldWidth != width();
+
+#ifdef ANDROID_LAYOUT
+ checkAndSetRelayoutChildren(&relayoutChildren);
+#endif
+
+ RenderBox* innerTextRenderer = innerTextElement()->renderBox();
+ RenderBox* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderBox() : 0;
+
+ // Set the text block height
+ int desiredHeight = textBlockHeight();
+ int currentHeight = innerTextRenderer->height();
+
+ if (currentHeight > height()) {
+ if (desiredHeight != currentHeight)
+ relayoutChildren = true;
+ innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed));
+ if (m_innerBlock)
+ innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed));
+ }
+
+ // Set the text block width
+ int desiredWidth = textBlockWidth();
+ if (desiredWidth != innerTextRenderer->width())
+ relayoutChildren = true;
+ innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed));
+
+ if (m_innerBlock) {
+ int innerBlockWidth = width() - borderAndPaddingWidth();
+ if (innerBlockWidth != innerBlockRenderer->width())
+ relayoutChildren = true;
+ innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed));
+ }
+
+ RenderBlock::layoutBlock(relayoutChildren);
+
+ // Center the child block vertically
+ RenderBox* childBlock = innerBlockRenderer ? innerBlockRenderer : innerTextRenderer;
+ currentHeight = childBlock->height();
+ if (currentHeight < height())
+ childBlock->setY((height() - currentHeight) / 2);
+
+ // Ignores the paddings for the inner spin button.
+ if (RenderBox* spinBox = m_innerSpinButton ? m_innerSpinButton->renderBox() : 0) {
+ spinBox->setLocation(spinBox->x() + paddingRight(), borderTop());
+ spinBox->setHeight(height() - borderTop() - borderBottom());
+ }
+
+#if ENABLE(INPUT_SPEECH)
+ if (RenderBox* button = m_speechButton ? m_speechButton->renderBox() : 0) {
+ if (m_innerBlock) {
+ // This is mostly the case where this is a search field. The speech button is a sibling
+ // of the inner block and laid out at the far right.
+ int x = width() - borderAndPaddingWidth() - button->width() - button->borderAndPaddingWidth();
+ int y = (height() - button->height()) / 2;
+ button->setLocation(x, y);
+ } else {
+ // For non-search fields which are simpler and we let the defaut layout handle things
+ // except for small tweaking below.
+ button->setLocation(button->x() + paddingRight(), (height() - button->height()) / 2);
+ }
+ }
+#endif
+
+ // Center the spin button vertically, and move it to the right by
+ // padding + border of the text fields.
+ if (RenderBox* spinBox = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) {
+ int diff = height() - spinBox->height();
+ // If the diff is odd, the top area over the spin button takes the
+ // remaining one pixel. It's good for Mac NSStepper because it has
+ // shadow at the bottom.
+ int y = (diff / 2) + (diff % 2);
+ int x = width() - borderRight() - paddingRight() - spinBox->width();
+ spinBox->setLocation(x, y);
+ }
+}
+
+bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction hitTestAction)
+{
+ // If we're within the text control, we want to act as if we've hit the inner text block element, in case the point
+ // was on the control but not on the inner element (see Radar 4617841).
+
+ // In a search field, we want to act as if we've hit the results block if we're to the left of the inner text block,
+ // and act as if we've hit the close block if we're to the right of the inner text block.
+
+ if (!RenderTextControl::nodeAtPoint(request, result, xPos, yPos, tx, ty, hitTestAction))
+ return false;
+
+ // If we hit a node inside the inner text element, say that we hit that element,
+ // and if we hit our node (e.g. we're over the border or padding), also say that we hit the
+ // inner text element so that it gains focus.
+ if (result.innerNode()->isDescendantOf(innerTextElement()) || result.innerNode() == node())
+ hitInnerTextElement(result, xPos, yPos, tx, ty);
+
+ // If we found a spin button, we're done.
+ if (m_innerSpinButton && result.innerNode() == m_innerSpinButton)
+ return true;
+ if (m_outerSpinButton && result.innerNode() == m_outerSpinButton)
+ return true;
+#if ENABLE(INPUT_SPEECH)
+ if (m_speechButton && result.innerNode() == m_speechButton)
+ return true;
+#endif
+ // If we're not a search field, or we already found the speech, results or cancel buttons, we're done.
+ if (!m_innerBlock || result.innerNode() == m_resultsButton || result.innerNode() == m_cancelButton)
+ return true;
+
+ Node* innerNode = 0;
+ RenderBox* innerBlockRenderer = m_innerBlock->renderBox();
+ RenderBox* innerTextRenderer = innerTextElement()->renderBox();
+
+ IntPoint localPoint = result.localPoint();
+ localPoint.move(-innerBlockRenderer->x(), -innerBlockRenderer->y());
+
+ int textLeft = tx + x() + innerBlockRenderer->x() + innerTextRenderer->x();
+ if (m_resultsButton && m_resultsButton->renderer() && xPos < textLeft)
+ innerNode = m_resultsButton.get();
+
+ if (!innerNode) {
+ int textRight = textLeft + innerTextRenderer->width();
+ if (m_cancelButton && m_cancelButton->renderer() && xPos > textRight)
+ innerNode = m_cancelButton.get();
+ }
+
+ if (innerNode) {
+ result.setInnerNode(innerNode);
+ localPoint.move(-innerNode->renderBox()->x(), -innerNode->renderBox()->y());
+ }
+
+ result.setLocalPoint(localPoint);
+ return true;
+}
+
+void RenderTextControlSingleLine::forwardEvent(Event* event)
+{
+ RenderBox* innerTextRenderer = innerTextElement()->renderBox();
+
+ if (event->type() == eventNames().blurEvent) {
+ if (innerTextRenderer) {
+ if (RenderLayer* innerLayer = innerTextRenderer->layer())
+ innerLayer->scrollToOffset(!style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0);
+ }
+
+ capsLockStateMayHaveChanged();
+ } else if (event->type() == eventNames().focusEvent)
+ capsLockStateMayHaveChanged();
+
+ if (!event->isMouseEvent()) {
+ RenderTextControl::forwardEvent(event);
+ return;
+ }
+
+#if ENABLE(INPUT_SPEECH)
+ if (RenderBox* speechBox = m_speechButton ? m_speechButton->renderBox() : 0) {
+ FloatPoint pointInTextControlCoords = absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true);
+ if (speechBox->frameRect().contains(roundedIntPoint(pointInTextControlCoords))) {
+ m_speechButton->defaultEventHandler(event);
+ return;
+ }
+ }
+#endif
+
+ FloatPoint localPoint = innerTextRenderer->absoluteToLocal(static_cast<MouseEvent*>(event)->absoluteLocation(), false, true);
+ int textRight = innerTextRenderer->borderBoxRect().right();
+
+ if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBoxRect().x())
+ m_resultsButton->defaultEventHandler(event);
+ else if (m_cancelButton && localPoint.x() > textRight)
+ m_cancelButton->defaultEventHandler(event);
+ else if (m_innerSpinButton && localPoint.x() > textRight && m_innerSpinButton->renderBox() && localPoint.x() < textRight + m_innerSpinButton->renderBox()->width())
+ m_innerSpinButton->defaultEventHandler(event);
+ else if (m_outerSpinButton && localPoint.x() > textRight)
+ m_outerSpinButton->defaultEventHandler(event);
+ else
+ RenderTextControl::forwardEvent(event);
+}
+
+void RenderTextControlSingleLine::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderTextControl::styleDidChange(diff, oldStyle);
+
+ if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) {
+ // We may have set the width and the height in the old style in layout().
+ // Reset them now to avoid getting a spurious layout hint.
+ innerBlockRenderer->style()->setHeight(Length());
+ innerBlockRenderer->style()->setWidth(Length());
+ innerBlockRenderer->setStyle(createInnerBlockStyle(style()));
+ }
+
+ if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0)
+ resultsRenderer->setStyle(createResultsButtonStyle(style()));
+
+ if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0)
+ cancelRenderer->setStyle(createCancelButtonStyle(style()));
+
+ if (RenderObject* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderer() : 0)
+ spinRenderer->setStyle(createOuterSpinButtonStyle());
+
+#if ENABLE(INPUT_SPEECH)
+ if (RenderObject* speechRenderer = m_speechButton ? m_speechButton->renderer() : 0)
+ speechRenderer->setStyle(createSpeechButtonStyle());
+#endif
+
+ setHasOverflowClip(false);
+}
+
+void RenderTextControlSingleLine::capsLockStateMayHaveChanged()
+{
+ if (!node() || !document())
+ return;
+
+ // Only draw the caps lock indicator if these things are true:
+ // 1) The field is a password field
+ // 2) The frame is active
+ // 3) The element is focused
+ // 4) The caps lock is on
+ bool shouldDrawCapsLockIndicator = false;
+
+ if (Frame* frame = document()->frame())
+ shouldDrawCapsLockIndicator = inputElement()->isPasswordField()
+ && frame->selection()->isFocusedAndActive()
+ && document()->focusedNode() == node()
+ && PlatformKeyboardEvent::currentCapsLockState();
+
+ if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) {
+ m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator;
+ repaint();
+ }
+}
+
+bool RenderTextControlSingleLine::hasControlClip() const
+{
+ bool clip = m_cancelButton;
+ return clip;
+}
+
+IntRect RenderTextControlSingleLine::controlClipRect(int tx, int ty) const
+{
+ // This should only get called for search & speech inputs.
+ ASSERT(hasControlClip());
+
+ IntRect clipRect = IntRect(m_innerBlock->renderBox()->frameRect());
+ clipRect.move(tx, ty);
+ return clipRect;
+}
+
+int RenderTextControlSingleLine::textBlockWidth() const
+{
+ int width = RenderTextControl::textBlockWidth();
+
+ if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
+ resultsRenderer->computeLogicalWidth();
+ width -= resultsRenderer->width() + resultsRenderer->marginLeft() + resultsRenderer->marginRight();
+ }
+
+ if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
+ cancelRenderer->computeLogicalWidth();
+ width -= cancelRenderer->width() + cancelRenderer->marginLeft() + cancelRenderer->marginRight();
+ }
+
+ if (RenderBox* spinRenderer = m_innerSpinButton ? m_innerSpinButton->renderBox() : 0) {
+ spinRenderer->computeLogicalWidth();
+ width -= spinRenderer->width() + spinRenderer->marginLeft() + spinRenderer->marginRight();
+ }
+
+#if ENABLE(INPUT_SPEECH)
+ if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) {
+ speechRenderer->computeLogicalWidth();
+ width -= speechRenderer->width() + speechRenderer->marginLeft() + speechRenderer->marginRight();
+ }
+#endif
+
+ return width - decorationWidthRight();
+}
+
+int RenderTextControlSingleLine::decorationWidthRight() const
+{
+ int width = 0;
+ if (RenderBox* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) {
+ spinRenderer->computeLogicalWidth();
+ width += spinRenderer->width() + spinRenderer->marginLeft() + spinRenderer->marginRight();
+ }
+ if (width > 0)
+ width += paddingRight() + borderRight();
+ return width;
+}
+
+float RenderTextControlSingleLine::getAvgCharWidth(AtomicString family)
+{
+ // Since Lucida Grande is the default font, we want this to match the width
+ // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
+ // IE for some encodings (in IE, the default font is encoding specific).
+ // 901 is the avgCharWidth value in the OS/2 table for MS Shell Dlg.
+ if (family == AtomicString("Lucida Grande"))
+ return scaleEmToUnits(901);
+
+ return RenderTextControl::getAvgCharWidth(family);
+}
+
+int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const
+{
+ int factor = inputElement()->size();
+ if (factor <= 0)
+ factor = 20;
+
+ int result = static_cast<int>(ceilf(charWidth * factor));
+
+ float maxCharWidth = 0.f;
+ AtomicString family = style()->font().family().family();
+ // Since Lucida Grande is the default font, we want this to match the width
+ // of MS Shell Dlg, the default font for textareas in Firefox, Safari Win and
+ // IE for some encodings (in IE, the default font is encoding specific).
+ // 4027 is the (xMax - xMin) value in the "head" font table for MS Shell Dlg.
+ if (family == AtomicString("Lucida Grande"))
+ maxCharWidth = scaleEmToUnits(4027);
+ else if (hasValidAvgCharWidth(family))
+ maxCharWidth = roundf(style()->font().primaryFont()->maxCharWidth());
+
+ // For text inputs, IE adds some extra width.
+ if (maxCharWidth > 0.f)
+ result += maxCharWidth - charWidth;
+
+ if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
+ result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() +
+ resultsRenderer->paddingLeft() + resultsRenderer->paddingRight();
+
+ if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
+ result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() +
+ cancelRenderer->paddingLeft() + cancelRenderer->paddingRight();
+
+ if (RenderBox* spinRenderer = m_innerSpinButton ? m_innerSpinButton->renderBox() : 0)
+ result += spinRenderer->minPreferredLogicalWidth();
+
+#if ENABLE(INPUT_SPEECH)
+ if (RenderBox* speechRenderer = m_speechButton ? m_speechButton->renderBox() : 0) {
+ result += speechRenderer->borderLeft() + speechRenderer->borderRight() +
+ speechRenderer->paddingLeft() + speechRenderer->paddingRight();
+ }
+#endif
+ return result;
+}
+
+int RenderTextControlSingleLine::preferredDecorationWidthRight() const
+{
+ int width = 0;
+ if (RenderBox* spinRenderer = m_outerSpinButton ? m_outerSpinButton->renderBox() : 0) {
+ spinRenderer->computeLogicalWidth();
+ width += spinRenderer->minPreferredLogicalWidth() + spinRenderer->marginLeft() + spinRenderer->marginRight();
+ }
+ if (width > 0)
+ width += paddingRight() + borderRight();
+ return width;
+}
+
+void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight)
+{
+ if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0) {
+ resultsRenderer->computeLogicalHeight();
+ setHeight(max(height(),
+ resultsRenderer->borderTop() + resultsRenderer->borderBottom() +
+ resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() +
+ resultsRenderer->marginTop() + resultsRenderer->marginBottom()));
+ lineHeight = max(lineHeight, resultsRenderer->height());
+ }
+ if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0) {
+ cancelRenderer->computeLogicalHeight();
+ setHeight(max(height(),
+ cancelRenderer->borderTop() + cancelRenderer->borderBottom() +
+ cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() +
+ cancelRenderer->marginTop() + cancelRenderer->marginBottom()));
+ lineHeight = max(lineHeight, cancelRenderer->height());
+ }
+
+ setHeight(height() + lineHeight);
+}
+
+void RenderTextControlSingleLine::createSubtreeIfNeeded()
+{
+ bool createSubtree = inputElement()->isSearchField();
+ if (!createSubtree) {
+ RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
+#if ENABLE(INPUT_SPEECH)
+ if (inputElement()->isSpeechEnabled() && !m_speechButton) {
+ // Create the speech button element.
+ m_speechButton = InputFieldSpeechButtonElement::create(static_cast<HTMLElement*>(node()));
+ m_speechButton->attachInnerElement(node(), createSpeechButtonStyle(), renderArena());
+ }
+#endif
+ bool hasSpinButton = inputElement()->hasSpinButton();
+ if (hasSpinButton && !m_innerSpinButton) {
+ m_innerSpinButton = SpinButtonElement::create(static_cast<HTMLElement*>(node()));
+ m_innerSpinButton->attachInnerElement(node(), createInnerSpinButtonStyle(), renderArena());
+ }
+ if (hasSpinButton && !m_outerSpinButton) {
+ m_outerSpinButton = SpinButtonElement::create(static_cast<HTMLElement*>(node()));
+ m_outerSpinButton->attachInnerElement(node(), createOuterSpinButtonStyle(), renderArena());
+ }
+ return;
+ }
+
+ if (!m_innerBlock) {
+ // Create the inner block element
+ m_innerBlock = TextControlInnerElement::create(static_cast<HTMLElement*>(node()));
+ m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena());
+ }
+#if ENABLE(INPUT_SPEECH)
+ if (inputElement()->isSpeechEnabled() && !m_speechButton) {
+ // Create the speech button element.
+ m_speechButton = InputFieldSpeechButtonElement::create(static_cast<HTMLElement*>(node()));
+ m_speechButton->attachInnerElement(node(), createSpeechButtonStyle(), renderArena());
+ }
+#endif
+ if (inputElement()->hasSpinButton() && !m_outerSpinButton) {
+ m_outerSpinButton = SpinButtonElement::create(static_cast<HTMLElement*>(node()));
+ m_outerSpinButton->attachInnerElement(node(), createOuterSpinButtonStyle(), renderArena());
+ }
+
+ if (inputElement()->isSearchField()) {
+ if (!m_resultsButton) {
+ // Create the search results button element.
+ m_resultsButton = SearchFieldResultsButtonElement::create(document());
+ m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena());
+ }
+ }
+
+ // Create innerText element before adding the other buttons.
+ RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get());
+
+ if (inputElement()->isSearchField()) {
+ if (!m_cancelButton) {
+ // Create the cancel button element.
+ m_cancelButton = SearchFieldCancelButtonElement::create(document());
+ m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena());
+ }
+ }
+}
+
+#if ENABLE(INPUT_SPEECH)
+void RenderTextControlSingleLine::speechAttributeChanged()
+{
+ // The inner text element of this renderer has different styles depending on whether the
+ // speech button is visible or not. So when the speech attribute changes, we reset the
+ // whole thing and recreate to get the right styles and layout.
+ if (m_speechButton)
+ m_speechButton->detach();
+ setChildrenInline(true);
+ RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style();
+ setStyle(createInnerTextStyle(parentStyle));
+ updateFromElement();
+}
+#endif
+
+void RenderTextControlSingleLine::updateFromElement()
+{
+ createSubtreeIfNeeded();
+ RenderTextControl::updateFromElement();
+
+ if (m_cancelButton)
+ updateCancelButtonVisibility();
+
+ if (!inputElement()->suggestedValue().isNull())
+ setInnerTextValue(inputElement()->suggestedValue());
+ else {
+ if (node()->hasTagName(inputTag)) {
+ // For HTMLInputElement, update the renderer value if the formControlValueMatchesRenderer()
+ // flag is false. It protects an unacceptable renderer value from
+ // being overwritten with the DOM value.
+ if (!static_cast<HTMLInputElement*>(node())->formControlValueMatchesRenderer())
+ setInnerTextValue(inputElement()->value());
+ }
+ }
+
+ if (m_searchPopupIsVisible)
+ m_searchPopup->popupMenu()->updateFromElement();
+}
+
+void RenderTextControlSingleLine::cacheSelection(int start, int end)
+{
+ inputElement()->cacheSelection(start, end);
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const
+{
+ RefPtr<RenderStyle> textBlockStyle = RenderStyle::create();
+ textBlockStyle->inheritFrom(startStyle);
+ adjustInnerTextStyle(startStyle, textBlockStyle.get());
+
+ textBlockStyle->setWhiteSpace(PRE);
+ textBlockStyle->setWordWrap(NormalWordWrap);
+ textBlockStyle->setOverflowX(OHIDDEN);
+ textBlockStyle->setOverflowY(OHIDDEN);
+
+ // Do not allow line-height to be smaller than our default.
+ if (textBlockStyle->font().lineSpacing() > lineHeight(true, HorizontalLine, PositionOfInteriorLineBoxes))
+ textBlockStyle->setLineHeight(Length(-100.0f, Percent));
+
+ WebCore::EDisplay display = (m_innerBlock || inputElement()->hasSpinButton() ? INLINE_BLOCK : BLOCK);
+#if ENABLE(INPUT_SPEECH)
+ if (inputElement()->isSpeechEnabled())
+ display = INLINE_BLOCK;
+#endif
+ textBlockStyle->setDisplay(display);
+
+ // We're adding one extra pixel of padding to match WinIE.
+ textBlockStyle->setPaddingLeft(Length(1, Fixed));
+ textBlockStyle->setPaddingRight(Length(1, Fixed));
+
+ return textBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const
+{
+ ASSERT(node()->isHTMLElement());
+
+ RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create();
+ innerBlockStyle->inheritFrom(startStyle);
+
+ innerBlockStyle->setDisplay(inputElement()->hasSpinButton() ? INLINE_BLOCK : BLOCK);
+ innerBlockStyle->setDirection(LTR);
+
+ // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable.
+ innerBlockStyle->setUserModify(READ_ONLY);
+
+ return innerBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const
+{
+ ASSERT(node()->isHTMLElement());
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+
+ RefPtr<RenderStyle> resultsBlockStyle;
+ if (input->maxResults() < 0)
+ resultsBlockStyle = getCachedPseudoStyle(SEARCH_DECORATION);
+ else if (!input->maxResults())
+ resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_DECORATION);
+ else
+ resultsBlockStyle = getCachedPseudoStyle(SEARCH_RESULTS_BUTTON);
+
+ if (!resultsBlockStyle)
+ resultsBlockStyle = RenderStyle::create();
+
+ if (startStyle)
+ resultsBlockStyle->inheritFrom(startStyle);
+
+ return resultsBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const
+{
+ ASSERT(node()->isHTMLElement());
+ RefPtr<RenderStyle> cancelBlockStyle;
+
+ if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(SEARCH_CANCEL_BUTTON))
+ // We may be sharing style with another search field, but we must not share the cancel button style.
+ cancelBlockStyle = RenderStyle::clone(pseudoStyle.get());
+ else
+ cancelBlockStyle = RenderStyle::create();
+
+ if (startStyle)
+ cancelBlockStyle->inheritFrom(startStyle);
+
+ cancelBlockStyle->setVisibility(visibilityForCancelButton());
+ return cancelBlockStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerSpinButtonStyle() const
+{
+ ASSERT(node()->isHTMLElement());
+ RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(INNER_SPIN_BUTTON);
+ if (!buttonStyle)
+ buttonStyle = RenderStyle::create();
+ buttonStyle->inheritFrom(style());
+ return buttonStyle.release();
+}
+
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createOuterSpinButtonStyle() const
+{
+ ASSERT(node()->isHTMLElement());
+ RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(OUTER_SPIN_BUTTON);
+ if (!buttonStyle)
+ buttonStyle = RenderStyle::create();
+ buttonStyle->inheritFrom(style());
+ return buttonStyle.release();
+}
+
+#if ENABLE(INPUT_SPEECH)
+PassRefPtr<RenderStyle> RenderTextControlSingleLine::createSpeechButtonStyle() const
+{
+ ASSERT(node()->isHTMLElement());
+ RefPtr<RenderStyle> buttonStyle = getCachedPseudoStyle(INPUT_SPEECH_BUTTON);
+ if (!buttonStyle)
+ buttonStyle = RenderStyle::create();
+ buttonStyle->inheritFrom(style());
+ return buttonStyle.release();
+}
+#endif
+
+void RenderTextControlSingleLine::updateCancelButtonVisibility() const
+{
+ if (!m_cancelButton->renderer())
+ return;
+
+ const RenderStyle* curStyle = m_cancelButton->renderer()->style();
+ EVisibility buttonVisibility = visibilityForCancelButton();
+ if (curStyle->visibility() == buttonVisibility)
+ return;
+
+ RefPtr<RenderStyle> cancelButtonStyle = RenderStyle::clone(curStyle);
+ cancelButtonStyle->setVisibility(buttonVisibility);
+ m_cancelButton->renderer()->setStyle(cancelButtonStyle);
+}
+
+EVisibility RenderTextControlSingleLine::visibilityForCancelButton() const
+{
+ ASSERT(node()->isHTMLElement());
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ return input->value().isEmpty() ? HIDDEN : VISIBLE;
+}
+
+const AtomicString& RenderTextControlSingleLine::autosaveName() const
+{
+ return static_cast<Element*>(node())->getAttribute(autosaveAttr);
+}
+
+void RenderTextControlSingleLine::startSearchEventTimer()
+{
+ ASSERT(node()->isHTMLElement());
+ unsigned length = text().length();
+
+ // If there's no text, fire the event right away.
+ if (!length) {
+ stopSearchEventTimer();
+ static_cast<HTMLInputElement*>(node())->onSearch();
+ return;
+ }
+
+ // After typing the first key, we wait 0.5 seconds.
+ // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on.
+ m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length));
+}
+
+void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*)
+{
+ ASSERT(node()->isHTMLElement());
+ static_cast<HTMLInputElement*>(node())->onSearch();
+}
+
+// PopupMenuClient methods
+void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents)
+{
+ ASSERT(node()->isHTMLElement());
+ ASSERT(static_cast<int>(listIndex) < listSize());
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
+ if (static_cast<int>(listIndex) == (listSize() - 1)) {
+ if (fireEvents) {
+ m_recentSearches.clear();
+ const AtomicString& name = autosaveName();
+ if (!name.isEmpty()) {
+ if (!m_searchPopup)
+ m_searchPopup = document()->page()->chrome()->createSearchPopupMenu(this);
+ m_searchPopup->saveRecentSearches(name, m_recentSearches);
+ }
+ }
+ } else {
+ input->setValue(itemText(listIndex));
+ if (fireEvents)
+ input->onSearch();
+ input->select();
+ }
+}
+
+String RenderTextControlSingleLine::itemText(unsigned listIndex) const
+{
+ int size = listSize();
+ if (size == 1) {
+ ASSERT(!listIndex);
+ return searchMenuNoRecentSearchesText();
+ }
+ if (!listIndex)
+ return searchMenuRecentSearchesText();
+ if (itemIsSeparator(listIndex))
+ return String();
+ if (static_cast<int>(listIndex) == (size - 1))
+ return searchMenuClearRecentSearchesText();
+ return m_recentSearches[listIndex - 1];
+}
+
+String RenderTextControlSingleLine::itemLabel(unsigned) const
+{
+ return String();
+}
+
+String RenderTextControlSingleLine::itemIcon(unsigned) const
+{
+ return String();
+}
+
+bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const
+{
+ if (!listIndex || itemIsSeparator(listIndex))
+ return false;
+ return true;
+}
+
+PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const
+{
+ return menuStyle();
+}
+
+PopupMenuStyle RenderTextControlSingleLine::menuStyle() const
+{
+ return PopupMenuStyle(style()->visitedDependentColor(CSSPropertyColor), style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->font(), style()->visibility() == VISIBLE, style()->display() == NONE, style()->textIndent(), style()->direction());
+}
+
+int RenderTextControlSingleLine::clientInsetLeft() const
+{
+ // Inset the menu by the radius of the cap on the left so that
+ // it only runs along the straight part of the bezel.
+ return height() / 2;
+}
+
+int RenderTextControlSingleLine::clientInsetRight() const
+{
+ // Inset the menu by the radius of the cap on the right so that
+ // it only runs along the straight part of the bezel (unless it needs
+ // to be wider).
+ return height() / 2;
+}
+
+int RenderTextControlSingleLine::clientPaddingLeft() const
+{
+ int padding = paddingLeft();
+
+ if (RenderBox* resultsRenderer = m_resultsButton ? m_resultsButton->renderBox() : 0)
+ padding += resultsRenderer->width();
+
+ return padding;
+}
+
+int RenderTextControlSingleLine::clientPaddingRight() const
+{
+ int padding = paddingRight();
+
+ if (RenderBox* cancelRenderer = m_cancelButton ? m_cancelButton->renderBox() : 0)
+ padding += cancelRenderer->width();
+
+ return padding;
+}
+
+int RenderTextControlSingleLine::listSize() const
+{
+ // If there are no recent searches, then our menu will have 1 "No recent searches" item.
+ if (!m_recentSearches.size())
+ return 1;
+ // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item.
+ return m_recentSearches.size() + 3;
+}
+
+int RenderTextControlSingleLine::selectedIndex() const
+{
+ return -1;
+}
+
+void RenderTextControlSingleLine::popupDidHide()
+{
+ m_searchPopupIsVisible = false;
+}
+
+bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const
+{
+ // The separator will be the second to last item in our list.
+ return static_cast<int>(listIndex) == (listSize() - 2);
+}
+
+bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const
+{
+ return listIndex == 0;
+}
+
+bool RenderTextControlSingleLine::itemIsSelected(unsigned) const
+{
+ return false;
+}
+
+void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex)
+{
+ ASSERT(node()->isHTMLElement());
+ static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex));
+}
+
+FontSelector* RenderTextControlSingleLine::fontSelector() const
+{
+ return document()->styleSelector()->fontSelector();
+}
+
+HostWindow* RenderTextControlSingleLine::hostWindow() const
+{
+ return document()->view()->hostWindow();
+}
+
+void RenderTextControlSingleLine::autoscroll()
+{
+ RenderLayer* layer = innerTextElement()->renderBox()->layer();
+ if (layer)
+ layer->autoscroll();
+}
+
+int RenderTextControlSingleLine::scrollWidth() const
+{
+ if (innerTextElement())
+ return innerTextElement()->scrollWidth();
+ return RenderBlock::scrollWidth();
+}
+
+int RenderTextControlSingleLine::scrollHeight() const
+{
+ if (innerTextElement())
+ return innerTextElement()->scrollHeight();
+ return RenderBlock::scrollHeight();
+}
+
+int RenderTextControlSingleLine::scrollLeft() const
+{
+ if (innerTextElement())
+ return innerTextElement()->scrollLeft();
+ return RenderBlock::scrollLeft();
+}
+
+int RenderTextControlSingleLine::scrollTop() const
+{
+ if (innerTextElement())
+ return innerTextElement()->scrollTop();
+ return RenderBlock::scrollTop();
+}
+
+void RenderTextControlSingleLine::setScrollLeft(int newLeft)
+{
+ if (innerTextElement())
+ innerTextElement()->setScrollLeft(newLeft);
+}
+
+void RenderTextControlSingleLine::setScrollTop(int newTop)
+{
+ if (innerTextElement())
+ innerTextElement()->setScrollTop(newTop);
+}
+
+bool RenderTextControlSingleLine::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
+{
+ RenderLayer* layer = innerTextElement()->renderBox()->layer();
+ if (layer && layer->scroll(direction, granularity, multiplier))
+ return true;
+ return RenderBlock::scroll(direction, granularity, multiplier, stopNode);
+}
+
+bool RenderTextControlSingleLine::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
+{
+ RenderLayer* layer = innerTextElement()->renderBox()->layer();
+ if (layer && layer->scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier))
+ return true;
+ return RenderBlock::logicalScroll(direction, granularity, multiplier, stopNode);
+}
+
+PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
+{
+ RefPtr<Scrollbar> widget;
+ bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
+ if (hasCustomScrollbarStyle)
+ widget = RenderScrollbar::createCustomScrollbar(client, orientation, this);
+ else
+ widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize);
+ return widget.release();
+}
+
+InputElement* RenderTextControlSingleLine::inputElement() const
+{
+ return toInputElement(static_cast<Element*>(node()));
+}
+
+int RenderTextControlSingleLine::textBlockInsetLeft() const
+{
+ int inset = borderLeft() + clientPaddingLeft();
+ if (HTMLElement* innerText = innerTextElement()) {
+ if (RenderBox* innerTextRenderer = innerText->renderBox())
+ inset += innerTextRenderer->paddingLeft();
+ }
+ return inset;
+}
+
+int RenderTextControlSingleLine::textBlockInsetRight() const
+{
+ int inset = borderRight() + clientPaddingRight();
+ if (HTMLElement* innerText = innerTextElement()) {
+ if (RenderBox* innerTextRenderer = innerText->renderBox())
+ inset += innerTextRenderer->paddingRight();
+ }
+ return inset;
+}
+
+int RenderTextControlSingleLine::textBlockInsetTop() const
+{
+ RenderBox* innerRenderer = 0;
+ if (m_innerBlock)
+ innerRenderer = m_innerBlock->renderBox();
+ else if (HTMLElement* innerText = innerTextElement())
+ innerRenderer = innerText->renderBox();
+
+ if (innerRenderer)
+ return innerRenderer->y();
+
+ return borderTop() + paddingTop();
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderTextControlSingleLine.h b/Source/WebCore/rendering/RenderTextControlSingleLine.h
new file mode 100644
index 0000000..51509c0
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextControlSingleLine.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ * 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 RenderTextControlSingleLine_h
+#define RenderTextControlSingleLine_h
+
+#include "PopupMenuClient.h"
+#include "RenderTextControl.h"
+#include "SearchPopupMenu.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+class InputElement;
+class InputFieldSpeechButtonElement;
+class SearchFieldCancelButtonElement;
+class SearchFieldResultsButtonElement;
+class SpinButtonElement;
+class TextControlInnerElement;
+
+class RenderTextControlSingleLine : public RenderTextControl, private PopupMenuClient {
+public:
+ RenderTextControlSingleLine(Node*, bool);
+ virtual ~RenderTextControlSingleLine();
+
+ bool placeholderIsVisible() const { return m_placeholderVisible; }
+ bool placeholderShouldBeVisible() const;
+
+ void addSearchResult();
+ void stopSearchEventTimer();
+
+ bool popupIsVisible() const { return m_searchPopupIsVisible; }
+ void showPopup();
+ void hidePopup();
+
+ void forwardEvent(Event*);
+
+ void capsLockStateMayHaveChanged();
+
+ // Decoration width outside of the text field.
+ int decorationWidthRight() const;
+
+#if ENABLE(INPUT_SPEECH)
+ void speechAttributeChanged();
+#endif
+
+private:
+ int preferredDecorationWidthRight() const;
+ virtual bool hasControlClip() const;
+ virtual IntRect controlClipRect(int tx, int ty) const;
+ virtual bool isTextField() const { return true; }
+
+ virtual void subtreeHasChanged();
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual void paintBoxDecorations(PaintInfo&, int tx, int ty);
+ virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
+ virtual void layout();
+
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+
+ virtual void autoscroll();
+
+ // Subclassed to forward to our inner div.
+ 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 scroll(ScrollDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0);
+ virtual bool logicalScroll(ScrollLogicalDirection, ScrollGranularity, float multiplier = 1, Node** stopNode = 0);
+
+ int textBlockWidth() const;
+ virtual float getAvgCharWidth(AtomicString family);
+ virtual int preferredContentWidth(float charWidth) const;
+ virtual void adjustControlHeightBasedOnLineHeight(int lineHeight);
+
+ void createSubtreeIfNeeded();
+ virtual void updateFromElement();
+ virtual void cacheSelection(int start, int end);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+ virtual RenderStyle* textBaseStyle() const;
+ virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const;
+ PassRefPtr<RenderStyle> createInnerBlockStyle(const RenderStyle* startStyle) const;
+ PassRefPtr<RenderStyle> createResultsButtonStyle(const RenderStyle* startStyle) const;
+ PassRefPtr<RenderStyle> createCancelButtonStyle(const RenderStyle* startStyle) const;
+ PassRefPtr<RenderStyle> createInnerSpinButtonStyle() const;
+ PassRefPtr<RenderStyle> createOuterSpinButtonStyle() const;
+#if ENABLE(INPUT_SPEECH)
+ PassRefPtr<RenderStyle> createSpeechButtonStyle() const;
+#endif
+
+ void updateCancelButtonVisibility() const;
+ EVisibility visibilityForCancelButton() const;
+ const AtomicString& autosaveName() const;
+
+ void startSearchEventTimer();
+ void searchEventTimerFired(Timer<RenderTextControlSingleLine>*);
+
+ // PopupMenuClient methods
+ virtual void valueChanged(unsigned listIndex, bool fireEvents = true);
+ virtual void selectionChanged(unsigned, bool) {}
+ virtual void selectionCleared() {}
+ virtual String itemText(unsigned listIndex) const;
+ virtual String itemLabel(unsigned listIndex) const;
+ virtual String itemIcon(unsigned listIndex) const;
+ virtual String itemToolTip(unsigned) const { return String(); }
+ virtual String itemAccessibilityText(unsigned) const { return String(); }
+ virtual bool itemIsEnabled(unsigned listIndex) const;
+ virtual PopupMenuStyle itemStyle(unsigned listIndex) const;
+ virtual PopupMenuStyle menuStyle() const;
+ virtual int clientInsetLeft() const;
+ virtual int clientInsetRight() const;
+ virtual int clientPaddingLeft() const;
+ virtual int clientPaddingRight() const;
+ virtual int listSize() const;
+ virtual int selectedIndex() const;
+ virtual void popupDidHide();
+ virtual bool itemIsSeparator(unsigned listIndex) const;
+ virtual bool itemIsLabel(unsigned listIndex) const;
+ virtual bool itemIsSelected(unsigned listIndex) const;
+ virtual bool shouldPopOver() const { return false; }
+ virtual bool valueShouldChangeOnHotTrack() const { return false; }
+ virtual void setTextFromItem(unsigned listIndex);
+ virtual FontSelector* fontSelector() const;
+ virtual HostWindow* hostWindow() const;
+ virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize);
+
+ InputElement* inputElement() const;
+
+ virtual int textBlockInsetLeft() const;
+ virtual int textBlockInsetRight() const;
+ virtual int textBlockInsetTop() const;
+
+ bool m_searchPopupIsVisible;
+ bool m_shouldDrawCapsLockIndicator;
+
+ RefPtr<TextControlInnerElement> m_innerBlock;
+ RefPtr<SearchFieldResultsButtonElement> m_resultsButton;
+ RefPtr<SearchFieldCancelButtonElement> m_cancelButton;
+ RefPtr<TextControlInnerElement> m_innerSpinButton;
+ RefPtr<TextControlInnerElement> m_outerSpinButton;
+#if ENABLE(INPUT_SPEECH)
+ RefPtr<InputFieldSpeechButtonElement> m_speechButton;
+#endif
+
+ Timer<RenderTextControlSingleLine> m_searchEventTimer;
+ RefPtr<SearchPopupMenu> m_searchPopup;
+ Vector<String> m_recentSearches;
+};
+
+inline RenderTextControlSingleLine* toRenderTextControlSingleLine(RenderObject* object)
+{
+ ASSERT(!object || object->isTextField());
+ return static_cast<RenderTextControlSingleLine*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTextControlSingleLine(const RenderTextControlSingleLine*);
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderTextFragment.cpp b/Source/WebCore/rendering/RenderTextFragment.cpp
new file mode 100644
index 0000000..ec62d85
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextFragment.cpp
@@ -0,0 +1,117 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 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 "RenderTextFragment.h"
+
+#include "RenderBlock.h"
+#include "Text.h"
+
+namespace WebCore {
+
+RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str, int startOffset, int length)
+ : RenderText(node, str ? str->substring(startOffset, length) : 0)
+ , m_start(startOffset)
+ , m_end(length)
+ , m_firstLetter(0)
+{
+}
+
+RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str)
+ : RenderText(node, str)
+ , m_start(0)
+ , m_end(str ? str->length() : 0)
+ , m_contentString(str)
+ , m_firstLetter(0)
+{
+}
+
+RenderTextFragment::~RenderTextFragment()
+{
+}
+
+PassRefPtr<StringImpl> RenderTextFragment::originalText() const
+{
+ Node* e = node();
+ RefPtr<StringImpl> result = ((e && e->isTextNode()) ? static_cast<Text*>(e)->dataImpl() : contentString());
+ if (!result)
+ return 0;
+ return result->substring(start(), end());
+}
+
+void RenderTextFragment::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderText::styleDidChange(diff, oldStyle);
+
+ if (RenderBlock* block = blockForAccompanyingFirstLetter()) {
+ block->style()->removeCachedPseudoStyle(FIRST_LETTER);
+ block->updateFirstLetter();
+ }
+}
+
+void RenderTextFragment::destroy()
+{
+ if (m_firstLetter)
+ m_firstLetter->destroy();
+ RenderText::destroy();
+}
+
+void RenderTextFragment::setTextInternal(PassRefPtr<StringImpl> text)
+{
+ RenderText::setTextInternal(text);
+ if (m_firstLetter) {
+ ASSERT(!m_contentString);
+ m_firstLetter->destroy();
+ m_firstLetter = 0;
+ m_start = 0;
+ m_end = textLength();
+ if (Node* t = node()) {
+ ASSERT(!t->renderer());
+ t->setRenderer(this);
+ }
+ }
+}
+
+UChar RenderTextFragment::previousCharacter() const
+{
+ if (start()) {
+ Node* e = node();
+ StringImpl* original = ((e && e->isTextNode()) ? static_cast<Text*>(e)->dataImpl() : contentString());
+ if (original && start() <= original->length())
+ return (*original)[start() - 1];
+ }
+
+ return RenderText::previousCharacter();
+}
+
+RenderBlock* RenderTextFragment::blockForAccompanyingFirstLetter() const
+{
+ if (!m_firstLetter)
+ return 0;
+ for (RenderObject* block = m_firstLetter->parent(); block; block = block->parent()) {
+ if (block->style()->hasPseudoStyle(FIRST_LETTER) && block->canHaveChildren() && block->isRenderBlock())
+ return toRenderBlock(block);
+ }
+ return 0;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTextFragment.h b/Source/WebCore/rendering/RenderTextFragment.h
new file mode 100644
index 0000000..c251f81
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTextFragment.h
@@ -0,0 +1,84 @@
+/*
+ * (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 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 RenderTextFragment_h
+#define RenderTextFragment_h
+
+#include "RenderText.h"
+
+namespace WebCore {
+
+// Used to represent a text substring of an element, e.g., for text runs that are split because of
+// first letter and that must therefore have different styles (and positions in the render tree).
+// We cache offsets so that text transformations can be applied in such a way that we can recover
+// the original unaltered string from our corresponding DOM node.
+class RenderTextFragment : public RenderText {
+public:
+ RenderTextFragment(Node*, StringImpl*, int startOffset, int length);
+ RenderTextFragment(Node*, StringImpl*);
+ virtual ~RenderTextFragment();
+
+ virtual bool isTextFragment() const { return true; }
+
+ virtual void destroy();
+
+ unsigned start() const { return m_start; }
+ unsigned end() const { return m_end; }
+
+ RenderObject* firstLetter() const { return m_firstLetter; }
+ void setFirstLetter(RenderObject* firstLetter) { m_firstLetter = firstLetter; }
+
+ StringImpl* contentString() const { return m_contentString.get(); }
+ virtual PassRefPtr<StringImpl> originalText() const;
+
+protected:
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+
+private:
+ virtual void setTextInternal(PassRefPtr<StringImpl>);
+ virtual UChar previousCharacter() const;
+ RenderBlock* blockForAccompanyingFirstLetter() const;
+
+ unsigned m_start;
+ unsigned m_end;
+ RefPtr<StringImpl> m_contentString;
+ RenderObject* m_firstLetter;
+};
+
+inline RenderTextFragment* toRenderTextFragment(RenderObject* object)
+{
+ ASSERT(!object || toRenderText(object)->isTextFragment());
+ return static_cast<RenderTextFragment*>(object);
+}
+
+inline const RenderTextFragment* toRenderTextFragment(const RenderObject* object)
+{
+ ASSERT(!object || toRenderText(object)->isTextFragment());
+ return static_cast<const RenderTextFragment*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderTextFragment(const RenderTextFragment*);
+
+} // namespace WebCore
+
+#endif // RenderTextFragment_h
diff --git a/Source/WebCore/rendering/RenderTheme.cpp b/Source/WebCore/rendering/RenderTheme.cpp
new file mode 100644
index 0000000..538b6c6
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTheme.cpp
@@ -0,0 +1,1134 @@
+/**
+ * This file is part of the theme implementation for form controls in WebCore.
+ *
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 "RenderTheme.h"
+
+#include "CSSValueKeywords.h"
+#include "Document.h"
+#include "FloatConversion.h"
+#include "FocusController.h"
+#include "FontSelector.h"
+#include "Frame.h"
+#include "GraphicsContext.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "MediaControlElements.h"
+#include "Page.h"
+#include "RenderStyle.h"
+#include "RenderView.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "TextControlInnerElements.h"
+
+#if ENABLE(METER_TAG)
+#include "HTMLMeterElement.h"
+#include "RenderMeter.h"
+#endif
+
+#if ENABLE(INPUT_SPEECH)
+#include "RenderInputSpeech.h"
+#endif
+
+// The methods in this file are shared by all themes on every platform.
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static Color& customFocusRingColor()
+{
+ DEFINE_STATIC_LOCAL(Color, color, ());
+ return color;
+}
+
+RenderTheme::RenderTheme()
+#if USE(NEW_THEME)
+ : m_theme(platformTheme())
+#endif
+{
+}
+
+void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e,
+ bool UAHasAppearance, const BorderData& border, const FillLayer& background, const Color& backgroundColor)
+{
+ // Force inline and table display styles to be inline-block (except for table- which is block)
+ ControlPart part = style->appearance();
+ if (style->display() == INLINE || style->display() == INLINE_TABLE || style->display() == TABLE_ROW_GROUP ||
+ style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_FOOTER_GROUP ||
+ style->display() == TABLE_ROW || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN ||
+ style->display() == TABLE_CELL || style->display() == TABLE_CAPTION)
+ style->setDisplay(INLINE_BLOCK);
+ else if (style->display() == COMPACT || style->display() == RUN_IN || style->display() == LIST_ITEM || style->display() == TABLE)
+ style->setDisplay(BLOCK);
+
+ if (UAHasAppearance && isControlStyled(style, border, background, backgroundColor)) {
+ if (part == MenulistPart) {
+ style->setAppearance(MenulistButtonPart);
+ part = MenulistButtonPart;
+ } else
+ style->setAppearance(NoControlPart);
+ }
+
+ if (!style->hasAppearance())
+ return;
+
+ // Never support box-shadow on native controls.
+ style->setBoxShadow(0);
+
+#if USE(NEW_THEME)
+ switch (part) {
+ case ListButtonPart:
+ case CheckboxPart:
+ case InnerSpinButtonPart:
+ case OuterSpinButtonPart:
+ case RadioPart:
+ case PushButtonPart:
+ case SquareButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart: {
+ // Border
+ LengthBox borderBox(style->borderTopWidth(), style->borderRightWidth(), style->borderBottomWidth(), style->borderLeftWidth());
+ borderBox = m_theme->controlBorder(part, style->font(), borderBox, style->effectiveZoom());
+ if (borderBox.top().value() != style->borderTopWidth()) {
+ if (borderBox.top().value())
+ style->setBorderTopWidth(borderBox.top().value());
+ else
+ style->resetBorderTop();
+ }
+ if (borderBox.right().value() != style->borderRightWidth()) {
+ if (borderBox.right().value())
+ style->setBorderRightWidth(borderBox.right().value());
+ else
+ style->resetBorderRight();
+ }
+ if (borderBox.bottom().value() != style->borderBottomWidth()) {
+ style->setBorderBottomWidth(borderBox.bottom().value());
+ if (borderBox.bottom().value())
+ style->setBorderBottomWidth(borderBox.bottom().value());
+ else
+ style->resetBorderBottom();
+ }
+ if (borderBox.left().value() != style->borderLeftWidth()) {
+ style->setBorderLeftWidth(borderBox.left().value());
+ if (borderBox.left().value())
+ style->setBorderLeftWidth(borderBox.left().value());
+ else
+ style->resetBorderLeft();
+ }
+
+ // Padding
+ LengthBox paddingBox = m_theme->controlPadding(part, style->font(), style->paddingBox(), style->effectiveZoom());
+ if (paddingBox != style->paddingBox())
+ style->setPaddingBox(paddingBox);
+
+ // Whitespace
+ if (m_theme->controlRequiresPreWhiteSpace(part))
+ style->setWhiteSpace(PRE);
+
+ // Width / Height
+ // The width and height here are affected by the zoom.
+ // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
+ LengthSize controlSize = m_theme->controlSize(part, style->font(), LengthSize(style->width(), style->height()), style->effectiveZoom());
+ if (controlSize.width() != style->width())
+ style->setWidth(controlSize.width());
+ if (controlSize.height() != style->height())
+ style->setHeight(controlSize.height());
+
+ // Min-Width / Min-Height
+ LengthSize minControlSize = m_theme->minimumControlSize(part, style->font(), style->effectiveZoom());
+ if (minControlSize.width() != style->minWidth())
+ style->setMinWidth(minControlSize.width());
+ if (minControlSize.height() != style->minHeight())
+ style->setMinHeight(minControlSize.height());
+
+ // Font
+ FontDescription controlFont = m_theme->controlFont(part, style->font(), style->effectiveZoom());
+ if (controlFont != style->font().fontDescription()) {
+ // Reset our line-height
+ style->setLineHeight(RenderStyle::initialLineHeight());
+
+ // Now update our font.
+ if (style->setFontDescription(controlFont))
+ style->font().update(0);
+ }
+ }
+ default:
+ break;
+ }
+#endif
+
+ // Call the appropriate style adjustment method based off the appearance value.
+ switch (style->appearance()) {
+#if !USE(NEW_THEME)
+ case CheckboxPart:
+ return adjustCheckboxStyle(selector, style, e);
+ case RadioPart:
+ return adjustRadioStyle(selector, style, e);
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ return adjustButtonStyle(selector, style, e);
+ case InnerSpinButtonPart:
+ return adjustInnerSpinButtonStyle(selector, style, e);
+ case OuterSpinButtonPart:
+ return adjustOuterSpinButtonStyle(selector, style, e);
+#endif
+ case TextFieldPart:
+ return adjustTextFieldStyle(selector, style, e);
+ case TextAreaPart:
+ return adjustTextAreaStyle(selector, style, e);
+#if ENABLE(NO_LISTBOX_RENDERING)
+ case ListboxPart:
+ return adjustListboxStyle(selector, style, e);
+#endif
+ case MenulistPart:
+ return adjustMenuListStyle(selector, style, e);
+ case MenulistButtonPart:
+ return adjustMenuListButtonStyle(selector, style, e);
+ case MediaSliderPart:
+ case MediaVolumeSliderPart:
+ case SliderHorizontalPart:
+ case SliderVerticalPart:
+ return adjustSliderTrackStyle(selector, style, e);
+ case SliderThumbHorizontalPart:
+ case SliderThumbVerticalPart:
+ return adjustSliderThumbStyle(selector, style, e);
+ case SearchFieldPart:
+ return adjustSearchFieldStyle(selector, style, e);
+ case SearchFieldCancelButtonPart:
+ return adjustSearchFieldCancelButtonStyle(selector, style, e);
+ case SearchFieldDecorationPart:
+ return adjustSearchFieldDecorationStyle(selector, style, e);
+ case SearchFieldResultsDecorationPart:
+ return adjustSearchFieldResultsDecorationStyle(selector, style, e);
+ case SearchFieldResultsButtonPart:
+ return adjustSearchFieldResultsButtonStyle(selector, style, e);
+#if ENABLE(PROGRESS_TAG)
+ case ProgressBarPart:
+ return adjustProgressBarStyle(selector, style, e);
+#endif
+#if ENABLE(METER_TAG)
+ case MeterPart:
+ case RelevancyLevelIndicatorPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+ return adjustMeterStyle(selector, style, e);
+#endif
+#if ENABLE(INPUT_SPEECH)
+ case InputSpeechButtonPart:
+ return adjustInputFieldSpeechButtonStyle(selector, style, e);
+#endif
+ default:
+ break;
+ }
+}
+
+bool RenderTheme::paint(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ // If painting is disabled, but we aren't updating control tints, then just bail.
+ // If we are updating control tints, just schedule a repaint if the theme supports tinting
+ // for that control.
+ if (paintInfo.context->updatingControlTints()) {
+ if (controlSupportsTints(o))
+ o->repaint();
+ return false;
+ }
+ if (paintInfo.context->paintingDisabled())
+ return false;
+
+ ControlPart part = o->style()->appearance();
+
+#if USE(NEW_THEME)
+ switch (part) {
+ case CheckboxPart:
+ case RadioPart:
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ case InnerSpinButtonPart:
+ case OuterSpinButtonPart:
+ m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView());
+ return false;
+ default:
+ break;
+ }
+#endif
+
+ // Call the appropriate paint method based off the appearance value.
+ switch (part) {
+#if !USE(NEW_THEME)
+ case CheckboxPart:
+ return paintCheckbox(o, paintInfo, r);
+ case RadioPart:
+ return paintRadio(o, paintInfo, r);
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ return paintButton(o, paintInfo, r);
+ case InnerSpinButtonPart:
+ return paintInnerSpinButton(o, paintInfo, r);
+ case OuterSpinButtonPart:
+ return paintOuterSpinButton(o, paintInfo, r);
+#endif
+ case MenulistPart:
+ return paintMenuList(o, paintInfo, r);
+#if ENABLE(METER_TAG)
+ case MeterPart:
+ case RelevancyLevelIndicatorPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+ return paintMeter(o, paintInfo, r);
+#endif
+#if ENABLE(PROGRESS_TAG)
+ case ProgressBarPart:
+ return paintProgressBar(o, paintInfo, r);
+#endif
+ case SliderHorizontalPart:
+ case SliderVerticalPart:
+ return paintSliderTrack(o, paintInfo, r);
+ case SliderThumbHorizontalPart:
+ case SliderThumbVerticalPart:
+ if (o->parent()->isSlider())
+ return paintSliderThumb(o, paintInfo, r);
+ // We don't support drawing a slider thumb without a parent slider
+ break;
+ case MediaFullscreenButtonPart:
+ return paintMediaFullscreenButton(o, paintInfo, r);
+ case MediaPlayButtonPart:
+ return paintMediaPlayButton(o, paintInfo, r);
+ case MediaMuteButtonPart:
+ return paintMediaMuteButton(o, paintInfo, r);
+ case MediaSeekBackButtonPart:
+ return paintMediaSeekBackButton(o, paintInfo, r);
+ case MediaSeekForwardButtonPart:
+ return paintMediaSeekForwardButton(o, paintInfo, r);
+ case MediaRewindButtonPart:
+ return paintMediaRewindButton(o, paintInfo, r);
+ case MediaReturnToRealtimeButtonPart:
+ return paintMediaReturnToRealtimeButton(o, paintInfo, r);
+ case MediaToggleClosedCaptionsButtonPart:
+ return paintMediaToggleClosedCaptionsButton(o, paintInfo, r);
+ case MediaSliderPart:
+ return paintMediaSliderTrack(o, paintInfo, r);
+ case MediaSliderThumbPart:
+ if (o->parent()->isSlider())
+ return paintMediaSliderThumb(o, paintInfo, r);
+ break;
+ case MediaVolumeSliderMuteButtonPart:
+ return paintMediaMuteButton(o, paintInfo, r);
+ case MediaVolumeSliderContainerPart:
+ return paintMediaVolumeSliderContainer(o, paintInfo, r);
+ case MediaVolumeSliderPart:
+ return paintMediaVolumeSliderTrack(o, paintInfo, r);
+ case MediaVolumeSliderThumbPart:
+ if (o->parent()->isSlider())
+ return paintMediaVolumeSliderThumb(o, paintInfo, r);
+ break;
+ case MediaTimeRemainingPart:
+ return paintMediaTimeRemaining(o, paintInfo, r);
+ case MediaCurrentTimePart:
+ return paintMediaCurrentTime(o, paintInfo, r);
+ case MediaControlsBackgroundPart:
+ return paintMediaControlsBackground(o, paintInfo, r);
+ case MenulistButtonPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ case ListboxPart:
+ return true;
+ case SearchFieldPart:
+ return paintSearchField(o, paintInfo, r);
+ case SearchFieldCancelButtonPart:
+ return paintSearchFieldCancelButton(o, paintInfo, r);
+ case SearchFieldDecorationPart:
+ return paintSearchFieldDecoration(o, paintInfo, r);
+ case SearchFieldResultsDecorationPart:
+ return paintSearchFieldResultsDecoration(o, paintInfo, r);
+ case SearchFieldResultsButtonPart:
+ return paintSearchFieldResultsButton(o, paintInfo, r);
+#if ENABLE(INPUT_SPEECH)
+ case InputSpeechButtonPart:
+ return paintInputFieldSpeechButton(o, paintInfo, r);
+#endif
+ default:
+ break;
+ }
+
+ return true; // We don't support the appearance, so let the normal background/border paint.
+}
+
+bool RenderTheme::paintBorderOnly(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ if (paintInfo.context->paintingDisabled())
+ return false;
+
+ // Call the appropriate paint method based off the appearance value.
+ switch (o->style()->appearance()) {
+ case TextFieldPart:
+ return paintTextField(o, paintInfo, r);
+ case ListboxPart:
+ case TextAreaPart:
+ return paintTextArea(o, paintInfo, r);
+ case MenulistButtonPart:
+ case SearchFieldPart:
+ return true;
+ case CheckboxPart:
+ case RadioPart:
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ case MenulistPart:
+#if ENABLE(METER_TAG)
+ case MeterPart:
+ case RelevancyLevelIndicatorPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+#endif
+#if ENABLE(PROGRESS_TAG)
+ case ProgressBarPart:
+#endif
+ case SliderHorizontalPart:
+ case SliderVerticalPart:
+ case SliderThumbHorizontalPart:
+ case SliderThumbVerticalPart:
+ case SearchFieldCancelButtonPart:
+ case SearchFieldDecorationPart:
+ case SearchFieldResultsDecorationPart:
+ case SearchFieldResultsButtonPart:
+#if ENABLE(INPUT_SPEECH)
+ case InputSpeechButtonPart:
+#endif
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool RenderTheme::paintDecorations(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ if (paintInfo.context->paintingDisabled())
+ return false;
+
+ // Call the appropriate paint method based off the appearance value.
+ switch (o->style()->appearance()) {
+ case MenulistButtonPart:
+ return paintMenuListButton(o, paintInfo, r);
+ case TextFieldPart:
+ case TextAreaPart:
+ case ListboxPart:
+ case CheckboxPart:
+ case RadioPart:
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ case MenulistPart:
+#if ENABLE(METER_TAG)
+ case MeterPart:
+ case RelevancyLevelIndicatorPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+#endif
+#if ENABLE(PROGRESS_TAG)
+ case ProgressBarPart:
+#endif
+ case SliderHorizontalPart:
+ case SliderVerticalPart:
+ case SliderThumbHorizontalPart:
+ case SliderThumbVerticalPart:
+ case SearchFieldPart:
+ case SearchFieldCancelButtonPart:
+ case SearchFieldDecorationPart:
+ case SearchFieldResultsDecorationPart:
+ case SearchFieldResultsButtonPart:
+#if ENABLE(INPUT_SPEECH)
+ case InputSpeechButtonPart:
+#endif
+ default:
+ break;
+ }
+
+ return false;
+}
+
+#if ENABLE(VIDEO)
+bool RenderTheme::hitTestMediaControlPart(RenderObject* o, const IntPoint& absPoint)
+{
+ if (!o->isBox())
+ return false;
+
+ FloatPoint localPoint = o->absoluteToLocal(absPoint, false, true); // respect transforms
+ return toRenderBox(o)->borderBoxRect().contains(roundedIntPoint(localPoint));
+}
+
+bool RenderTheme::shouldRenderMediaControlPart(ControlPart part, Element* e)
+{
+ HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(e);
+ switch (part) {
+ case MediaMuteButtonPart:
+ return mediaElement->hasAudio();
+ case MediaRewindButtonPart:
+ return mediaElement->movieLoadType() != MediaPlayer::LiveStream;
+ case MediaReturnToRealtimeButtonPart:
+ return mediaElement->movieLoadType() == MediaPlayer::LiveStream;
+ case MediaFullscreenButtonPart:
+ return mediaElement->supportsFullscreen();
+ case MediaToggleClosedCaptionsButtonPart:
+ return mediaElement->hasClosedCaptions();
+ default:
+ return true;
+ }
+}
+
+String RenderTheme::formatMediaControlsTime(float time) const
+{
+ if (!isfinite(time))
+ time = 0;
+ int seconds = (int)fabsf(time);
+ int hours = seconds / (60 * 60);
+ int minutes = (seconds / 60) % 60;
+ seconds %= 60;
+ if (hours) {
+ if (hours > 9)
+ return String::format("%s%02d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
+
+ return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds);
+ }
+
+ return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds);
+}
+
+String RenderTheme::formatMediaControlsCurrentTime(float currentTime, float /*duration*/) const
+{
+ return formatMediaControlsTime(currentTime);
+}
+
+String RenderTheme::formatMediaControlsRemainingTime(float currentTime, float duration) const
+{
+ return formatMediaControlsTime(currentTime - duration);
+}
+
+IntPoint RenderTheme::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size) const
+{
+ int y = -size.height();
+ FloatPoint absPoint = muteButton->renderer()->localToAbsolute(FloatPoint(muteButton->renderBox()->offsetLeft(), y), true, true);
+ if (absPoint.y() < 0)
+ y = muteButton->renderBox()->height();
+ return IntPoint(0, y);
+}
+
+#endif
+
+Color RenderTheme::activeSelectionBackgroundColor() const
+{
+ if (!m_activeSelectionBackgroundColor.isValid())
+ m_activeSelectionBackgroundColor = platformActiveSelectionBackgroundColor().blendWithWhite();
+ return m_activeSelectionBackgroundColor;
+}
+
+Color RenderTheme::inactiveSelectionBackgroundColor() const
+{
+ if (!m_inactiveSelectionBackgroundColor.isValid())
+ m_inactiveSelectionBackgroundColor = platformInactiveSelectionBackgroundColor().blendWithWhite();
+ return m_inactiveSelectionBackgroundColor;
+}
+
+Color RenderTheme::activeSelectionForegroundColor() const
+{
+ if (!m_activeSelectionForegroundColor.isValid() && supportsSelectionForegroundColors())
+ m_activeSelectionForegroundColor = platformActiveSelectionForegroundColor();
+ return m_activeSelectionForegroundColor;
+}
+
+Color RenderTheme::inactiveSelectionForegroundColor() const
+{
+ if (!m_inactiveSelectionForegroundColor.isValid() && supportsSelectionForegroundColors())
+ m_inactiveSelectionForegroundColor = platformInactiveSelectionForegroundColor();
+ return m_inactiveSelectionForegroundColor;
+}
+
+Color RenderTheme::activeListBoxSelectionBackgroundColor() const
+{
+ if (!m_activeListBoxSelectionBackgroundColor.isValid())
+ m_activeListBoxSelectionBackgroundColor = platformActiveListBoxSelectionBackgroundColor();
+ return m_activeListBoxSelectionBackgroundColor;
+}
+
+Color RenderTheme::inactiveListBoxSelectionBackgroundColor() const
+{
+ if (!m_inactiveListBoxSelectionBackgroundColor.isValid())
+ m_inactiveListBoxSelectionBackgroundColor = platformInactiveListBoxSelectionBackgroundColor();
+ return m_inactiveListBoxSelectionBackgroundColor;
+}
+
+Color RenderTheme::activeListBoxSelectionForegroundColor() const
+{
+ if (!m_activeListBoxSelectionForegroundColor.isValid() && supportsListBoxSelectionForegroundColors())
+ m_activeListBoxSelectionForegroundColor = platformActiveListBoxSelectionForegroundColor();
+ return m_activeListBoxSelectionForegroundColor;
+}
+
+Color RenderTheme::inactiveListBoxSelectionForegroundColor() const
+{
+ if (!m_inactiveListBoxSelectionForegroundColor.isValid() && supportsListBoxSelectionForegroundColors())
+ m_inactiveListBoxSelectionForegroundColor = platformInactiveListBoxSelectionForegroundColor();
+ return m_inactiveListBoxSelectionForegroundColor;
+}
+
+Color RenderTheme::platformActiveSelectionBackgroundColor() const
+{
+ // Use a blue color by default if the platform theme doesn't define anything.
+ return Color(0, 0, 255);
+}
+
+Color RenderTheme::platformActiveSelectionForegroundColor() const
+{
+ // Use a white color by default if the platform theme doesn't define anything.
+ return Color::white;
+}
+
+Color RenderTheme::platformInactiveSelectionBackgroundColor() const
+{
+ // Use a grey color by default if the platform theme doesn't define anything.
+ // This color matches Firefox's inactive color.
+ return Color(176, 176, 176);
+}
+
+Color RenderTheme::platformInactiveSelectionForegroundColor() const
+{
+ // Use a black color by default.
+ return Color::black;
+}
+
+Color RenderTheme::platformActiveListBoxSelectionBackgroundColor() const
+{
+ return platformActiveSelectionBackgroundColor();
+}
+
+Color RenderTheme::platformActiveListBoxSelectionForegroundColor() const
+{
+ return platformActiveSelectionForegroundColor();
+}
+
+Color RenderTheme::platformInactiveListBoxSelectionBackgroundColor() const
+{
+ return platformInactiveSelectionBackgroundColor();
+}
+
+Color RenderTheme::platformInactiveListBoxSelectionForegroundColor() const
+{
+ return platformInactiveSelectionForegroundColor();
+}
+
+int RenderTheme::baselinePosition(const RenderObject* o) const
+{
+ if (!o->isBox())
+ return 0;
+
+ const RenderBox* box = toRenderBox(o);
+
+#if USE(NEW_THEME)
+ return box->height() + box->marginTop() + m_theme->baselinePositionAdjustment(o->style()->appearance()) * o->style()->effectiveZoom();
+#else
+ return box->height() + box->marginTop();
+#endif
+}
+
+bool RenderTheme::isControlContainer(ControlPart appearance) const
+{
+ // There are more leaves than this, but we'll patch this function as we add support for
+ // more controls.
+ return appearance != CheckboxPart && appearance != RadioPart;
+}
+
+bool RenderTheme::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& background,
+ const Color& backgroundColor) const
+{
+ switch (style->appearance()) {
+ case PushButtonPart:
+ case SquareButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ case ListboxPart:
+ case MenulistPart:
+ case ProgressBarPart:
+ case MeterPart:
+ case RelevancyLevelIndicatorPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+ // FIXME: Uncomment this when making search fields style-able.
+ // case SearchFieldPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ // Test the style to see if the UA border and background match.
+ return (style->border() != border ||
+ *style->backgroundLayers() != background ||
+ style->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor);
+ default:
+ return false;
+ }
+}
+
+void RenderTheme::adjustRepaintRect(const RenderObject* o, IntRect& r)
+{
+#if USE(NEW_THEME)
+ m_theme->inflateControlPaintRect(o->style()->appearance(), controlStatesForRenderer(o), r, o->style()->effectiveZoom());
+#endif
+}
+
+bool RenderTheme::supportsFocusRing(const RenderStyle* style) const
+{
+ return (style->hasAppearance() && style->appearance() != TextFieldPart && style->appearance() != TextAreaPart && style->appearance() != MenulistButtonPart && style->appearance() != ListboxPart);
+}
+
+bool RenderTheme::stateChanged(RenderObject* o, ControlState state) const
+{
+ // Default implementation assumes the controls don't respond to changes in :hover state
+ if (state == HoverState && !supportsHover(o->style()))
+ return false;
+
+ // Assume pressed state is only responded to if the control is enabled.
+ if (state == PressedState && !isEnabled(o))
+ return false;
+
+ // Repaint the control.
+ o->repaint();
+ return true;
+}
+
+ControlStates RenderTheme::controlStatesForRenderer(const RenderObject* o) const
+{
+ ControlStates result = 0;
+ if (isHovered(o)) {
+ result |= HoverState;
+ if (isSpinUpButtonPartHovered(o))
+ result |= SpinUpState;
+ }
+ if (isPressed(o)) {
+ result |= PressedState;
+ if (isSpinUpButtonPartPressed(o))
+ result |= SpinUpState;
+ }
+ if (isFocused(o) && o->style()->outlineStyleIsAuto())
+ result |= FocusState;
+ if (isEnabled(o))
+ result |= EnabledState;
+ if (isChecked(o))
+ result |= CheckedState;
+ if (isReadOnlyControl(o))
+ result |= ReadOnlyState;
+ if (isDefault(o))
+ result |= DefaultState;
+ if (!isActive(o))
+ result |= WindowInactiveState;
+ if (isIndeterminate(o))
+ result |= IndeterminateState;
+ return result;
+}
+
+bool RenderTheme::isActive(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ Frame* frame = node->document()->frame();
+ if (!frame)
+ return false;
+
+ Page* page = frame->page();
+ if (!page)
+ return false;
+
+ return page->focusController()->isActive();
+}
+
+bool RenderTheme::isChecked(const RenderObject* o) const
+{
+ if (!o->node() || !o->node()->isElementNode())
+ return false;
+
+ InputElement* inputElement = toInputElement(static_cast<Element*>(o->node()));
+ if (!inputElement)
+ return false;
+
+ return inputElement->isChecked();
+}
+
+bool RenderTheme::isIndeterminate(const RenderObject* o) const
+{
+ if (!o->node() || !o->node()->isElementNode())
+ return false;
+
+ InputElement* inputElement = toInputElement(static_cast<Element*>(o->node()));
+ if (!inputElement)
+ return false;
+
+ return inputElement->isIndeterminate();
+}
+
+bool RenderTheme::isEnabled(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node || !node->isElementNode())
+ return true;
+ return static_cast<Element*>(node)->isEnabledFormControl();
+}
+
+bool RenderTheme::isFocused(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+ Document* document = node->document();
+ Frame* frame = document->frame();
+ return node == document->focusedNode() && frame && frame->selection()->isFocusedAndActive();
+}
+
+bool RenderTheme::isPressed(const RenderObject* o) const
+{
+ if (!o->node())
+ return false;
+ return o->node()->active();
+}
+
+bool RenderTheme::isSpinUpButtonPartPressed(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node || !node->active() || !node->isElementNode()
+ || !static_cast<Element*>(node)->isSpinButtonElement())
+ return false;
+ SpinButtonElement* element = static_cast<SpinButtonElement*>(node);
+ return element->upDownState() == SpinButtonElement::Up;
+}
+
+bool RenderTheme::isReadOnlyControl(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node || !node->isElementNode())
+ return false;
+ return static_cast<Element*>(node)->isReadOnlyFormControl();
+}
+
+bool RenderTheme::isHovered(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+ if (!node->isElementNode() || !static_cast<Element*>(node)->isSpinButtonElement())
+ return node->hovered();
+ SpinButtonElement* element = static_cast<SpinButtonElement*>(node);
+ return element->hovered() && element->upDownState() != SpinButtonElement::Indeterminate;
+}
+
+bool RenderTheme::isSpinUpButtonPartHovered(const RenderObject* o) const
+{
+ Node* node = o->node();
+ if (!node || !node->isElementNode() || !static_cast<Element*>(node)->isSpinButtonElement())
+ return false;
+ SpinButtonElement* element = static_cast<SpinButtonElement*>(node);
+ return element->upDownState() == SpinButtonElement::Up;
+}
+
+bool RenderTheme::isDefault(const RenderObject* o) const
+{
+ // A button should only have the default appearance if the page is active
+ if (!isActive(o))
+ return false;
+
+ if (!o->document())
+ return false;
+
+ Settings* settings = o->document()->settings();
+ if (!settings || !settings->inApplicationChromeMode())
+ return false;
+
+ return o->style()->appearance() == DefaultButtonPart;
+}
+
+#if !USE(NEW_THEME)
+
+void RenderTheme::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // A summary of the rules for checkbox designed to match WinIE:
+ // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.)
+ // font-size - not honored (control has no text), but we use it to decide which control size to use.
+ setCheckboxSize(style);
+
+ // padding - not honored by WinIE, needs to be removed.
+ style->resetPadding();
+
+ // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme)
+ // for now, we will not honor it.
+ style->resetBorder();
+
+ style->setBoxShadow(0);
+}
+
+void RenderTheme::adjustRadioStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // A summary of the rules for checkbox designed to match WinIE:
+ // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.)
+ // font-size - not honored (control has no text), but we use it to decide which control size to use.
+ setRadioSize(style);
+
+ // padding - not honored by WinIE, needs to be removed.
+ style->resetPadding();
+
+ // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme)
+ // for now, we will not honor it.
+ style->resetBorder();
+
+ style->setBoxShadow(0);
+}
+
+void RenderTheme::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // Most platforms will completely honor all CSS, and so we have no need to adjust the style
+ // at all by default. We will still allow the theme a crack at setting up a desired vertical size.
+ setButtonSize(style);
+}
+
+void RenderTheme::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+#endif
+
+void RenderTheme::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+#if ENABLE(INPUT_SPEECH)
+void RenderTheme::adjustInputFieldSpeechButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* element) const
+{
+ RenderInputSpeech::adjustInputFieldSpeechButtonStyle(selector, style, element);
+}
+
+bool RenderTheme::paintInputFieldSpeechButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderInputSpeech::paintInputFieldSpeechButton(object, paintInfo, rect);
+}
+#endif
+
+#if ENABLE(METER_TAG)
+void RenderTheme::adjustMeterStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ style->setBoxShadow(0);
+}
+
+IntSize RenderTheme::meterSizeForBounds(const RenderMeter*, const IntRect& bounds) const
+{
+ return bounds.size();
+}
+
+bool RenderTheme::supportsMeter(ControlPart, bool) const
+{
+ return false;
+}
+
+bool RenderTheme::paintMeter(RenderObject*, const PaintInfo&, const IntRect&)
+{
+ return true;
+}
+
+#endif
+
+#if ENABLE(PROGRESS_TAG)
+double RenderTheme::animationRepeatIntervalForProgressBar(RenderProgress*) const
+{
+ return 0;
+}
+
+double RenderTheme::animationDurationForProgressBar(RenderProgress*) const
+{
+ return 0;
+}
+
+void RenderTheme::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+#endif
+
+void RenderTheme::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSliderThumbSize(RenderObject*) const
+{
+}
+
+void RenderTheme::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+void RenderTheme::platformColorsDidChange()
+{
+ m_activeSelectionForegroundColor = Color();
+ m_inactiveSelectionForegroundColor = Color();
+ m_activeSelectionBackgroundColor = Color();
+ m_inactiveSelectionBackgroundColor = Color();
+
+ m_activeListBoxSelectionForegroundColor = Color();
+ m_inactiveListBoxSelectionForegroundColor = Color();
+ m_activeListBoxSelectionBackgroundColor = Color();
+ m_inactiveListBoxSelectionForegroundColor = Color();
+
+ Page::scheduleForcedStyleRecalcForAllPages();
+}
+
+Color RenderTheme::systemColor(int cssValueId) const
+{
+ switch (cssValueId) {
+ case CSSValueActiveborder:
+ return 0xFFFFFFFF;
+ case CSSValueActivecaption:
+ return 0xFFCCCCCC;
+ case CSSValueAppworkspace:
+ return 0xFFFFFFFF;
+ case CSSValueBackground:
+ return 0xFF6363CE;
+ case CSSValueButtonface:
+ return 0xFFC0C0C0;
+ case CSSValueButtonhighlight:
+ return 0xFFDDDDDD;
+ case CSSValueButtonshadow:
+ return 0xFF888888;
+ case CSSValueButtontext:
+ return 0xFF000000;
+ case CSSValueCaptiontext:
+ return 0xFF000000;
+ case CSSValueGraytext:
+ return 0xFF808080;
+ case CSSValueHighlight:
+ return 0xFFB5D5FF;
+ case CSSValueHighlighttext:
+ return 0xFF000000;
+ case CSSValueInactiveborder:
+ return 0xFFFFFFFF;
+ case CSSValueInactivecaption:
+ return 0xFFFFFFFF;
+ case CSSValueInactivecaptiontext:
+ return 0xFF7F7F7F;
+ case CSSValueInfobackground:
+ return 0xFFFBFCC5;
+ case CSSValueInfotext:
+ return 0xFF000000;
+ case CSSValueMenu:
+ return 0xFFC0C0C0;
+ case CSSValueMenutext:
+ return 0xFF000000;
+ case CSSValueScrollbar:
+ return 0xFFFFFFFF;
+ case CSSValueText:
+ return 0xFF000000;
+ case CSSValueThreeddarkshadow:
+ return 0xFF666666;
+ case CSSValueThreedface:
+ return 0xFFC0C0C0;
+ case CSSValueThreedhighlight:
+ return 0xFFDDDDDD;
+ case CSSValueThreedlightshadow:
+ return 0xFFC0C0C0;
+ case CSSValueThreedshadow:
+ return 0xFF888888;
+ case CSSValueWindow:
+ return 0xFFFFFFFF;
+ case CSSValueWindowframe:
+ return 0xFFCCCCCC;
+ case CSSValueWindowtext:
+ return 0xFF000000;
+ }
+ return Color();
+}
+
+Color RenderTheme::platformActiveTextSearchHighlightColor() const
+{
+ return Color(255, 150, 50); // Orange.
+}
+
+Color RenderTheme::platformInactiveTextSearchHighlightColor() const
+{
+ return Color(255, 255, 0); // Yellow.
+}
+
+void RenderTheme::setCustomFocusRingColor(const Color& c)
+{
+ customFocusRingColor() = c;
+}
+
+Color RenderTheme::focusRingColor()
+{
+ return customFocusRingColor().isValid() ? customFocusRingColor() : defaultTheme()->platformFocusRingColor();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTheme.h b/Source/WebCore/rendering/RenderTheme.h
new file mode 100644
index 0000000..13c69e6
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTheme.h
@@ -0,0 +1,334 @@
+/*
+ * This file is part of the theme implementation for form controls in WebCore.
+ *
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 RenderTheme_h
+#define RenderTheme_h
+
+#if USE(NEW_THEME)
+#include "Theme.h"
+#else
+#include "ThemeTypes.h"
+#endif
+#include "RenderObject.h"
+#include "ScrollTypes.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class Element;
+class PopupMenu;
+class RenderMenuList;
+#if ENABLE(METER_TAG)
+class RenderMeter;
+#endif
+#if ENABLE(PROGRESS_TAG)
+class RenderProgress;
+#endif
+class CSSStyleSheet;
+
+class RenderTheme : public RefCounted<RenderTheme> {
+protected:
+ RenderTheme();
+
+public:
+ virtual ~RenderTheme() { }
+
+ // This function is to be implemented in your platform-specific theme implementation to hand back the
+ // appropriate platform theme. When the theme is needed in non-page dependent code, a default theme is
+ // used as fallback, which is returned for a nulled page, so the platform code needs to account for this.
+ static PassRefPtr<RenderTheme> themeForPage(Page* page);
+
+ // When the theme is needed in non-page dependent code, the defaultTheme() is used as fallback.
+ static inline PassRefPtr<RenderTheme> defaultTheme()
+ {
+ return themeForPage(0);
+ };
+
+ // This method is called whenever style has been computed for an element and the appearance
+ // property has been set to a value other than "none". The theme should map in all of the appropriate
+ // metrics and defaults given the contents of the style. This includes sophisticated operations like
+ // selection of control size based off the font, the disabling of appearance when certain other properties like
+ // "border" are set, or if the appearance is not supported by the theme.
+ void adjustStyle(CSSStyleSelector*, RenderStyle*, Element*, bool UAHasAppearance,
+ const BorderData&, const FillLayer&, const Color& backgroundColor);
+
+ // This method is called to paint the widget as a background of the RenderObject. A widget's foreground, e.g., the
+ // text of a button, is always rendered by the engine itself. The boolean return value indicates
+ // whether the CSS border/background should also be painted.
+ bool paint(RenderObject*, const PaintInfo&, const IntRect&);
+ bool paintBorderOnly(RenderObject*, const PaintInfo&, const IntRect&);
+ bool paintDecorations(RenderObject*, const PaintInfo&, const IntRect&);
+
+ // The remaining methods should be implemented by the platform-specific portion of the theme, e.g.,
+ // RenderThemeMac.cpp for Mac OS X.
+
+ // These methods return the theme's extra style sheets rules, to let each platform
+ // adjust the default CSS rules in html.css, quirks.css, or mediaControls.css
+ virtual String extraDefaultStyleSheet() { return String(); }
+ virtual String extraQuirksStyleSheet() { return String(); }
+#if ENABLE(VIDEO)
+ virtual String extraMediaControlsStyleSheet() { return String(); };
+#endif
+
+ // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline
+ // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
+ // controls that need to do this.
+ virtual int baselinePosition(const RenderObject*) const;
+
+ // A method for asking if a control is a container or not. Leaf controls have to have some special behavior (like
+ // the baseline position API above).
+ bool isControlContainer(ControlPart) const;
+
+ // A method asking if the control changes its tint when the window has focus or not.
+ virtual bool controlSupportsTints(const RenderObject*) const { return false; }
+
+ // Whether or not the control has been styled enough by the author to disable the native appearance.
+ virtual bool isControlStyled(const RenderStyle*, const BorderData&, const FillLayer&, const Color& backgroundColor) const;
+
+ // A general method asking if any control tinting is supported at all.
+ virtual bool supportsControlTints() const { return false; }
+
+ // Some controls may spill out of their containers (e.g., the check on an OS X checkbox). When these controls repaint,
+ // the theme needs to communicate this inflated rect to the engine so that it can invalidate the whole control.
+ virtual void adjustRepaintRect(const RenderObject*, IntRect&);
+
+ // This method is called whenever a relevant state changes on a particular themed object, e.g., the mouse becomes pressed
+ // or a control becomes disabled.
+ virtual bool stateChanged(RenderObject*, ControlState) const;
+
+ // This method is called whenever the theme changes on the system in order to flush cached resources from the
+ // old theme.
+ virtual void themeChanged() { }
+
+ // A method asking if the theme is able to draw the focus ring.
+ virtual bool supportsFocusRing(const RenderStyle*) const;
+
+ // A method asking if the theme's controls actually care about redrawing when hovered.
+ virtual bool supportsHover(const RenderStyle*) const { return false; }
+
+ // Text selection colors.
+ Color activeSelectionBackgroundColor() const;
+ Color inactiveSelectionBackgroundColor() const;
+ Color activeSelectionForegroundColor() const;
+ Color inactiveSelectionForegroundColor() const;
+
+ // List box selection colors
+ Color activeListBoxSelectionBackgroundColor() const;
+ Color activeListBoxSelectionForegroundColor() const;
+ Color inactiveListBoxSelectionBackgroundColor() const;
+ Color inactiveListBoxSelectionForegroundColor() const;
+
+ // Highlighting colors for TextMatches.
+ virtual Color platformActiveTextSearchHighlightColor() const;
+ virtual Color platformInactiveTextSearchHighlightColor() const;
+
+ static Color focusRingColor();
+ virtual Color platformFocusRingColor() const { return Color(0, 0, 0); }
+ static void setCustomFocusRingColor(const Color&);
+
+ virtual void platformColorsDidChange();
+
+ virtual double caretBlinkInterval() const { return 0.5; }
+
+ // System fonts and colors for CSS.
+ virtual void systemFont(int cssValueId, FontDescription&) const = 0;
+ virtual Color systemColor(int cssValueId) const;
+
+ virtual int minimumMenuListSize(RenderStyle*) const { return 0; }
+
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ virtual int popupInternalPaddingLeft(RenderStyle*) const { return 0; }
+ virtual int popupInternalPaddingRight(RenderStyle*) const { return 0; }
+ virtual int popupInternalPaddingTop(RenderStyle*) const { return 0; }
+ virtual int popupInternalPaddingBottom(RenderStyle*) const { return 0; }
+ virtual bool popupOptionSupportsTextIndent() const { return false; }
+
+ virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return RegularScrollbar; }
+
+ // Method for painting the caps lock indicator
+ virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&) { return 0; };
+
+#if ENABLE(PROGRESS_TAG)
+ // Returns the repeat interval of the animation for the progress bar.
+ virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const;
+ // Returns the duration of the animation for the progress bar.
+ virtual double animationDurationForProgressBar(RenderProgress*) const;
+#endif
+
+#if ENABLE(VIDEO)
+ // Media controls
+ virtual bool hitTestMediaControlPart(RenderObject*, const IntPoint& absPoint);
+ virtual bool shouldRenderMediaControlPart(ControlPart, Element*);
+ virtual double mediaControlsFadeInDuration() { return 0.1; }
+ virtual double mediaControlsFadeOutDuration() { return 0.3; }
+ virtual String formatMediaControlsTime(float time) const;
+ virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const;
+ virtual String formatMediaControlsRemainingTime(float currentTime, float duration) const;
+
+ // Returns the media volume slider container's offset from the mute button.
+ virtual IntPoint volumeSliderOffsetFromMuteButton(Node*, const IntSize&) const;
+#endif
+
+#if ENABLE(METER_TAG)
+ virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const;
+ virtual bool supportsMeter(ControlPart, bool isHorizontal) const;
+#endif
+
+protected:
+ // The platform selection color.
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+
+ virtual Color platformActiveListBoxSelectionBackgroundColor() const;
+ virtual Color platformInactiveListBoxSelectionBackgroundColor() const;
+ virtual Color platformActiveListBoxSelectionForegroundColor() const;
+ virtual Color platformInactiveListBoxSelectionForegroundColor() const;
+
+ virtual bool supportsSelectionForegroundColors() const { return true; }
+ virtual bool supportsListBoxSelectionForegroundColors() const { return true; }
+
+#if !USE(NEW_THEME)
+ // Methods for each appearance value.
+ virtual void adjustCheckboxStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual void setCheckboxSize(RenderStyle*) const { }
+
+ virtual void adjustRadioStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual void setRadioSize(RenderStyle*) const { }
+
+ virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual void setButtonSize(RenderStyle*) const { }
+
+ virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual void adjustOuterSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintOuterSpinButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+#endif
+
+ virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+#if ENABLE(NO_LISTBOX_RENDERING)
+ virtual void adjustListboxStyle(CSSStyleSelector*, RenderStyle*, Element*) const {}
+#endif
+ virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+#if ENABLE(METER_TAG)
+ virtual void adjustMeterStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+#if ENABLE(PROGRESS_TAG)
+ virtual void adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+#endif
+
+#if ENABLE(INPUT_SPEECH)
+ virtual void adjustInputFieldSpeechButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintInputFieldSpeechButton(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+ virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+ virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaRewindButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaReturnToRealtimeButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaCurrentTime(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+ virtual bool paintMediaTimeRemaining(RenderObject*, const PaintInfo&, const IntRect&) { return true; }
+
+public:
+ // Methods for state querying
+ ControlStates controlStatesForRenderer(const RenderObject* o) const;
+ bool isActive(const RenderObject*) const;
+ bool isChecked(const RenderObject*) const;
+ bool isIndeterminate(const RenderObject*) const;
+ bool isEnabled(const RenderObject*) const;
+ bool isFocused(const RenderObject*) const;
+ bool isPressed(const RenderObject*) const;
+ bool isSpinUpButtonPartPressed(const RenderObject*) const;
+ bool isHovered(const RenderObject*) const;
+ bool isSpinUpButtonPartHovered(const RenderObject*) const;
+ bool isReadOnlyControl(const RenderObject*) const;
+ bool isDefault(const RenderObject*) const;
+
+private:
+ mutable Color m_activeSelectionBackgroundColor;
+ mutable Color m_inactiveSelectionBackgroundColor;
+ mutable Color m_activeSelectionForegroundColor;
+ mutable Color m_inactiveSelectionForegroundColor;
+
+ mutable Color m_activeListBoxSelectionBackgroundColor;
+ mutable Color m_inactiveListBoxSelectionBackgroundColor;
+ mutable Color m_activeListBoxSelectionForegroundColor;
+ mutable Color m_inactiveListBoxSelectionForegroundColor;
+
+#if USE(NEW_THEME)
+ Theme* m_theme; // The platform-specific theme.
+#endif
+};
+
+} // namespace WebCore
+
+#endif // RenderTheme_h
diff --git a/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp b/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp
new file mode 100644
index 0000000..de83ae9
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumLinux.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2007 Apple Inc.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2008, 2009 Google Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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 "RenderThemeChromiumLinux.h"
+
+#include "CSSValueKeywords.h"
+#include "Color.h"
+#include "PlatformThemeChromiumGtk.h"
+#include "RenderObject.h"
+#include "ScrollbarTheme.h"
+#include "UserAgentStyleSheets.h"
+
+namespace WebCore {
+
+unsigned RenderThemeChromiumLinux::m_activeSelectionBackgroundColor =
+ 0xff1e90ff;
+unsigned RenderThemeChromiumLinux::m_activeSelectionForegroundColor =
+ Color::black;
+unsigned RenderThemeChromiumLinux::m_inactiveSelectionBackgroundColor =
+ 0xffc8c8c8;
+unsigned RenderThemeChromiumLinux::m_inactiveSelectionForegroundColor =
+ 0xff323232;
+
+double RenderThemeChromiumLinux::m_caretBlinkInterval;
+
+PassRefPtr<RenderTheme> RenderThemeChromiumLinux::create()
+{
+ return adoptRef(new RenderThemeChromiumLinux());
+}
+
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+ static RenderTheme* rt = RenderThemeChromiumLinux::create().releaseRef();
+ return rt;
+}
+
+RenderThemeChromiumLinux::RenderThemeChromiumLinux()
+{
+ m_caretBlinkInterval = RenderTheme::caretBlinkInterval();
+}
+
+RenderThemeChromiumLinux::~RenderThemeChromiumLinux()
+{
+}
+
+Color RenderThemeChromiumLinux::systemColor(int cssValueId) const
+{
+ static const Color linuxButtonGrayColor(0xffdddddd);
+
+ if (cssValueId == CSSValueButtonface)
+ return linuxButtonGrayColor;
+ return RenderTheme::systemColor(cssValueId);
+}
+
+String RenderThemeChromiumLinux::extraDefaultStyleSheet()
+{
+ return RenderThemeChromiumSkia::extraDefaultStyleSheet() +
+ String(themeChromiumLinuxUserAgentStyleSheet, sizeof(themeChromiumLinuxUserAgentStyleSheet));
+}
+
+bool RenderThemeChromiumLinux::controlSupportsTints(const RenderObject* o) const
+{
+ return isEnabled(o);
+}
+
+Color RenderThemeChromiumLinux::activeListBoxSelectionBackgroundColor() const
+{
+ return Color(0x28, 0x28, 0x28);
+}
+
+Color RenderThemeChromiumLinux::activeListBoxSelectionForegroundColor() const
+{
+ return Color::black;
+}
+
+Color RenderThemeChromiumLinux::inactiveListBoxSelectionBackgroundColor() const
+{
+ return Color(0xc8, 0xc8, 0xc8);
+}
+
+Color RenderThemeChromiumLinux::inactiveListBoxSelectionForegroundColor() const
+{
+ return Color(0x32, 0x32, 0x32);
+}
+
+Color RenderThemeChromiumLinux::platformActiveSelectionBackgroundColor() const
+{
+ return m_activeSelectionBackgroundColor;
+}
+
+Color RenderThemeChromiumLinux::platformInactiveSelectionBackgroundColor() const
+{
+ return m_inactiveSelectionBackgroundColor;
+}
+
+Color RenderThemeChromiumLinux::platformActiveSelectionForegroundColor() const
+{
+ return m_activeSelectionForegroundColor;
+}
+
+Color RenderThemeChromiumLinux::platformInactiveSelectionForegroundColor() const
+{
+ return m_inactiveSelectionForegroundColor;
+}
+
+void RenderThemeChromiumLinux::adjustSliderThumbSize(RenderObject* o) const
+{
+ // These sizes match the sizes in Chromium Win.
+ const int sliderThumbAlongAxis = 11;
+ const int sliderThumbAcrossAxis = 21;
+ if (o->style()->appearance() == SliderThumbHorizontalPart) {
+ o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed));
+ o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed));
+ } else if (o->style()->appearance() == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed));
+ o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed));
+ } else
+ RenderThemeChromiumSkia::adjustSliderThumbSize(o);
+}
+
+bool RenderThemeChromiumLinux::supportsControlTints() const
+{
+ return true;
+}
+
+void RenderThemeChromiumLinux::setCaretBlinkInterval(double interval)
+{
+ m_caretBlinkInterval = interval;
+}
+
+double RenderThemeChromiumLinux::caretBlinkIntervalInternal() const
+{
+ return m_caretBlinkInterval;
+}
+
+void RenderThemeChromiumLinux::setSelectionColors(
+ unsigned activeBackgroundColor,
+ unsigned activeForegroundColor,
+ unsigned inactiveBackgroundColor,
+ unsigned inactiveForegroundColor)
+{
+ m_activeSelectionBackgroundColor = activeBackgroundColor;
+ m_activeSelectionForegroundColor = activeForegroundColor;
+ m_inactiveSelectionBackgroundColor = inactiveBackgroundColor;
+ m_inactiveSelectionForegroundColor = inactiveForegroundColor;
+}
+
+void RenderThemeChromiumLinux::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ int width = ScrollbarTheme::nativeTheme()->scrollbarThickness();
+ style->setWidth(Length(width, Fixed));
+ style->setMinWidth(Length(width, Fixed));
+}
+
+bool RenderThemeChromiumLinux::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ ControlStates northStates = controlStatesForRenderer(object);
+ ControlStates southStates = northStates;
+ if (northStates & SpinUpState)
+ southStates &= ~(HoverState | PressedState);
+ else
+ northStates &= ~(HoverState | PressedState);
+
+ IntRect half = rect;
+ half.setHeight(rect.height() / 2);
+ PlatformThemeChromiumGtk::paintArrowButton(info.context, half, PlatformThemeChromiumGtk::North, northStates);
+
+ half.setY(rect.y() + rect.height() / 2);
+ PlatformThemeChromiumGtk::paintArrowButton(info.context, half, PlatformThemeChromiumGtk::South, southStates);
+ return false;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeChromiumLinux.h b/Source/WebCore/rendering/RenderThemeChromiumLinux.h
new file mode 100644
index 0000000..9eeca97
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumLinux.h
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
+ * Copyright (C) 2007 Holger Hans Peter Freyther
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008, 2009 Google, Inc.
+ * All rights reserved.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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 RenderThemeChromiumLinux_h
+#define RenderThemeChromiumLinux_h
+
+#include "RenderThemeChromiumSkia.h"
+
+namespace WebCore {
+
+ class RenderThemeChromiumLinux : public RenderThemeChromiumSkia {
+ public:
+ static PassRefPtr<RenderTheme> create();
+ virtual String extraDefaultStyleSheet();
+
+ virtual Color systemColor(int cssValidId) const;
+
+ // A method asking if the control changes its tint when the window has focus or not.
+ virtual bool controlSupportsTints(const RenderObject*) const;
+
+ // List Box selection color
+ virtual Color activeListBoxSelectionBackgroundColor() const;
+ virtual Color activeListBoxSelectionForegroundColor() const;
+ virtual Color inactiveListBoxSelectionBackgroundColor() const;
+ virtual Color inactiveListBoxSelectionForegroundColor() const;
+
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ static void setCaretBlinkInterval(double interval);
+ virtual double caretBlinkIntervalInternal() const;
+
+ virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ static void setSelectionColors(unsigned activeBackgroundColor,
+ unsigned activeForegroundColor,
+ unsigned inactiveBackgroundColor,
+ unsigned inactiveForegroundColor);
+
+ private:
+ RenderThemeChromiumLinux();
+ virtual ~RenderThemeChromiumLinux();
+
+ // A general method asking if any control tinting is supported at all.
+ virtual bool supportsControlTints() const;
+
+ static double m_caretBlinkInterval;
+
+ static unsigned m_activeSelectionBackgroundColor;
+ static unsigned m_activeSelectionForegroundColor;
+ static unsigned m_inactiveSelectionBackgroundColor;
+ static unsigned m_inactiveSelectionForegroundColor;
+ };
+
+} // namespace WebCore
+
+#endif // RenderThemeChromiumLinux_h
diff --git a/Source/WebCore/rendering/RenderThemeChromiumMac.h b/Source/WebCore/rendering/RenderThemeChromiumMac.h
new file mode 100644
index 0000000..d1875fc
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumMac.h
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the theme implementation for form controls in WebCore.
+ *
+ * Copyright (C) 2005 Apple Computer, Inc.
+ * Copyright (C) 2008, 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 RenderThemeChromiumMac_h
+#define RenderThemeChromiumMac_h
+
+#import "RenderThemeMac.h"
+
+namespace WebCore {
+
+class RenderThemeChromiumMac : public RenderThemeMac {
+public:
+ static PassRefPtr<RenderTheme> create();
+protected:
+#if ENABLE(VIDEO)
+ virtual void adjustMediaSliderThumbSize(RenderObject*) const;
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool shouldRenderMediaControlPart(ControlPart, Element*);
+ virtual String extraMediaControlsStyleSheet();
+
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual IntPoint volumeSliderOffsetFromMuteButton(Node*, const IntSize&) const;
+
+#endif
+
+ virtual bool usesTestModeFocusRingColor() const;
+ virtual NSView* documentViewFor(RenderObject*) const;
+private:
+ virtual void updateActiveState(NSCell*, const RenderObject*);
+};
+
+} // namespace WebCore
+
+#endif // RenderThemeChromiumMac_h
diff --git a/Source/WebCore/rendering/RenderThemeChromiumMac.mm b/Source/WebCore/rendering/RenderThemeChromiumMac.mm
new file mode 100644
index 0000000..e8ffe6c
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumMac.mm
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 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.
+ */
+
+#import "config.h"
+#import "RenderThemeChromiumMac.h"
+#import "ChromiumBridge.h"
+#import "RenderMediaControlsChromium.h"
+#import "UserAgentStyleSheets.h"
+#import <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/StdLibExtras.h>
+#import <math.h>
+
+@interface RTCMFlippedView : NSView
+{}
+
+- (BOOL)isFlipped;
+- (NSText *)currentEditor;
+
+@end
+
+@implementation RTCMFlippedView
+
+- (BOOL)isFlipped {
+ return [[NSGraphicsContext currentContext] isFlipped];
+}
+
+- (NSText *)currentEditor {
+ return nil;
+}
+
+@end
+
+namespace WebCore {
+
+NSView* FlippedView()
+{
+ static NSView* view = [[RTCMFlippedView alloc] init];
+ return view;
+}
+
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
+{
+ static RenderTheme* rt = RenderThemeChromiumMac::create().releaseRef();
+ return rt;
+}
+
+PassRefPtr<RenderTheme> RenderThemeChromiumMac::create()
+{
+ return adoptRef(new RenderThemeChromiumMac);
+}
+
+bool RenderThemeChromiumMac::usesTestModeFocusRingColor() const
+{
+ return ChromiumBridge::layoutTestMode();
+}
+
+NSView* RenderThemeChromiumMac::documentViewFor(RenderObject*) const
+{
+ return FlippedView();
+}
+
+// Updates the control tint (a.k.a. active state) of |cell| (from |o|).
+// In the Chromium port, the renderer runs as a background process and controls'
+// NSCell(s) lack a parent NSView. Therefore controls don't have their tint
+// color updated correctly when the application is activated/deactivated.
+// FocusController's setActive() is called when the application is
+// activated/deactivated, which causes a repaint at which time this code is
+// called.
+// This function should be called before drawing any NSCell-derived controls,
+// unless you're sure it isn't needed.
+void RenderThemeChromiumMac::updateActiveState(NSCell* cell, const RenderObject* o)
+{
+ NSControlTint oldTint = [cell controlTint];
+ NSControlTint tint = isActive(o) ? [NSColor currentControlTint] :
+ static_cast<NSControlTint>(NSClearControlTint);
+
+ if (tint != oldTint)
+ [cell setControlTint:tint];
+}
+
+#if ENABLE(VIDEO)
+
+void RenderThemeChromiumMac::adjustMediaSliderThumbSize(RenderObject* o) const
+{
+ RenderMediaControlsChromium::adjustMediaSliderThumbSize(o);
+}
+
+bool RenderThemeChromiumMac::shouldRenderMediaControlPart(ControlPart part, Element* e)
+{
+ return RenderMediaControlsChromium::shouldRenderMediaControlPart(part, e);
+}
+
+bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect);
+}
+
+bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect);
+}
+
+bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect);
+}
+
+bool RenderThemeChromiumMac::paintMediaControlsBackground(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaTimelineContainer, object, paintInfo, rect);
+}
+
+String RenderThemeChromiumMac::extraMediaControlsStyleSheet()
+{
+ return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet));
+}
+
+bool RenderThemeChromiumMac::paintMediaVolumeSliderContainer(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return true;
+}
+
+bool RenderThemeChromiumMac::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect);
+}
+
+bool RenderThemeChromiumMac::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect);
+}
+
+bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect);
+}
+
+IntPoint RenderThemeChromiumMac::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size) const
+{
+ return RenderTheme::volumeSliderOffsetFromMuteButton(muteButton, size);
+}
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp b/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp
new file mode 100644
index 0000000..9824851
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumSkia.cpp
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2007 Apple Inc.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2008, 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.
+ *
+ */
+
+#include "config.h"
+#include "RenderThemeChromiumSkia.h"
+
+#include "ChromiumBridge.h"
+#include "CSSValueKeywords.h"
+#include "CurrentTime.h"
+#include "GraphicsContext.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "Image.h"
+#include "MediaControlElements.h"
+#include "PlatformContextSkia.h"
+#include "RenderBox.h"
+#include "RenderMediaControlsChromium.h"
+#include "RenderObject.h"
+#include "RenderProgress.h"
+#include "RenderSlider.h"
+#include "ScrollbarTheme.h"
+#include "TimeRanges.h"
+#include "TransformationMatrix.h"
+#include "UserAgentStyleSheets.h"
+
+#include "SkShader.h"
+#include "SkGradientShader.h"
+
+namespace WebCore {
+
+enum PaddingType {
+ TopPadding,
+ RightPadding,
+ BottomPadding,
+ LeftPadding
+};
+
+static const int styledMenuListInternalPadding[4] = { 1, 4, 1, 4 };
+
+// These values all match Safari/Win.
+static const float defaultControlFontPixelSize = 13;
+static const float defaultCancelButtonSize = 9;
+static const float minCancelButtonSize = 5;
+static const float maxCancelButtonSize = 21;
+static const float defaultSearchFieldResultsDecorationSize = 13;
+static const float minSearchFieldResultsDecorationSize = 9;
+static const float maxSearchFieldResultsDecorationSize = 30;
+static const float defaultSearchFieldResultsButtonWidth = 18;
+
+static void setSizeIfAuto(RenderStyle* style, const IntSize& size)
+{
+ if (style->width().isIntrinsicOrAuto())
+ style->setWidth(Length(size.width(), Fixed));
+ if (style->height().isAuto())
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+static void drawVertLine(SkCanvas* canvas, int x, int y1, int y2, const SkPaint& paint)
+{
+ SkIRect skrect;
+ skrect.set(x, y1, x + 1, y2 + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+static void drawHorizLine(SkCanvas* canvas, int x1, int x2, int y, const SkPaint& paint)
+{
+ SkIRect skrect;
+ skrect.set(x1, y, x2 + 1, y + 1);
+ canvas->drawIRect(skrect, paint);
+}
+
+static void drawBox(SkCanvas* canvas, const IntRect& rect, const SkPaint& paint)
+{
+ const int right = rect.x() + rect.width() - 1;
+ const int bottom = rect.y() + rect.height() - 1;
+ drawHorizLine(canvas, rect.x(), right, rect.y(), paint);
+ drawVertLine(canvas, right, rect.y(), bottom, paint);
+ drawHorizLine(canvas, rect.x(), right, bottom, paint);
+ drawVertLine(canvas, rect.x(), rect.y(), bottom, paint);
+}
+
+// We aim to match IE here.
+// -IE uses a font based on the encoding as the default font for form controls.
+// -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
+// which returns MS Shell Dlg)
+// -Safari uses Lucida Grande.
+//
+// FIXME: The only case where we know we don't match IE is for ANSI encodings.
+// IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
+// sizes (e.g. 15px). So, for now we just use Arial.
+const String& RenderThemeChromiumSkia::defaultGUIFont()
+{
+ DEFINE_STATIC_LOCAL(String, fontFace, ("Arial"));
+ return fontFace;
+}
+
+float RenderThemeChromiumSkia::defaultFontSize = 16.0;
+
+RenderThemeChromiumSkia::RenderThemeChromiumSkia()
+{
+}
+
+RenderThemeChromiumSkia::~RenderThemeChromiumSkia()
+{
+}
+
+// Use the Windows style sheets to match their metrics.
+String RenderThemeChromiumSkia::extraDefaultStyleSheet()
+{
+ return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)) +
+ String(themeChromiumSkiaUserAgentStyleSheet, sizeof(themeChromiumSkiaUserAgentStyleSheet));
+}
+
+String RenderThemeChromiumSkia::extraQuirksStyleSheet()
+{
+ return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
+}
+
+#if ENABLE(VIDEO)
+String RenderThemeChromiumSkia::extraMediaControlsStyleSheet()
+{
+ return String(mediaControlsChromiumUserAgentStyleSheet, sizeof(mediaControlsChromiumUserAgentStyleSheet));
+}
+#endif
+
+bool RenderThemeChromiumSkia::supportsHover(const RenderStyle* style) const
+{
+ return true;
+}
+
+bool RenderThemeChromiumSkia::supportsFocusRing(const RenderStyle* style) const
+{
+ // This causes WebKit to draw the focus rings for us.
+ return false;
+}
+
+Color RenderThemeChromiumSkia::platformActiveSelectionBackgroundColor() const
+{
+ return Color(0x1e, 0x90, 0xff);
+}
+
+Color RenderThemeChromiumSkia::platformInactiveSelectionBackgroundColor() const
+{
+ return Color(0xc8, 0xc8, 0xc8);
+}
+
+Color RenderThemeChromiumSkia::platformActiveSelectionForegroundColor() const
+{
+ return Color::black;
+}
+
+Color RenderThemeChromiumSkia::platformInactiveSelectionForegroundColor() const
+{
+ return Color(0x32, 0x32, 0x32);
+}
+
+Color RenderThemeChromiumSkia::platformFocusRingColor() const
+{
+ static Color focusRingColor(229, 151, 0, 255);
+ return focusRingColor;
+}
+
+double RenderThemeChromiumSkia::caretBlinkInterval() const
+{
+ // Disable the blinking caret in layout test mode, as it introduces
+ // a race condition for the pixel tests. http://b/1198440
+ if (ChromiumBridge::layoutTestMode())
+ return 0;
+
+ return caretBlinkIntervalInternal();
+}
+
+void RenderThemeChromiumSkia::systemFont(int propId, FontDescription& fontDescription) const
+{
+ float fontSize = defaultFontSize;
+
+ switch (propId) {
+ case CSSValueWebkitMiniControl:
+ case CSSValueWebkitSmallControl:
+ case CSSValueWebkitControl:
+ // Why 2 points smaller? Because that's what Gecko does. Note that we
+ // are assuming a 96dpi screen, which is the default that we use on
+ // Windows.
+ static const float pointsPerInch = 72.0f;
+ static const float pixelsPerInch = 96.0f;
+ fontSize -= (2.0f / pointsPerInch) * pixelsPerInch;
+ break;
+ }
+
+ fontDescription.firstFamily().setFamily(defaultGUIFont());
+ fontDescription.setSpecifiedSize(fontSize);
+ fontDescription.setIsAbsoluteSize(true);
+ fontDescription.setGenericFamily(FontDescription::NoFamily);
+ fontDescription.setWeight(FontWeightNormal);
+ fontDescription.setItalic(false);
+}
+
+int RenderThemeChromiumSkia::minimumMenuListSize(RenderStyle* style) const
+{
+ return 0;
+}
+
+// These are the default dimensions of radio buttons and checkboxes.
+static const int widgetStandardWidth = 13;
+static const int widgetStandardHeight = 13;
+
+// Return a rectangle that has the same center point as |original|, but with a
+// size capped at |width| by |height|.
+IntRect center(const IntRect& original, int width, int height)
+{
+ width = std::min(original.width(), width);
+ height = std::min(original.height(), height);
+ int x = original.x() + (original.width() - width) / 2;
+ int y = original.y() + (original.height() - height) / 2;
+
+ return IntRect(x, y, width, height);
+}
+
+bool RenderThemeChromiumSkia::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ static Image* const checkedImage = Image::loadPlatformResource("linuxCheckboxOn").releaseRef();
+ static Image* const uncheckedImage = Image::loadPlatformResource("linuxCheckboxOff").releaseRef();
+ static Image* const indeterminateImage = Image::loadPlatformResource("linuxCheckboxIndeterminate").releaseRef();
+ static Image* const disabledCheckedImage = Image::loadPlatformResource("linuxCheckboxDisabledOn").releaseRef();
+ static Image* const disabledUncheckedImage = Image::loadPlatformResource("linuxCheckboxDisabledOff").releaseRef();
+ static Image* const disabledIndeterminateImage = Image::loadPlatformResource("linuxCheckboxDisabledIndeterminate").releaseRef();
+
+ Image* image;
+
+ if (isIndeterminate(o))
+ image = isEnabled(o) ? indeterminateImage : disabledIndeterminateImage;
+ else if (isChecked(o))
+ image = isEnabled(o) ? checkedImage : disabledCheckedImage;
+ else
+ image = isEnabled(o) ? uncheckedImage : disabledUncheckedImage;
+
+ i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth));
+ return false;
+}
+
+void RenderThemeChromiumSkia::setCheckboxSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // FIXME: A hard-coded size of 13 is used. This is wrong but necessary
+ // for now. It matches Firefox. At different DPI settings on Windows,
+ // querying the theme gives you a larger size that accounts for the higher
+ // DPI. Until our entire engine honors a DPI setting other than 96, we
+ // can't rely on the theme's metrics.
+ const IntSize size(widgetStandardHeight, widgetStandardWidth);
+ setSizeIfAuto(style, size);
+}
+
+bool RenderThemeChromiumSkia::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ static Image* const checkedImage = Image::loadPlatformResource("linuxRadioOn").releaseRef();
+ static Image* const uncheckedImage = Image::loadPlatformResource("linuxRadioOff").releaseRef();
+ static Image* const disabledCheckedImage = Image::loadPlatformResource("linuxRadioDisabledOn").releaseRef();
+ static Image* const disabledUncheckedImage = Image::loadPlatformResource("linuxRadioDisabledOff").releaseRef();
+
+ Image* image;
+ if (this->isEnabled(o))
+ image = this->isChecked(o) ? checkedImage : uncheckedImage;
+ else
+ image = this->isChecked(o) ? disabledCheckedImage : disabledUncheckedImage;
+
+ i.context->drawImage(image, o->style()->colorSpace(), center(rect, widgetStandardHeight, widgetStandardWidth));
+ return false;
+}
+
+void RenderThemeChromiumSkia::setRadioSize(RenderStyle* style) const
+{
+ // Use same sizing for radio box as checkbox.
+ setCheckboxSize(style);
+}
+
+static SkColor brightenColor(double h, double s, double l, float brightenAmount)
+{
+ l += brightenAmount;
+ if (l > 1.0)
+ l = 1.0;
+ if (l < 0.0)
+ l = 0.0;
+
+ return makeRGBAFromHSLA(h, s, l, 1.0);
+}
+
+static void paintButtonLike(RenderTheme* theme, RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ SkCanvas* const canvas = i.context->platformContext()->canvas();
+ SkPaint paint;
+ SkRect skrect;
+ const int right = rect.x() + rect.width();
+ const int bottom = rect.y() + rect.height();
+ SkColor baseColor = SkColorSetARGB(0xff, 0xdd, 0xdd, 0xdd);
+ if (o->hasBackground())
+ baseColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor).rgb();
+ double h, s, l;
+ Color(baseColor).getHSL(h, s, l);
+ // Our standard gradient is from 0xdd to 0xf8. This is the amount of
+ // increased luminance between those values.
+ SkColor lightColor(brightenColor(h, s, l, 0.105));
+
+ // If the button is too small, fallback to drawing a single, solid color
+ if (rect.width() < 5 || rect.height() < 5) {
+ paint.setColor(baseColor);
+ skrect.set(rect.x(), rect.y(), right, bottom);
+ canvas->drawRect(skrect, paint);
+ return;
+ }
+
+ const int borderAlpha = theme->isHovered(o) ? 0x80 : 0x55;
+ paint.setARGB(borderAlpha, 0, 0, 0);
+ canvas->drawLine(rect.x() + 1, rect.y(), right - 1, rect.y(), paint);
+ canvas->drawLine(right - 1, rect.y() + 1, right - 1, bottom - 1, paint);
+ canvas->drawLine(rect.x() + 1, bottom - 1, right - 1, bottom - 1, paint);
+ canvas->drawLine(rect.x(), rect.y() + 1, rect.x(), bottom - 1, paint);
+
+ paint.setColor(SK_ColorBLACK);
+ SkPoint p[2];
+ const int lightEnd = theme->isPressed(o) ? 1 : 0;
+ const int darkEnd = !lightEnd;
+ p[lightEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(rect.y()));
+ p[darkEnd].set(SkIntToScalar(rect.x()), SkIntToScalar(bottom - 1));
+ SkColor colors[2];
+ colors[0] = lightColor;
+ colors[1] = baseColor;
+
+ SkShader* shader = SkGradientShader::CreateLinear(
+ p, colors, NULL, 2, SkShader::kClamp_TileMode, NULL);
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setShader(shader);
+ shader->unref();
+
+ skrect.set(rect.x() + 1, rect.y() + 1, right - 1, bottom - 1);
+ canvas->drawRect(skrect, paint);
+
+ paint.setShader(NULL);
+ paint.setColor(brightenColor(h, s, l, -0.0588));
+ canvas->drawPoint(rect.x() + 1, rect.y() + 1, paint);
+ canvas->drawPoint(right - 2, rect.y() + 1, paint);
+ canvas->drawPoint(rect.x() + 1, bottom - 2, paint);
+ canvas->drawPoint(right - 2, bottom - 2, paint);
+}
+
+bool RenderThemeChromiumSkia::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ paintButtonLike(this, o, i, rect);
+ return false;
+}
+
+void RenderThemeChromiumSkia::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ if (style->appearance() == PushButtonPart) {
+ // Ignore line-height.
+ style->setLineHeight(RenderStyle::initialLineHeight());
+ }
+}
+
+
+bool RenderThemeChromiumSkia::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ return true;
+}
+
+bool RenderThemeChromiumSkia::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintTextField(o, i, r);
+}
+
+void RenderThemeChromiumSkia::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // Ignore line-height.
+ style->setLineHeight(RenderStyle::initialLineHeight());
+}
+
+bool RenderThemeChromiumSkia::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintTextField(o, i, r);
+}
+
+void RenderThemeChromiumSkia::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // Scale the button size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
+ style->setWidth(Length(cancelButtonSize, Fixed));
+ style->setHeight(Length(cancelButtonSize, Fixed));
+}
+
+IntRect RenderThemeChromiumSkia::convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const
+{
+ // Compute an offset between the part renderer and the input renderer.
+ IntSize offsetFromInputRenderer = -(partRenderer->offsetFromAncestorContainer(inputRenderer));
+ // Move the rect into partRenderer's coords.
+ partRect.move(offsetFromInputRenderer);
+ // Account for the local drawing offset.
+ partRect.move(localOffset.x(), localOffset.y());
+
+ return partRect;
+}
+
+bool RenderThemeChromiumSkia::paintSearchFieldCancelButton(RenderObject* cancelButtonObject, const PaintInfo& paintInfo, const IntRect& r)
+{
+ // Get the renderer of <input> element.
+ Node* input = cancelButtonObject->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 cancelButtonSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), r.height()));
+ // Calculate cancel 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 cancelButtonRect(cancelButtonObject->offsetFromAncestorContainer(inputRenderBox).width(),
+ inputContentBox.y() + (inputContentBox.height() - cancelButtonSize + 1) / 2,
+ cancelButtonSize, cancelButtonSize);
+ IntRect paintingRect = convertToPaintingRect(inputRenderBox, cancelButtonObject, cancelButtonRect, r);
+
+ static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
+ static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
+ paintInfo.context->drawImage(isPressed(cancelButtonObject) ? cancelPressedImage : cancelImage,
+ cancelButtonObject->style()->colorSpace(), paintingRect);
+ return false;
+}
+
+void RenderThemeChromiumSkia::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ IntSize emptySize(1, 11);
+ style->setWidth(Length(emptySize.width(), Fixed));
+ style->setHeight(Length(emptySize.height(), Fixed));
+}
+
+void RenderThemeChromiumSkia::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // Scale the decoration size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int magnifierSize = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
+ maxSearchFieldResultsDecorationSize));
+ style->setWidth(Length(magnifierSize, Fixed));
+ style->setHeight(Length(magnifierSize, Fixed));
+}
+
+bool RenderThemeChromiumSkia::paintSearchFieldResultsDecoration(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r)
+{
+ // Get the renderer of <input> element.
+ Node* input = magnifierObject->node()->shadowAncestorNode();
+ if (!input->renderer()->isBox())
+ return false;
+ RenderBox* inputRenderBox = toRenderBox(input->renderer());
+ IntRect inputContentBox = inputRenderBox->contentBoxRect();
+
+ // Make sure the scaled decoration stays square and will fit in its parent's box.
+ int magnifierSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), r.height()));
+ // Calculate decoration's coordinates relative to the input element.
+ // Center the decoration 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 magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(),
+ inputContentBox.y() + (inputContentBox.height() - magnifierSize + 1) / 2,
+ magnifierSize, magnifierSize);
+ IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r);
+
+ static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
+ paintInfo.context->drawImage(magnifierImage, magnifierObject->style()->colorSpace(), paintingRect);
+ return false;
+}
+
+void RenderThemeChromiumSkia::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ // Scale the button size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int magnifierHeight = lroundf(std::min(std::max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
+ maxSearchFieldResultsDecorationSize));
+ int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
+ style->setWidth(Length(magnifierWidth, Fixed));
+ style->setHeight(Length(magnifierHeight, Fixed));
+}
+
+bool RenderThemeChromiumSkia::paintSearchFieldResultsButton(RenderObject* magnifierObject, const PaintInfo& paintInfo, const IntRect& r)
+{
+ // Get the renderer of <input> element.
+ Node* input = magnifierObject->node()->shadowAncestorNode();
+ if (!input->renderer()->isBox())
+ return false;
+ RenderBox* inputRenderBox = toRenderBox(input->renderer());
+ IntRect inputContentBox = inputRenderBox->contentBoxRect();
+
+ // Make sure the scaled decoration will fit in its parent's box.
+ int magnifierHeight = std::min(inputContentBox.height(), r.height());
+ int magnifierWidth = std::min(inputContentBox.width(), static_cast<int>(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize));
+ IntRect magnifierRect(magnifierObject->offsetFromAncestorContainer(inputRenderBox).width(),
+ inputContentBox.y() + (inputContentBox.height() - magnifierHeight + 1) / 2,
+ magnifierWidth, magnifierHeight);
+ IntRect paintingRect = convertToPaintingRect(inputRenderBox, magnifierObject, magnifierRect, r);
+
+ static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
+ paintInfo.context->drawImage(magnifierImage, magnifierObject->style()->colorSpace(), paintingRect);
+ return false;
+}
+
+bool RenderThemeChromiumSkia::paintMediaControlsBackground(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaTimelineContainer, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaSlider, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSlider, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+void RenderThemeChromiumSkia::adjustSliderThumbSize(RenderObject* object) const
+{
+#if ENABLE(VIDEO)
+ RenderMediaControlsChromium::adjustMediaSliderThumbSize(object);
+#else
+ UNUSED_PARAM(object);
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaSliderThumb, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaVolumeSliderThumb, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaPlayButton, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+bool RenderThemeChromiumSkia::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
+{
+#if ENABLE(VIDEO)
+ return RenderMediaControlsChromium::paintMediaControlsPart(MediaMuteButton, object, paintInfo, rect);
+#else
+ UNUSED_PARAM(object);
+ UNUSED_PARAM(paintInfo);
+ UNUSED_PARAM(rect);
+ return false;
+#endif
+}
+
+void RenderThemeChromiumSkia::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
+{
+ // Height is locked to auto on all browsers.
+ style->setLineHeight(RenderStyle::initialLineHeight());
+}
+
+bool RenderThemeChromiumSkia::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ SkCanvas* const canvas = i.context->platformContext()->canvas();
+ const int right = rect.x() + rect.width();
+ const int middle = rect.y() + rect.height() / 2;
+
+ paintButtonLike(this, o, i, rect);
+
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kFill_Style);
+
+ int arrowXPosition = (o->style()->direction() == RTL) ? rect.x() + 7 : right - 13;
+ SkPath path;
+ path.moveTo(arrowXPosition, middle - 3);
+ path.rLineTo(6, 0);
+ path.rLineTo(-3, 6);
+ path.close();
+ canvas->drawPath(path, paint);
+
+ return false;
+}
+
+void RenderThemeChromiumSkia::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ adjustMenuListStyle(selector, style, e);
+}
+
+// Used to paint styled menulists (i.e. with a non-default border)
+bool RenderThemeChromiumSkia::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ return paintMenuList(o, i, rect);
+}
+
+bool RenderThemeChromiumSkia::paintSliderTrack(RenderObject*, const PaintInfo& i, const IntRect& rect)
+{
+ // Just paint a grey box for now (matches the color of a scrollbar background.
+ SkCanvas* const canvas = i.context->platformContext()->canvas();
+ int verticalCenter = rect.y() + rect.height() / 2;
+ int top = std::max(rect.y(), verticalCenter - 2);
+ int bottom = std::min(rect.y() + rect.height(), verticalCenter + 2);
+
+ SkPaint paint;
+ const SkColor grey = SkColorSetARGB(0xff, 0xe3, 0xdd, 0xd8);
+ paint.setColor(grey);
+
+ SkRect skrect;
+ skrect.set(rect.x(), top, rect.x() + rect.width(), bottom);
+ canvas->drawRect(skrect, paint);
+
+ return false;
+}
+
+bool RenderThemeChromiumSkia::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& rect)
+{
+ // Make a thumb similar to the scrollbar thumb.
+ const bool hovered = isHovered(o) || toRenderSlider(o->parent())->inDragMode();
+ const int midx = rect.x() + rect.width() / 2;
+ const int midy = rect.y() + rect.height() / 2;
+ const bool vertical = (o->style()->appearance() == SliderThumbVerticalPart);
+ SkCanvas* const canvas = i.context->platformContext()->canvas();
+
+ const SkColor thumbLightGrey = SkColorSetARGB(0xff, 0xf4, 0xf2, 0xef);
+ const SkColor thumbDarkGrey = SkColorSetARGB(0xff, 0xea, 0xe5, 0xe0);
+ SkPaint paint;
+ paint.setColor(hovered ? SK_ColorWHITE : thumbLightGrey);
+
+ SkIRect skrect;
+ if (vertical)
+ skrect.set(rect.x(), rect.y(), midx + 1, rect.bottom());
+ else
+ skrect.set(rect.x(), rect.y(), rect.right(), midy + 1);
+
+ canvas->drawIRect(skrect, paint);
+
+ paint.setColor(hovered ? thumbLightGrey : thumbDarkGrey);
+
+ if (vertical)
+ skrect.set(midx + 1, rect.y(), rect.right(), rect.bottom());
+ else
+ skrect.set(rect.x(), midy + 1, rect.right(), rect.bottom());
+
+ canvas->drawIRect(skrect, paint);
+
+ const SkColor borderDarkGrey = SkColorSetARGB(0xff, 0x9d, 0x96, 0x8e);
+ paint.setColor(borderDarkGrey);
+ drawBox(canvas, rect, paint);
+
+ if (rect.height() > 10 && rect.width() > 10) {
+ drawHorizLine(canvas, midx - 2, midx + 2, midy, paint);
+ drawHorizLine(canvas, midx - 2, midx + 2, midy - 3, paint);
+ drawHorizLine(canvas, midx - 2, midx + 2, midy + 3, paint);
+ }
+
+ return false;
+}
+
+int RenderThemeChromiumSkia::popupInternalPaddingLeft(RenderStyle* style) const
+{
+ return menuListInternalPadding(style, LeftPadding);
+}
+
+int RenderThemeChromiumSkia::popupInternalPaddingRight(RenderStyle* style) const
+{
+ return menuListInternalPadding(style, RightPadding);
+}
+
+int RenderThemeChromiumSkia::popupInternalPaddingTop(RenderStyle* style) const
+{
+ return menuListInternalPadding(style, TopPadding);
+}
+
+int RenderThemeChromiumSkia::popupInternalPaddingBottom(RenderStyle* style) const
+{
+ return menuListInternalPadding(style, BottomPadding);
+}
+
+#if ENABLE(VIDEO)
+bool RenderThemeChromiumSkia::shouldRenderMediaControlPart(ControlPart part, Element* e)
+{
+ return RenderMediaControlsChromium::shouldRenderMediaControlPart(part, e);
+}
+#endif
+
+// static
+void RenderThemeChromiumSkia::setDefaultFontSize(int fontSize)
+{
+ defaultFontSize = static_cast<float>(fontSize);
+}
+
+double RenderThemeChromiumSkia::caretBlinkIntervalInternal() const
+{
+ return RenderTheme::caretBlinkInterval();
+}
+
+int RenderThemeChromiumSkia::menuListInternalPadding(RenderStyle* style, int paddingType) const
+{
+ // This internal padding is in addition to the user-supplied padding.
+ // Matches the FF behavior.
+ int padding = styledMenuListInternalPadding[paddingType];
+
+ // Reserve the space for right arrow here. The rest of the padding is
+ // set by adjustMenuListStyle, since PopMenuWin.cpp uses the padding from
+ // RenderMenuList to lay out the individual items in the popup.
+ // If the MenuList actually has appearance "NoAppearance", then that means
+ // we don't draw a button, so don't reserve space for it.
+ const int barType = style->direction() == LTR ? RightPadding : LeftPadding;
+ if (paddingType == barType && style->appearance() != NoControlPart)
+ padding += ScrollbarTheme::nativeTheme()->scrollbarThickness();
+
+ return padding;
+}
+
+#if ENABLE(PROGRESS_TAG)
+
+//
+// Following values are come from default of GTK+
+//
+static const int progressDeltaPixelsPerSecond = 100;
+static const int progressActivityBlocks = 5;
+static const int progressAnimationFrmaes = 10;
+static const double progressAnimationInterval = 0.125;
+
+IntRect RenderThemeChromiumSkia::determinateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const
+{
+ int dx = rect.width() * renderProgress->position();
+ if (renderProgress->style()->direction() == RTL)
+ return IntRect(rect.x() + rect.width() - dx, rect.y(), dx, rect.height());
+ return IntRect(rect.x(), rect.y(), dx, rect.height());
+}
+
+IntRect RenderThemeChromiumSkia::indeterminateProgressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const
+{
+
+ int valueWidth = rect.width() / progressActivityBlocks;
+ int movableWidth = rect.width() - valueWidth;
+ if (movableWidth <= 0)
+ return IntRect();
+
+ double progress = renderProgress->animationProgress();
+ if (progress < 0.5)
+ return IntRect(rect.x() + progress * 2 * movableWidth, rect.y(), valueWidth, rect.height());
+ return IntRect(rect.x() + (1.0 - progress) * 2 * movableWidth, rect.y(), valueWidth, rect.height());
+}
+
+double RenderThemeChromiumSkia::animationRepeatIntervalForProgressBar(RenderProgress*) const
+{
+ return progressAnimationInterval;
+}
+
+double RenderThemeChromiumSkia::animationDurationForProgressBar(RenderProgress* renderProgress) const
+{
+ return progressAnimationInterval * progressAnimationFrmaes * 2; // "2" for back and forth
+}
+
+bool RenderThemeChromiumSkia::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ static Image* barImage = Image::loadPlatformResource("linuxProgressBar").releaseRef();
+ static Image* valueImage = Image::loadPlatformResource("linuxProgressValue").releaseRef();
+ static Image* leftBorderImage = Image::loadPlatformResource("linuxProgressBorderLeft").releaseRef();
+ static Image* rightBorderImage = Image::loadPlatformResource("linuxProgressBorderRight").releaseRef();
+ ASSERT(barImage->height() == valueImage->height());
+
+ if (!renderObject->isProgress())
+ return true;
+
+ paintInfo.context->platformContext()->setImageResamplingHint(barImage->size(), rect.size());
+
+ RenderProgress* renderProgress = toRenderProgress(renderObject);
+ double tileScale = static_cast<double>(rect.height()) / barImage->height();
+ IntSize barTileSize(static_cast<int>(barImage->width() * tileScale), rect.height());
+ ColorSpace colorSpace = renderObject->style()->colorSpace();
+
+ paintInfo.context->drawTiledImage(barImage, colorSpace, rect, IntPoint(0, 0), barTileSize);
+
+ IntRect valueRect = progressValueRectFor(renderProgress, rect);
+ if (valueRect.width()) {
+
+ IntSize valueTileSize(std::max(1, static_cast<int>(valueImage->width() * tileScale)), valueRect.height());
+
+ int leftOffset = valueRect.x() - rect.x();
+ int roundedLeftOffset= (leftOffset / valueTileSize.width()) * valueTileSize.width();
+ int dstLeftValueWidth = roundedLeftOffset - leftOffset + (leftOffset % valueImage->width()) ? valueTileSize.width() : 0;
+
+ IntRect dstLeftValueRect(valueRect.x(), valueRect.y(), dstLeftValueWidth, valueRect.height());
+ int srcLeftValueWidth = dstLeftValueWidth / tileScale;
+ IntRect srcLeftValueRect(valueImage->width() - srcLeftValueWidth, 0, srcLeftValueWidth, valueImage->height());
+ paintInfo.context->drawImage(valueImage, colorSpace, dstLeftValueRect, srcLeftValueRect);
+
+ int rightOffset = valueRect.right() - rect.x();
+ int roundedRightOffset = (rightOffset / valueTileSize.width()) * valueTileSize.width();
+ int dstRightValueWidth = rightOffset - roundedRightOffset;
+ IntRect dstRightValueRect(rect.x() + roundedRightOffset, valueRect.y(), dstRightValueWidth, valueTileSize.height());
+ int srcRightValueWidth = dstRightValueWidth / tileScale;
+ IntRect srcRightValueRect(0, 0, srcRightValueWidth, valueImage->height());
+ paintInfo.context->drawImage(valueImage, colorSpace, dstRightValueRect, srcRightValueRect);
+
+ IntRect alignedValueRect(dstLeftValueRect.right(), dstLeftValueRect.y(),
+ dstRightValueRect.x() - dstLeftValueRect.right(), dstLeftValueRect.height());
+ paintInfo.context->drawTiledImage(valueImage, colorSpace, alignedValueRect, IntPoint(0, 0), valueTileSize);
+ }
+
+ int dstLeftBorderWidth = leftBorderImage->width() * tileScale;
+ IntRect dstLeftBorderRect(rect.x(), rect.y(), dstLeftBorderWidth, rect.height());
+ paintInfo.context->drawImage(leftBorderImage, colorSpace, dstLeftBorderRect, leftBorderImage->rect());
+
+ int dstRightBorderWidth = rightBorderImage->width() * tileScale;
+ IntRect dstRightBorderRect(rect.right() - dstRightBorderWidth, rect.y(), dstRightBorderWidth, rect.height());
+ paintInfo.context->drawImage(rightBorderImage, colorSpace, dstRightBorderRect, rightBorderImage->rect());
+
+ paintInfo.context->platformContext()->clearImageResamplingHint();
+
+ return false;
+}
+
+
+IntRect RenderThemeChromiumSkia::progressValueRectFor(RenderProgress* renderProgress, const IntRect& rect) const
+{
+ return renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, rect) : indeterminateProgressValueRectFor(renderProgress, rect);
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeChromiumSkia.h b/Source/WebCore/rendering/RenderThemeChromiumSkia.h
new file mode 100644
index 0000000..a11046d
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumSkia.h
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com
+ * Copyright (C) 2007 Holger Hans Peter Freyther
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008, 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 RenderThemeChromiumSkia_h
+#define RenderThemeChromiumSkia_h
+
+#include "RenderTheme.h"
+
+namespace WebCore {
+
+class RenderProgress;
+
+ class RenderThemeChromiumSkia : public RenderTheme {
+ public:
+ RenderThemeChromiumSkia();
+ virtual ~RenderThemeChromiumSkia();
+
+ virtual String extraDefaultStyleSheet();
+ virtual String extraQuirksStyleSheet();
+#if ENABLE(VIDEO)
+ virtual String extraMediaControlsStyleSheet();
+#endif
+
+ // A method asking if the theme's controls actually care about redrawing when hovered.
+ virtual bool supportsHover(const RenderStyle*) const;
+
+ // A method asking if the theme is able to draw the focus ring.
+ virtual bool supportsFocusRing(const RenderStyle*) const;
+
+ // The platform selection color.
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+ virtual Color platformFocusRingColor() const;
+
+ // To change the blink interval, override caretBlinkIntervalInternal instead of this one so that we may share layout test code an intercepts.
+ virtual double caretBlinkInterval() const;
+
+ // System fonts.
+ virtual void systemFont(int propId, FontDescription&) const;
+
+ virtual int minimumMenuListSize(RenderStyle*) const;
+
+ virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void setCheckboxSize(RenderStyle*) const;
+
+ virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void setRadioSize(RenderStyle*) const;
+
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ // MenuList refers to an unstyled menulist (meaning a menulist without
+ // background-color or border set) and MenuListButton refers to a styled
+ // menulist (a menulist with background-color or border set). They have
+ // this distinction to support showing aqua style themes whenever they
+ // possibly can, which is something we don't want to replicate.
+ //
+ // In short, we either go down the MenuList code path or the MenuListButton
+ // codepath. We never go down both. And in both cases, they render the
+ // entire menulist.
+ virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+
+#if ENABLE(PROGRESS_TAG)
+ virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const;
+ virtual double animationDurationForProgressBar(RenderProgress*) const;
+ virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+ // These methods define the padding for the MenuList's inner block.
+ virtual int popupInternalPaddingLeft(RenderStyle*) const;
+ virtual int popupInternalPaddingRight(RenderStyle*) const;
+ virtual int popupInternalPaddingTop(RenderStyle*) const;
+ virtual int popupInternalPaddingBottom(RenderStyle*) const;
+
+#if ENABLE(VIDEO)
+ // Media controls
+ virtual bool shouldRenderMediaControlPart(ControlPart, Element*);
+#endif
+
+ // Provide a way to pass the default font size from the Settings object
+ // to the render theme. FIXME: http://b/1129186 A cleaner way would be
+ // to remove the default font size from this object and have callers
+ // that need the value to get it directly from the appropriate Settings
+ // object.
+ static void setDefaultFontSize(int);
+
+ protected:
+ static const String& defaultGUIFont();
+
+ // The default variable-width font size. We use this as the default font
+ // size for the "system font", and as a base size (which we then shrink) for
+ // form control fonts.
+ static float defaultFontSize;
+
+ virtual double caretBlinkIntervalInternal() const;
+
+#if ENABLE(PROGRESS_TAG)
+ IntRect determinateProgressValueRectFor(RenderProgress*, const IntRect&) const;
+ IntRect indeterminateProgressValueRectFor(RenderProgress*, const IntRect&) const;
+ IntRect progressValueRectFor(RenderProgress*, const IntRect&) const;
+#endif
+
+ private:
+ int menuListInternalPadding(RenderStyle*, int paddingType) const;
+ bool paintMediaButtonInternal(GraphicsContext*, const IntRect&, Image*);
+ IntRect convertToPaintingRect(RenderObject* inputRenderer, const RenderObject* partRenderer, IntRect partRect, const IntRect& localOffset) const;
+ };
+
+} // namespace WebCore
+
+#endif // RenderThemeChromiumSkia_h
diff --git a/Source/WebCore/rendering/RenderThemeChromiumWin.cpp b/Source/WebCore/rendering/RenderThemeChromiumWin.cpp
new file mode 100644
index 0000000..be670ff
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumWin.cpp
@@ -0,0 +1,770 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2008, 2009 Google, Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderThemeChromiumWin.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vssym32.h>
+
+#include "CSSValueKeywords.h"
+#include "ChromiumBridge.h"
+#include "CurrentTime.h"
+#include "FontSelector.h"
+#include "FontUtilsChromiumWin.h"
+#include "GraphicsContext.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "MediaControlElements.h"
+#include "RenderBox.h"
+#include "RenderProgress.h"
+#include "RenderSlider.h"
+#include "ScrollbarTheme.h"
+#include "TransparencyWin.h"
+#include "WindowsVersion.h"
+
+// FIXME: This dependency should eventually be removed.
+#include <skia/ext/skia_utils_win.h>
+
+#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(structName, member) \
+ offsetof(structName, member) + \
+ (sizeof static_cast<structName*>(0)->member)
+#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
+ SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
+
+namespace WebCore {
+
+// The standard width for the menu list drop-down button when run under
+// layout test mode. Use the value that's currently captured in most baselines.
+static const int kStandardMenuListButtonWidth = 17;
+
+namespace {
+class ThemePainter {
+public:
+ ThemePainter(GraphicsContext* context, const IntRect& r)
+ {
+ TransparencyWin::TransformMode transformMode = getTransformMode(context->getCTM());
+ m_helper.init(context, getLayerMode(context, transformMode), transformMode, r);
+
+ if (!m_helper.context()) {
+ // TransparencyWin doesn't have well-defined copy-ctor nor op=()
+ // so we re-initialize it instead of assigning a fresh istance.
+ // On the reinitialization, we fallback to use NoLayer mode.
+ // Note that the original initialization failure can be caused by
+ // a failure of an internal buffer allocation and NoLayer mode
+ // does not have such buffer allocations.
+ m_helper.~TransparencyWin();
+ new (&m_helper) TransparencyWin();
+ m_helper.init(context, TransparencyWin::NoLayer, transformMode, r);
+ }
+ }
+
+ ~ThemePainter()
+ {
+ m_helper.composite();
+ }
+
+ GraphicsContext* context() { return m_helper.context(); }
+ const IntRect& drawRect() { return m_helper.drawRect(); }
+
+private:
+
+ static bool canvasHasMultipleLayers(const SkCanvas* canvas)
+ {
+ SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
+ iter.next(); // There is always at least one layer.
+ return !iter.done(); // There is > 1 layer if the the iterator can stil advance.
+ }
+
+ static TransparencyWin::LayerMode getLayerMode(GraphicsContext* context, TransparencyWin::TransformMode transformMode)
+ {
+ if (context->platformContext()->isDrawingToImageBuffer()) // Might have transparent background.
+ return TransparencyWin::WhiteLayer;
+ if (canvasHasMultipleLayers(context->platformContext()->canvas())) // Needs antialiasing help.
+ return TransparencyWin::OpaqueCompositeLayer;
+ // Nothing interesting.
+ return transformMode == TransparencyWin::KeepTransform ? TransparencyWin::NoLayer : TransparencyWin::OpaqueCompositeLayer;
+ }
+
+ static TransparencyWin::TransformMode getTransformMode(const AffineTransform& matrix)
+ {
+ if (matrix.b() || matrix.c()) // Skew.
+ return TransparencyWin::Untransform;
+ if (matrix.a() != 1.0 || matrix.d() != 1.0) // Scale.
+ return TransparencyWin::ScaleTransform;
+ // Nothing interesting.
+ return TransparencyWin::KeepTransform;
+ }
+
+ TransparencyWin m_helper;
+};
+
+} // namespace
+
+static void getNonClientMetrics(NONCLIENTMETRICS* metrics)
+{
+ static UINT size = isVistaOrNewer() ?
+ sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
+ metrics->cbSize = size;
+ bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, size, metrics, 0);
+ ASSERT(success);
+}
+
+static FontDescription smallSystemFont;
+static FontDescription menuFont;
+static FontDescription labelFont;
+
+// Internal static helper functions. We don't put them in an anonymous
+// namespace so they have easier access to the WebCore namespace.
+
+static bool supportsFocus(ControlPart appearance)
+{
+ switch (appearance) {
+ case PushButtonPart:
+ case ButtonPart:
+ case DefaultButtonPart:
+ case SearchFieldPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ return true;
+ }
+ return false;
+}
+
+// Return the height of system font |font| in pixels. We use this size by
+// default for some non-form-control elements.
+static float systemFontSize(const LOGFONT& font)
+{
+ float size = -font.lfHeight;
+ if (size < 0) {
+ HFONT hFont = CreateFontIndirect(&font);
+ if (hFont) {
+ HDC hdc = GetDC(0); // What about printing? Is this the right DC?
+ if (hdc) {
+ HGDIOBJ hObject = SelectObject(hdc, hFont);
+ TEXTMETRIC tm;
+ GetTextMetrics(hdc, &tm);
+ SelectObject(hdc, hObject);
+ ReleaseDC(0, hdc);
+ size = tm.tmAscent;
+ }
+ DeleteObject(hFont);
+ }
+ }
+
+ // The "codepage 936" bit here is from Gecko; apparently this helps make
+ // fonts more legible in Simplified Chinese where the default font size is
+ // too small.
+ //
+ // FIXME: http://b/1119883 Since this is only used for "small caption",
+ // "menu", and "status bar" objects, I'm not sure how much this even
+ // matters. Plus the Gecko patch went in back in 2002, and maybe this
+ // isn't even relevant anymore. We should investigate whether this should
+ // be removed, or perhaps broadened to be "any CJK locale".
+ //
+ return ((size < 12.0f) && (GetACP() == 936)) ? 12.0f : size;
+}
+
+// Converts |points| to pixels. One point is 1/72 of an inch.
+static float pointsToPixels(float points)
+{
+ static float pixelsPerInch = 0.0f;
+ if (!pixelsPerInch) {
+ HDC hdc = GetDC(0); // What about printing? Is this the right DC?
+ if (hdc) { // Can this ever actually be NULL?
+ pixelsPerInch = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(0, hdc);
+ } else {
+ pixelsPerInch = 96.0f;
+ }
+ }
+
+ static const float pointsPerInch = 72.0f;
+ return points / pointsPerInch * pixelsPerInch;
+}
+
+static double querySystemBlinkInterval(double defaultInterval)
+{
+ UINT blinkTime = GetCaretBlinkTime();
+ if (!blinkTime)
+ return defaultInterval;
+ if (blinkTime == INFINITE)
+ return 0;
+ return blinkTime / 1000.0;
+}
+
+PassRefPtr<RenderTheme> RenderThemeChromiumWin::create()
+{
+ return adoptRef(new RenderThemeChromiumWin);
+}
+
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+ static RenderTheme* rt = RenderThemeChromiumWin::create().releaseRef();
+ return rt;
+}
+
+bool RenderThemeChromiumWin::supportsFocusRing(const RenderStyle* style) const
+{
+ // Let webkit draw one of its halo rings around any focused element,
+ // except push buttons. For buttons we use the windows PBS_DEFAULTED
+ // styling to give it a blue border.
+ return style->appearance() == ButtonPart
+ || style->appearance() == PushButtonPart;
+}
+
+Color RenderThemeChromiumWin::platformActiveSelectionBackgroundColor() const
+{
+ if (ChromiumBridge::layoutTestMode())
+ return Color(0x00, 0x00, 0xff); // Royal blue.
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
+}
+
+Color RenderThemeChromiumWin::platformInactiveSelectionBackgroundColor() const
+{
+ if (ChromiumBridge::layoutTestMode())
+ return Color(0x99, 0x99, 0x99); // Medium gray.
+ COLORREF color = GetSysColor(COLOR_GRAYTEXT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
+}
+
+Color RenderThemeChromiumWin::platformActiveSelectionForegroundColor() const
+{
+ if (ChromiumBridge::layoutTestMode())
+ return Color(0xff, 0xff, 0xcc); // Pale yellow.
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 0xff);
+}
+
+Color RenderThemeChromiumWin::platformInactiveSelectionForegroundColor() const
+{
+ return Color::white;
+}
+
+Color RenderThemeChromiumWin::platformActiveTextSearchHighlightColor() const
+{
+ return Color(0xff, 0x96, 0x32); // Orange.
+}
+
+Color RenderThemeChromiumWin::platformInactiveTextSearchHighlightColor() const
+{
+ return Color(0xff, 0xff, 0x96); // Yellow.
+}
+
+void RenderThemeChromiumWin::systemFont(int propId, FontDescription& fontDescription) const
+{
+ // This logic owes much to RenderThemeSafari.cpp.
+ FontDescription* cachedDesc = 0;
+ AtomicString faceName;
+ float fontSize = 0;
+ switch (propId) {
+ case CSSValueSmallCaption:
+ cachedDesc = &smallSystemFont;
+ if (!smallSystemFont.isAbsoluteSize()) {
+ NONCLIENTMETRICS metrics;
+ getNonClientMetrics(&metrics);
+ faceName = AtomicString(metrics.lfSmCaptionFont.lfFaceName, wcslen(metrics.lfSmCaptionFont.lfFaceName));
+ fontSize = systemFontSize(metrics.lfSmCaptionFont);
+ }
+ break;
+ case CSSValueMenu:
+ cachedDesc = &menuFont;
+ if (!menuFont.isAbsoluteSize()) {
+ NONCLIENTMETRICS metrics;
+ getNonClientMetrics(&metrics);
+ faceName = AtomicString(metrics.lfMenuFont.lfFaceName, wcslen(metrics.lfMenuFont.lfFaceName));
+ fontSize = systemFontSize(metrics.lfMenuFont);
+ }
+ break;
+ case CSSValueStatusBar:
+ cachedDesc = &labelFont;
+ if (!labelFont.isAbsoluteSize()) {
+ NONCLIENTMETRICS metrics;
+ getNonClientMetrics(&metrics);
+ faceName = metrics.lfStatusFont.lfFaceName;
+ fontSize = systemFontSize(metrics.lfStatusFont);
+ }
+ break;
+ case CSSValueWebkitMiniControl:
+ case CSSValueWebkitSmallControl:
+ case CSSValueWebkitControl:
+ faceName = defaultGUIFont();
+ // Why 2 points smaller? Because that's what Gecko does.
+ fontSize = defaultFontSize - pointsToPixels(2);
+ break;
+ default:
+ faceName = defaultGUIFont();
+ fontSize = defaultFontSize;
+ break;
+ }
+
+ if (!cachedDesc)
+ cachedDesc = &fontDescription;
+
+ if (fontSize) {
+ cachedDesc->firstFamily().setFamily(faceName);
+ cachedDesc->setIsAbsoluteSize(true);
+ cachedDesc->setGenericFamily(FontDescription::NoFamily);
+ cachedDesc->setSpecifiedSize(fontSize);
+ cachedDesc->setWeight(FontWeightNormal);
+ cachedDesc->setItalic(false);
+ }
+ fontDescription = *cachedDesc;
+}
+
+// Map a CSSValue* system color to an index understood by GetSysColor().
+static int cssValueIdToSysColorIndex(int cssValueId)
+{
+ switch (cssValueId) {
+ case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
+ case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
+ case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
+ case CSSValueBackground: return COLOR_BACKGROUND;
+ case CSSValueButtonface: return COLOR_BTNFACE;
+ case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
+ case CSSValueButtonshadow: return COLOR_BTNSHADOW;
+ case CSSValueButtontext: return COLOR_BTNTEXT;
+ case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
+ case CSSValueGraytext: return COLOR_GRAYTEXT;
+ case CSSValueHighlight: return COLOR_HIGHLIGHT;
+ case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
+ case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
+ case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
+ case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
+ case CSSValueInfobackground: return COLOR_INFOBK;
+ case CSSValueInfotext: return COLOR_INFOTEXT;
+ case CSSValueMenu: return COLOR_MENU;
+ case CSSValueMenutext: return COLOR_MENUTEXT;
+ case CSSValueScrollbar: return COLOR_SCROLLBAR;
+ case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
+ case CSSValueThreedface: return COLOR_3DFACE;
+ case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
+ case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
+ case CSSValueThreedshadow: return COLOR_3DSHADOW;
+ case CSSValueWindow: return COLOR_WINDOW;
+ case CSSValueWindowframe: return COLOR_WINDOWFRAME;
+ case CSSValueWindowtext: return COLOR_WINDOWTEXT;
+ default: return -1; // Unsupported CSSValue
+ }
+}
+
+Color RenderThemeChromiumWin::systemColor(int cssValueId) const
+{
+ int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
+ if (ChromiumBridge::layoutTestMode() || (sysColorIndex == -1))
+ return RenderTheme::systemColor(cssValueId);
+
+ COLORREF color = GetSysColor(sysColorIndex);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+void RenderThemeChromiumWin::adjustSliderThumbSize(RenderObject* o) const
+{
+ // These sizes match what WinXP draws for various menus.
+ const int sliderThumbAlongAxis = 11;
+ const int sliderThumbAcrossAxis = 21;
+ if (o->style()->appearance() == SliderThumbHorizontalPart) {
+ o->style()->setWidth(Length(sliderThumbAlongAxis, Fixed));
+ o->style()->setHeight(Length(sliderThumbAcrossAxis, Fixed));
+ } else if (o->style()->appearance() == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(sliderThumbAcrossAxis, Fixed));
+ o->style()->setHeight(Length(sliderThumbAlongAxis, Fixed));
+ } else
+ RenderThemeChromiumSkia::adjustSliderThumbSize(o);
+}
+
+bool RenderThemeChromiumWin::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintButton(o, i, r);
+}
+bool RenderThemeChromiumWin::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintButton(o, i, r);
+}
+
+bool RenderThemeChromiumWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ const ThemeData& themeData = getThemeData(o);
+
+ ThemePainter painter(i.context, r);
+ ChromiumBridge::paintButton(painter.context(),
+ themeData.m_part,
+ themeData.m_state,
+ themeData.m_classicState,
+ painter.drawRect());
+ return false;
+}
+
+bool RenderThemeChromiumWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintTextFieldInternal(o, i, r, true);
+}
+
+bool RenderThemeChromiumWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ const ThemeData& themeData = getThemeData(o);
+
+ ThemePainter painter(i.context, r);
+ ChromiumBridge::paintTrackbar(painter.context(),
+ themeData.m_part,
+ themeData.m_state,
+ themeData.m_classicState,
+ painter.drawRect());
+ return false;
+}
+
+bool RenderThemeChromiumWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintSliderTrack(o, i, r);
+}
+
+static int menuListButtonWidth()
+{
+ static int width = ChromiumBridge::layoutTestMode() ? kStandardMenuListButtonWidth : GetSystemMetrics(SM_CXVSCROLL);
+ return width;
+}
+
+// Used to paint unstyled menulists (i.e. with the default border)
+bool RenderThemeChromiumWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ if (!o->isBox())
+ return false;
+
+ const RenderBox* box = toRenderBox(o);
+ int borderRight = box->borderRight();
+ int borderLeft = box->borderLeft();
+ int borderTop = box->borderTop();
+ int borderBottom = box->borderBottom();
+
+ // If all the borders are 0, then tell skia not to paint the border on the
+ // textfield. FIXME: http://b/1210017 Figure out how to get Windows to not
+ // draw individual borders and then pass that to skia so we can avoid
+ // drawing any borders that are set to 0. For non-zero borders, we draw the
+ // border, but webkit just draws over it.
+ bool drawEdges = !(!borderRight && !borderLeft && !borderTop && !borderBottom);
+
+ paintTextFieldInternal(o, i, r, drawEdges);
+
+ // Take padding and border into account. If the MenuList is smaller than
+ // the size of a button, make sure to shrink it appropriately and not put
+ // its x position to the left of the menulist.
+ const int buttonWidth = menuListButtonWidth();
+ int spacingLeft = borderLeft + box->paddingLeft();
+ int spacingRight = borderRight + box->paddingRight();
+ int spacingTop = borderTop + box->paddingTop();
+ int spacingBottom = borderBottom + box->paddingBottom();
+
+ int buttonX;
+ if (r.right() - r.x() < buttonWidth)
+ buttonX = r.x();
+ else
+ buttonX = o->style()->direction() == LTR ? r.right() - spacingRight - buttonWidth : r.x() + spacingLeft;
+
+ // Compute the rectangle of the button in the destination image.
+ IntRect rect(buttonX,
+ r.y() + spacingTop,
+ std::min(buttonWidth, r.right() - r.x()),
+ r.height() - (spacingTop + spacingBottom));
+
+ // Get the correct theme data for a textfield and paint the menu.
+ ThemePainter painter(i.context, rect);
+ ChromiumBridge::paintMenuList(painter.context(),
+ CP_DROPDOWNBUTTON,
+ determineState(o),
+ determineClassicState(o),
+ painter.drawRect());
+ return false;
+}
+
+// static
+void RenderThemeChromiumWin::setDefaultFontSize(int fontSize)
+{
+ RenderThemeChromiumSkia::setDefaultFontSize(fontSize);
+
+ // Reset cached fonts.
+ smallSystemFont = menuFont = labelFont = FontDescription();
+}
+
+double RenderThemeChromiumWin::caretBlinkIntervalInternal() const
+{
+ // This involves a system call, so we cache the result.
+ static double blinkInterval = querySystemBlinkInterval(RenderTheme::caretBlinkInterval());
+ return blinkInterval;
+}
+
+unsigned RenderThemeChromiumWin::determineState(RenderObject* o, ControlSubPart subPart)
+{
+ unsigned result = TS_NORMAL;
+ ControlPart appearance = o->style()->appearance();
+ if (!isEnabled(o))
+ result = TS_DISABLED;
+ else if (isReadOnlyControl(o))
+ result = (appearance == TextFieldPart || appearance == TextAreaPart || appearance == SearchFieldPart) ? ETS_READONLY : TS_DISABLED;
+ // Active overrides hover and focused.
+ else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
+ result = TS_PRESSED;
+ else if (supportsFocus(appearance) && isFocused(o))
+ result = ETS_FOCUSED;
+ else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
+ result = TS_HOT;
+
+ // CBS_UNCHECKED*: 1-4
+ // CBS_CHECKED*: 5-8
+ // CBS_MIXED*: 9-12
+ if (isIndeterminate(o))
+ result += 8;
+ else if (isChecked(o))
+ result += 4;
+ return result;
+}
+
+unsigned RenderThemeChromiumWin::determineSliderThumbState(RenderObject* o)
+{
+ unsigned result = TUS_NORMAL;
+ if (!isEnabled(o->parent()))
+ result = TUS_DISABLED;
+ else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
+ result = TUS_FOCUSED;
+ else if (toRenderSlider(o->parent())->inDragMode())
+ result = TUS_PRESSED;
+ else if (isHovered(o))
+ result = TUS_HOT;
+ return result;
+}
+
+unsigned RenderThemeChromiumWin::determineClassicState(RenderObject* o, ControlSubPart subPart)
+{
+ unsigned result = 0;
+
+ ControlPart part = o->style()->appearance();
+
+ // Sliders are always in the normal state.
+ if (part == SliderHorizontalPart || part == SliderVerticalPart)
+ return result;
+
+ // So are readonly text fields.
+ if (isReadOnlyControl(o) && (part == TextFieldPart || part == TextAreaPart || part == SearchFieldPart))
+ return result;
+
+ if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
+ if (!isEnabled(o->parent()))
+ result = DFCS_INACTIVE;
+ else if (toRenderSlider(o->parent())->inDragMode()) // Active supersedes hover
+ result = DFCS_PUSHED;
+ else if (isHovered(o))
+ result = DFCS_HOT;
+ } else {
+ if (!isEnabled(o) || isReadOnlyControl(o))
+ result = DFCS_INACTIVE;
+ // Active supersedes hover
+ else if (isPressed(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartPressed(o))
+ result = DFCS_PUSHED;
+ else if (supportsFocus(part) && isFocused(o)) // So does focused
+ result = 0;
+ else if (isHovered(o) && (subPart == SpinButtonUp) == isSpinUpButtonPartHovered(o))
+ result = DFCS_HOT;
+ // Classic theme can't represent indeterminate states. Use unchecked appearance.
+ if (isChecked(o) && !isIndeterminate(o))
+ result |= DFCS_CHECKED;
+ }
+ return result;
+}
+
+ThemeData RenderThemeChromiumWin::getThemeData(RenderObject* o, ControlSubPart subPart)
+{
+ ThemeData result;
+ switch (o->style()->appearance()) {
+ case CheckboxPart:
+ result.m_part = BP_CHECKBOX;
+ result.m_state = determineState(o);
+ result.m_classicState = DFCS_BUTTONCHECK;
+ break;
+ case RadioPart:
+ result.m_part = BP_RADIOBUTTON;
+ result.m_state = determineState(o);
+ result.m_classicState = DFCS_BUTTONRADIO;
+ break;
+ case PushButtonPart:
+ case ButtonPart:
+ result.m_part = BP_PUSHBUTTON;
+ result.m_state = determineState(o);
+ result.m_classicState = DFCS_BUTTONPUSH;
+ break;
+ case SliderHorizontalPart:
+ result.m_part = TKP_TRACK;
+ result.m_state = TRS_NORMAL;
+ break;
+ case SliderVerticalPart:
+ result.m_part = TKP_TRACKVERT;
+ result.m_state = TRVS_NORMAL;
+ break;
+ case SliderThumbHorizontalPart:
+ result.m_part = TKP_THUMBBOTTOM;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case SliderThumbVerticalPart:
+ result.m_part = TKP_THUMBVERT;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case ListboxPart:
+ case MenulistPart:
+ case MenulistButtonPart:
+ case SearchFieldPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ result.m_part = EP_EDITTEXT;
+ result.m_state = determineState(o);
+ break;
+ case InnerSpinButtonPart:
+ result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN;
+ result.m_state = determineState(o, subPart);
+ result.m_classicState = subPart == SpinButtonUp ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
+ break;
+ }
+
+ result.m_classicState |= determineClassicState(o, subPart);
+
+ return result;
+}
+
+bool RenderThemeChromiumWin::paintTextFieldInternal(RenderObject* o,
+ const PaintInfo& i,
+ const IntRect& r,
+ bool drawEdges)
+{
+ // Fallback to white if the specified color object is invalid.
+ // (Note ChromiumBridge::paintTextField duplicates this check).
+ Color backgroundColor(Color::white);
+ if (o->style()->visitedDependentColor(CSSPropertyBackgroundColor).isValid())
+ backgroundColor = o->style()->visitedDependentColor(CSSPropertyBackgroundColor);
+
+ // If we have background-image, don't fill the content area to expose the
+ // parent's background. Also, we shouldn't fill the content area if the
+ // alpha of the color is 0. The API of Windows GDI ignores the alpha.
+ //
+ // Note that we should paint the content area white if we have neither the
+ // background color nor background image explicitly specified to keep the
+ // appearance of select element consistent with other browsers.
+ bool fillContentArea = !o->style()->hasBackgroundImage() && backgroundColor.alpha();
+
+ if (o->style()->hasBorderRadius()) {
+ // If the style has rounded borders, setup the context to clip the
+ // background (themed or filled) appropriately.
+ // FIXME: make sure we do the right thing if css background-clip is set.
+ i.context->save();
+ IntSize topLeft, topRight, bottomLeft, bottomRight;
+ o->style()->getBorderRadiiForRect(r, topLeft, topRight, bottomLeft, bottomRight);
+ i.context->addRoundedRectClip(r, topLeft, topRight, bottomLeft, bottomRight);
+ }
+ {
+ const ThemeData& themeData = getThemeData(o);
+ ThemePainter painter(i.context, r);
+ ChromiumBridge::paintTextField(painter.context(),
+ themeData.m_part,
+ themeData.m_state,
+ themeData.m_classicState,
+ painter.drawRect(),
+ backgroundColor,
+ fillContentArea,
+ drawEdges);
+ // End of block commits the painter before restoring context.
+ }
+ if (o->style()->hasBorderRadius())
+ i.context->restore();
+ return false;
+}
+
+void RenderThemeChromiumWin::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ int width = ScrollbarTheme::nativeTheme()->scrollbarThickness();
+ style->setWidth(Length(width, Fixed));
+ style->setMinWidth(Length(width, Fixed));
+}
+
+bool RenderThemeChromiumWin::paintInnerSpinButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ IntRect half = rect;
+
+ half.setHeight(rect.height() / 2);
+ const ThemeData& upThemeData = getThemeData(object, SpinButtonUp);
+ ThemePainter upPainter(info.context, half);
+ ChromiumBridge::paintSpinButton(upPainter.context(),
+ upThemeData.m_part,
+ upThemeData.m_state,
+ upThemeData.m_classicState,
+ upPainter.drawRect());
+
+ half.setY(rect.y() + rect.height() / 2);
+ const ThemeData& downThemeData = getThemeData(object, SpinButtonDown);
+ ThemePainter downPainter(info.context, half);
+ ChromiumBridge::paintSpinButton(downPainter.context(),
+ downThemeData.m_part,
+ downThemeData.m_state,
+ downThemeData.m_classicState,
+ downPainter.drawRect());
+ return false;
+}
+
+#if ENABLE(PROGRESS_TAG)
+
+// MSDN says that update intervals for the bar is 30ms.
+// http://msdn.microsoft.com/en-us/library/bb760842(v=VS.85).aspx
+static const double progressAnimationFrameRate = 0.033;
+
+double RenderThemeChromiumWin::animationRepeatIntervalForProgressBar(RenderProgress*) const
+{
+ return progressAnimationFrameRate;
+}
+
+double RenderThemeChromiumWin::animationDurationForProgressBar(RenderProgress* renderProgress) const
+{
+ // On Chromium Windows port, animationProgress() and associated values aren't used.
+ // So here we can return arbitrary positive value.
+ return progressAnimationFrameRate;
+}
+
+void RenderThemeChromiumWin::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+bool RenderThemeChromiumWin::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ if (!o->isProgress())
+ return true;
+
+ RenderProgress* renderProgress = toRenderProgress(o);
+ // For indeterminate bar, valueRect is ignored and it is computed by the theme engine
+ // because the animation is a platform detail and WebKit doesn't need to know how.
+ IntRect valueRect = renderProgress->isDeterminate() ? determinateProgressValueRectFor(renderProgress, r) : IntRect(0, 0, 0, 0);
+ double animatedSeconds = renderProgress->animationStartTime() ? WTF::currentTime() - renderProgress->animationStartTime() : 0;
+ ThemePainter painter(i.context, r);
+ ChromiumBridge::paintProgressBar(painter.context(), r, valueRect, renderProgress->isDeterminate(), animatedSeconds);
+ return false;
+}
+
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeChromiumWin.h b/Source/WebCore/rendering/RenderThemeChromiumWin.h
new file mode 100644
index 0000000..661b623
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeChromiumWin.h
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2008, 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 RenderThemeChromiumWin_h
+#define RenderThemeChromiumWin_h
+
+#include "RenderThemeChromiumSkia.h"
+
+#if WIN32
+typedef void* HANDLE;
+typedef struct HINSTANCE__* HINSTANCE;
+typedef HINSTANCE HMODULE;
+#endif
+
+namespace WebCore {
+
+ struct ThemeData {
+ ThemeData() : m_part(0), m_state(0), m_classicState(0) {}
+
+ unsigned m_part;
+ unsigned m_state;
+ unsigned m_classicState;
+ };
+
+ class RenderThemeChromiumWin : public RenderThemeChromiumSkia {
+ public:
+ static PassRefPtr<RenderTheme> create();
+
+ // A method asking if the theme is able to draw the focus ring.
+ virtual bool supportsFocusRing(const RenderStyle*) const;
+
+ // The platform selection color.
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+ virtual Color platformActiveTextSearchHighlightColor() const;
+ virtual Color platformInactiveTextSearchHighlightColor() const;
+
+ // System fonts.
+ virtual void systemFont(int propId, FontDescription&) const;
+ virtual Color systemColor(int cssValueId) const;
+
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ // Various paint functions.
+ virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+
+ // MenuList refers to an unstyled menulist (meaning a menulist without
+ // background-color or border set) and MenuListButton refers to a styled
+ // menulist (a menulist with background-color or border set). They have
+ // this distinction to support showing aqua style themes whenever they
+ // possibly can, which is something we don't want to replicate.
+ //
+ // In short, we either go down the MenuList code path or the MenuListButton
+ // codepath. We never go down both. And in both cases, they render the
+ // entire menulist.
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+
+ // Override RenderThemeChromiumSkia's setDefaultFontSize method to also reset the local font property caches.
+ // See comment in RenderThemeChromiumSkia::setDefaultFontSize() regarding ugliness of this hack.
+ static void setDefaultFontSize(int);
+
+ virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+#if ENABLE(PROGRESS_TAG)
+ virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const;
+ virtual double animationDurationForProgressBar(RenderProgress*) const;
+ virtual void adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+ protected:
+ virtual double caretBlinkIntervalInternal() const;
+
+ private:
+ enum ControlSubPart {
+ None,
+ SpinButtonDown,
+ SpinButtonUp,
+ };
+
+ RenderThemeChromiumWin() { }
+ virtual ~RenderThemeChromiumWin() { }
+
+ unsigned determineState(RenderObject*, ControlSubPart = None);
+ unsigned determineSliderThumbState(RenderObject*);
+ unsigned determineClassicState(RenderObject*, ControlSubPart = None);
+
+ ThemeData getThemeData(RenderObject*, ControlSubPart = None);
+
+ bool paintTextFieldInternal(RenderObject*, const PaintInfo&, const IntRect&, bool);
+ };
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/RenderThemeMac.h b/Source/WebCore/rendering/RenderThemeMac.h
new file mode 100644
index 0000000..95661b8
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeMac.h
@@ -0,0 +1,237 @@
+/*
+ * This file is part of the theme implementation for form controls in WebCore.
+ *
+ * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 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 RenderThemeMac_h
+#define RenderThemeMac_h
+
+#import "RenderTheme.h"
+#import <wtf/HashMap.h>
+#import <wtf/RetainPtr.h>
+
+class RenderProgress;
+
+#ifdef __OBJC__
+@class WebCoreRenderThemeNotificationObserver;
+#else
+class WebCoreRenderThemeNotificationObserver;
+#endif
+
+namespace WebCore {
+
+class RenderStyle;
+
+class RenderThemeMac : public RenderTheme {
+public:
+ static PassRefPtr<RenderTheme> create();
+
+ // A method asking if the control changes its tint when the window has focus or not.
+ virtual bool controlSupportsTints(const RenderObject*) const;
+
+ // A general method asking if any control tinting is supported at all.
+ virtual bool supportsControlTints() const { return true; }
+
+ virtual void adjustRepaintRect(const RenderObject*, IntRect&);
+
+ virtual bool isControlStyled(const RenderStyle*, const BorderData&,
+ const FillLayer&, const Color& backgroundColor) const;
+
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveListBoxSelectionBackgroundColor() const;
+ virtual Color platformActiveListBoxSelectionForegroundColor() const;
+ virtual Color platformInactiveListBoxSelectionBackgroundColor() const;
+ virtual Color platformInactiveListBoxSelectionForegroundColor() const;
+ virtual Color platformFocusRingColor() const;
+
+ virtual ScrollbarControlSize scrollbarControlSizeForPart(ControlPart) { return SmallScrollbar; }
+
+ virtual void platformColorsDidChange();
+
+ // System fonts.
+ virtual void systemFont(int cssValueId, FontDescription&) const;
+
+ virtual int minimumMenuListSize(RenderStyle*) const;
+
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ virtual int popupInternalPaddingLeft(RenderStyle*) const;
+ virtual int popupInternalPaddingRight(RenderStyle*) const;
+ virtual int popupInternalPaddingTop(RenderStyle*) const;
+ virtual int popupInternalPaddingBottom(RenderStyle*) const;
+
+ virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&);
+
+#if ENABLE(METER_TAG)
+ virtual IntSize meterSizeForBounds(const RenderMeter*, const IntRect&) const;
+ virtual bool paintMeter(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool supportsMeter(ControlPart, bool isHorizontal) const;
+#endif
+
+#if ENABLE(PROGRESS_TAG)
+ // Returns the repeat interval of the animation for the progress bar.
+ virtual double animationRepeatIntervalForProgressBar(RenderProgress*) const;
+ // Returns the duration of the animation for the progress bar.
+ virtual double animationDurationForProgressBar(RenderProgress*) const;
+#endif
+
+ virtual Color systemColor(int cssValueId) const;
+ // Controls color values returned from platformFocusRingColor(). systemColor() will be used when false.
+ virtual bool usesTestModeFocusRingColor() const;
+ // A view associated to the contained document. Subclasses may not have such a view and return a fake.
+ virtual NSView* documentViewFor(RenderObject*) const;
+protected:
+ RenderThemeMac();
+ virtual ~RenderThemeMac();
+
+ virtual bool supportsSelectionForegroundColors() const { return false; }
+
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+#if ENABLE(PROGRESS_TAG)
+ virtual void adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+ virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+#if ENABLE(VIDEO)
+ virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaRewindButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaReturnToRealtimeButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaCurrentTime(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaTimeRemaining(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+
+ // Media controls
+ virtual String extraMediaControlsStyleSheet();
+
+ virtual bool shouldRenderMediaControlPart(ControlPart, Element*);
+ virtual void adjustMediaSliderThumbSize(RenderObject*) const;
+ virtual IntPoint volumeSliderOffsetFromMuteButton(Node*, const IntSize&) const;
+#endif
+
+private:
+
+ IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const;
+
+ FloatRect convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const;
+
+ // Get the control size based off the font. Used by some of the controls (like buttons).
+ NSControlSize controlSizeForFont(RenderStyle*) const;
+ NSControlSize controlSizeForSystemFont(RenderStyle*) const;
+ void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f);
+ void setSizeFromFont(RenderStyle*, const IntSize* sizes) const;
+ IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const;
+ IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const;
+ void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const;
+
+ void updateCheckedState(NSCell*, const RenderObject*);
+ void updateEnabledState(NSCell*, const RenderObject*);
+ void updateFocusedState(NSCell*, const RenderObject*);
+ void updatePressedState(NSCell*, const RenderObject*);
+ // An optional hook for subclasses to update the control tint of NSCell.
+ virtual void updateActiveState(NSCell*, const RenderObject*) {}
+
+ // Helpers for adjusting appearance and for painting
+
+ void setPopupButtonCellState(const RenderObject*, const IntRect&);
+ const IntSize* popupButtonSizes() const;
+ const int* popupButtonMargins() const;
+ const int* popupButtonPadding(NSControlSize) const;
+ void paintMenuListButtonGradients(RenderObject*, const PaintInfo&, const IntRect&);
+ const IntSize* menuListSizes() const;
+
+ const IntSize* searchFieldSizes() const;
+ const IntSize* cancelButtonSizes() const;
+ const IntSize* resultsButtonSizes() const;
+ void setSearchCellState(RenderObject*, const IntRect&);
+ void setSearchFieldSize(RenderStyle*) const;
+
+ NSPopUpButtonCell* popupButton() const;
+ NSSearchFieldCell* search() const;
+ NSMenu* searchMenuTemplate() const;
+ NSSliderCell* sliderThumbHorizontal() const;
+ NSSliderCell* sliderThumbVertical() const;
+
+#if ENABLE(METER_TAG)
+ NSLevelIndicatorStyle levelIndicatorStyleFor(ControlPart) const;
+ NSLevelIndicatorCell* levelIndicatorFor(const RenderMeter*) const;
+#endif
+
+private:
+ mutable RetainPtr<NSPopUpButtonCell> m_popupButton;
+ mutable RetainPtr<NSSearchFieldCell> m_search;
+ mutable RetainPtr<NSMenu> m_searchMenuTemplate;
+ mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal;
+ mutable RetainPtr<NSSliderCell> m_sliderThumbVertical;
+ mutable RetainPtr<NSLevelIndicatorCell> m_levelIndicator;
+
+ bool m_isSliderThumbHorizontalPressed;
+ bool m_isSliderThumbVerticalPressed;
+
+ mutable HashMap<int, RGBA32> m_systemColorCache;
+
+ RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver;
+};
+
+} // namespace WebCore
+
+#endif // RenderThemeMac_h
diff --git a/Source/WebCore/rendering/RenderThemeMac.mm b/Source/WebCore/rendering/RenderThemeMac.mm
new file mode 100644
index 0000000..a13b4aa
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeMac.mm
@@ -0,0 +1,2064 @@
+/*
+ * 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.
+ */
+
+#import "config.h"
+#import "RenderThemeMac.h"
+
+#import "BitmapImage.h"
+#import "ColorMac.h"
+#import "CSSStyleSelector.h"
+#import "CSSValueKeywords.h"
+#import "Document.h"
+#import "Element.h"
+#import "FrameView.h"
+#import "GraphicsContextCG.h"
+#import "HTMLInputElement.h"
+#import "HTMLMediaElement.h"
+#import "HTMLNames.h"
+#import "Image.h"
+#import "ImageBuffer.h"
+#import "LocalCurrentGraphicsContext.h"
+#import "MediaControlElements.h"
+#import "RenderMedia.h"
+#import "RenderSlider.h"
+#import "RenderView.h"
+#import "SharedBuffer.h"
+#import "TimeRanges.h"
+#import "ThemeMac.h"
+#import "WebCoreSystemInterface.h"
+#import "UserAgentStyleSheets.h"
+#import <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/StdLibExtras.h>
+#import <math.h>
+
+#import "RenderProgress.h"
+
+#if ENABLE(METER_TAG)
+#include "RenderMeter.h"
+#include "HTMLMeterElement.h"
+#endif
+
+#ifdef BUILDING_ON_TIGER
+typedef int NSInteger;
+typedef unsigned NSUInteger;
+#endif
+
+using namespace std;
+
+// The methods in this file are specific to the Mac OS X platform.
+
+// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
+
+// We estimate the animation rate of a Mac OS X progress bar is 33 fps.
+// Hard code the value here because we haven't found API for it.
+const double progressAnimationFrameRate = 0.033;
+
+// Mac OS X progress bar animation seems to have 256 frames.
+const double progressAnimationNumFrames = 256;
+
+@interface WebCoreRenderThemeNotificationObserver : NSObject
+{
+ WebCore::RenderTheme *_theme;
+}
+
+- (id)initWithTheme:(WebCore::RenderTheme *)theme;
+- (void)systemColorsDidChange:(NSNotification *)notification;
+
+@end
+
+@implementation WebCoreRenderThemeNotificationObserver
+
+- (id)initWithTheme:(WebCore::RenderTheme *)theme
+{
+ [super init];
+ _theme = theme;
+
+ return self;
+}
+
+- (void)systemColorsDidChange:(NSNotification *)unusedNotification
+{
+ ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
+ _theme->platformColorsDidChange();
+}
+
+@end
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+enum {
+ topMargin,
+ rightMargin,
+ bottomMargin,
+ leftMargin
+};
+
+enum {
+ topPadding,
+ rightPadding,
+ bottomPadding,
+ leftPadding
+};
+
+#if PLATFORM(MAC)
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
+{
+ static RenderTheme* rt = RenderThemeMac::create().releaseRef();
+ return rt;
+}
+#endif
+
+PassRefPtr<RenderTheme> RenderThemeMac::create()
+{
+ return adoptRef(new RenderThemeMac);
+}
+
+RenderThemeMac::RenderThemeMac()
+ : m_isSliderThumbHorizontalPressed(false)
+ , m_isSliderThumbVerticalPressed(false)
+ , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this])
+{
+ [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
+ selector:@selector(systemColorsDidChange:)
+ name:NSSystemColorsDidChangeNotification
+ object:nil];
+}
+
+RenderThemeMac::~RenderThemeMac()
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
+}
+
+Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
+{
+ NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
+}
+
+Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
+{
+ NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
+}
+
+Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const
+{
+ NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
+}
+
+Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const
+{
+ return Color::white;
+}
+
+Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const
+{
+ return Color::black;
+}
+
+Color RenderThemeMac::platformFocusRingColor() const
+{
+ if (usesTestModeFocusRingColor())
+ return oldAquaFocusRingColor();
+
+ return systemColor(CSSValueWebkitFocusRingColor);
+}
+
+Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
+{
+ return platformInactiveSelectionBackgroundColor();
+}
+
+static FontWeight toFontWeight(NSInteger appKitFontWeight)
+{
+ ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
+ if (appKitFontWeight > 14)
+ appKitFontWeight = 14;
+ else if (appKitFontWeight < 1)
+ appKitFontWeight = 1;
+
+ static FontWeight fontWeights[] = {
+ FontWeight100,
+ FontWeight100,
+ FontWeight200,
+ FontWeight300,
+ FontWeight400,
+ FontWeight500,
+ FontWeight600,
+ FontWeight600,
+ FontWeight700,
+ FontWeight800,
+ FontWeight800,
+ FontWeight900,
+ FontWeight900,
+ FontWeight900
+ };
+ return fontWeights[appKitFontWeight - 1];
+}
+
+void RenderThemeMac::systemFont(int cssValueId, FontDescription& fontDescription) const
+{
+ DEFINE_STATIC_LOCAL(FontDescription, systemFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, menuFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, labelFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ());
+ DEFINE_STATIC_LOCAL(FontDescription, controlFont, ());
+
+ FontDescription* cachedDesc;
+ NSFont* font = nil;
+ switch (cssValueId) {
+ case CSSValueSmallCaption:
+ cachedDesc = &smallSystemFont;
+ if (!smallSystemFont.isAbsoluteSize())
+ font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+ break;
+ case CSSValueMenu:
+ cachedDesc = &menuFont;
+ if (!menuFont.isAbsoluteSize())
+ font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
+ break;
+ case CSSValueStatusBar:
+ cachedDesc = &labelFont;
+ if (!labelFont.isAbsoluteSize())
+ font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
+ break;
+ case CSSValueWebkitMiniControl:
+ cachedDesc = &miniControlFont;
+ if (!miniControlFont.isAbsoluteSize())
+ font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
+ break;
+ case CSSValueWebkitSmallControl:
+ cachedDesc = &smallControlFont;
+ if (!smallControlFont.isAbsoluteSize())
+ font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
+ break;
+ case CSSValueWebkitControl:
+ cachedDesc = &controlFont;
+ if (!controlFont.isAbsoluteSize())
+ font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
+ break;
+ default:
+ cachedDesc = &systemFont;
+ if (!systemFont.isAbsoluteSize())
+ font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
+ }
+
+ if (font) {
+ NSFontManager *fontManager = [NSFontManager sharedFontManager];
+ cachedDesc->setIsAbsoluteSize(true);
+ cachedDesc->setGenericFamily(FontDescription::NoFamily);
+ cachedDesc->firstFamily().setFamily([font familyName]);
+ cachedDesc->setSpecifiedSize([font pointSize]);
+ cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font]));
+ cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
+ }
+ fontDescription = *cachedDesc;
+}
+
+static RGBA32 convertNSColorToColor(NSColor *color)
+{
+ NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
+ if (colorInColorSpace) {
+ static const double scaleFactor = nextafter(256.0, 0.0);
+ return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
+ static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
+ static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
+ }
+
+ // This conversion above can fail if the NSColor in question is an NSPatternColor
+ // (as many system colors are). These colors are actually a repeating pattern
+ // not just a solid color. To work around this we simply draw a 1x1 image of
+ // the color and use that pixel's color. It might be better to use an average of
+ // the colors in the pattern instead.
+ NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
+ pixelsWide:1
+ pixelsHigh:1
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:4
+ bitsPerPixel:32];
+
+ [NSGraphicsContext saveGraphicsState];
+ [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
+ NSEraseRect(NSMakeRect(0, 0, 1, 1));
+ [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
+ [NSGraphicsContext restoreGraphicsState];
+
+ NSUInteger pixel[4];
+ [offscreenRep getPixel:pixel atX:0 y:0];
+
+ [offscreenRep release];
+
+ return makeRGB(pixel[0], pixel[1], pixel[2]);
+}
+
+static RGBA32 menuBackgroundColor()
+{
+ NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
+ pixelsWide:1
+ pixelsHigh:1
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:4
+ bitsPerPixel:32];
+
+ CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
+ CGRect rect = CGRectMake(0, 0, 1, 1);
+ HIThemeMenuDrawInfo drawInfo;
+ drawInfo.version = 0;
+ drawInfo.menuType = kThemeMenuTypePopUp;
+ HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
+
+ NSUInteger pixel[4];
+ [offscreenRep getPixel:pixel atX:0 y:0];
+
+ [offscreenRep release];
+
+ return makeRGB(pixel[0], pixel[1], pixel[2]);
+}
+
+void RenderThemeMac::platformColorsDidChange()
+{
+ m_systemColorCache.clear();
+ RenderTheme::platformColorsDidChange();
+}
+
+Color RenderThemeMac::systemColor(int cssValueId) const
+{
+ if (m_systemColorCache.contains(cssValueId))
+ return m_systemColorCache.get(cssValueId);
+
+ Color color;
+ switch (cssValueId) {
+ case CSSValueActiveborder:
+ color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
+ break;
+ case CSSValueActivecaption:
+ color = convertNSColorToColor([NSColor windowFrameTextColor]);
+ break;
+ case CSSValueAppworkspace:
+ color = convertNSColorToColor([NSColor headerColor]);
+ break;
+ case CSSValueBackground:
+ // Use theme independent default
+ break;
+ case CSSValueButtonface:
+ // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
+ // We may want to change this to use the NSColor in future.
+ color = 0xFFC0C0C0;
+ break;
+ case CSSValueButtonhighlight:
+ color = convertNSColorToColor([NSColor controlHighlightColor]);
+ break;
+ case CSSValueButtonshadow:
+ color = convertNSColorToColor([NSColor controlShadowColor]);
+ break;
+ case CSSValueButtontext:
+ color = convertNSColorToColor([NSColor controlTextColor]);
+ break;
+ case CSSValueCaptiontext:
+ color = convertNSColorToColor([NSColor textColor]);
+ break;
+ case CSSValueGraytext:
+ color = convertNSColorToColor([NSColor disabledControlTextColor]);
+ break;
+ case CSSValueHighlight:
+ color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
+ break;
+ case CSSValueHighlighttext:
+ color = convertNSColorToColor([NSColor selectedTextColor]);
+ break;
+ case CSSValueInactiveborder:
+ color = convertNSColorToColor([NSColor controlBackgroundColor]);
+ break;
+ case CSSValueInactivecaption:
+ color = convertNSColorToColor([NSColor controlBackgroundColor]);
+ break;
+ case CSSValueInactivecaptiontext:
+ color = convertNSColorToColor([NSColor textColor]);
+ break;
+ case CSSValueInfobackground:
+ // There is no corresponding NSColor for this so we use a hard coded value.
+ color = 0xFFFBFCC5;
+ break;
+ case CSSValueInfotext:
+ color = convertNSColorToColor([NSColor textColor]);
+ break;
+ case CSSValueMenu:
+ color = menuBackgroundColor();
+ break;
+ case CSSValueMenutext:
+ color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
+ break;
+ case CSSValueScrollbar:
+ color = convertNSColorToColor([NSColor scrollBarColor]);
+ break;
+ case CSSValueText:
+ color = convertNSColorToColor([NSColor textColor]);
+ break;
+ case CSSValueThreeddarkshadow:
+ color = convertNSColorToColor([NSColor controlDarkShadowColor]);
+ break;
+ case CSSValueThreedshadow:
+ color = convertNSColorToColor([NSColor shadowColor]);
+ break;
+ case CSSValueThreedface:
+ // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
+ // We may want to change this to use the NSColor in future.
+ color = 0xFFC0C0C0;
+ break;
+ case CSSValueThreedhighlight:
+ color = convertNSColorToColor([NSColor highlightColor]);
+ break;
+ case CSSValueThreedlightshadow:
+ color = convertNSColorToColor([NSColor controlLightHighlightColor]);
+ break;
+ case CSSValueWebkitFocusRingColor:
+ color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
+ break;
+ case CSSValueWindow:
+ color = convertNSColorToColor([NSColor windowBackgroundColor]);
+ break;
+ case CSSValueWindowframe:
+ color = convertNSColorToColor([NSColor windowFrameColor]);
+ break;
+ case CSSValueWindowtext:
+ color = convertNSColorToColor([NSColor windowFrameTextColor]);
+ break;
+ }
+
+ if (!color.isValid())
+ color = RenderTheme::systemColor(cssValueId);
+
+ if (color.isValid())
+ m_systemColorCache.set(cssValueId, color.rgb());
+
+ return color;
+}
+
+bool RenderThemeMac::usesTestModeFocusRingColor() const
+{
+ return WebCore::usesTestModeFocusRingColor();
+}
+
+NSView* RenderThemeMac::documentViewFor(RenderObject* o) const
+{
+#if PLATFORM(MAC)
+ return ThemeMac::ensuredView(o->view()->frameView());
+#else
+ ASSERT_NOT_REACHED();
+ return 0;
+#endif
+}
+
+bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border,
+ const FillLayer& background, const Color& backgroundColor) const
+{
+ if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart)
+ return style->border() != border;
+
+ // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when
+ // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style
+ // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming
+ // is in effect we treat it like the control is styled.
+ if (style->appearance() == MenulistPart && style->effectiveZoom() != 1.0f)
+ return true;
+
+ return RenderTheme::isControlStyled(style, border, background, backgroundColor);
+}
+
+void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
+{
+ ControlPart part = o->style()->appearance();
+
+#if USE(NEW_THEME)
+ switch (part) {
+ case CheckboxPart:
+ case RadioPart:
+ case PushButtonPart:
+ case SquareButtonPart:
+ case ListButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart:
+ case OuterSpinButtonPart:
+ return RenderTheme::adjustRepaintRect(o, r);
+ default:
+ break;
+ }
+#endif
+
+ float zoomLevel = o->style()->effectiveZoom();
+
+ if (part == MenulistPart) {
+ setPopupButtonCellState(o, r);
+ IntSize size = popupButtonSizes()[[popupButton() controlSize]];
+ size.setHeight(size.height() * zoomLevel);
+ size.setWidth(r.width());
+ r = inflateRect(r, size, popupButtonMargins(), zoomLevel);
+ }
+}
+
+IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins, float zoomLevel) const
+{
+ // Only do the inflation if the available width/height are too small. Otherwise try to
+ // fit the glow/check space into the available box's width/height.
+ int widthDelta = r.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel);
+ int heightDelta = r.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel);
+ IntRect result(r);
+ if (widthDelta < 0) {
+ result.setX(result.x() - margins[leftMargin] * zoomLevel);
+ result.setWidth(result.width() - widthDelta);
+ }
+ if (heightDelta < 0) {
+ result.setY(result.y() - margins[topMargin] * zoomLevel);
+ result.setHeight(result.height() - heightDelta);
+ }
+ return result;
+}
+
+FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const
+{
+ FloatRect partRect(inputRect);
+
+ // Compute an offset between the part renderer and the input renderer
+ FloatSize offsetFromInputRenderer;
+ const RenderObject* renderer = partRenderer;
+ while (renderer && renderer != inputRenderer) {
+ RenderObject* containingRenderer = renderer->container();
+ offsetFromInputRenderer -= renderer->offsetFromContainer(containingRenderer, IntPoint());
+ renderer = containingRenderer;
+ }
+ // If the input renderer was not a container, something went wrong
+ ASSERT(renderer == inputRenderer);
+ // Move the rect into partRenderer's coords
+ partRect.move(offsetFromInputRenderer);
+ // Account for the local drawing offset (tx, ty)
+ partRect.move(r.x(), r.y());
+
+ return partRect;
+}
+
+void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o)
+{
+ bool oldIndeterminate = [cell state] == NSMixedState;
+ bool indeterminate = isIndeterminate(o);
+ bool checked = isChecked(o);
+
+ if (oldIndeterminate != indeterminate) {
+ [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
+ return;
+ }
+
+ bool oldChecked = [cell state] == NSOnState;
+ if (checked != oldChecked)
+ [cell setState:checked ? NSOnState : NSOffState];
+}
+
+void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o)
+{
+ bool oldEnabled = [cell isEnabled];
+ bool enabled = isEnabled(o);
+ if (enabled != oldEnabled)
+ [cell setEnabled:enabled];
+}
+
+void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o)
+{
+ bool oldFocused = [cell showsFirstResponder];
+ bool focused = isFocused(o) && o->style()->outlineStyleIsAuto();
+ if (focused != oldFocused)
+ [cell setShowsFirstResponder:focused];
+}
+
+void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o)
+{
+ bool oldPressed = [cell isHighlighted];
+ bool pressed = (o->node() && o->node()->active());
+ if (pressed != oldPressed)
+ [cell setHighlighted:pressed];
+}
+
+bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const
+{
+ // An alternate way to implement this would be to get the appropriate cell object
+ // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
+ // that would be that we would match AppKit behavior more closely, but a disadvantage
+ // would be that we would rely on an AppKit SPI method.
+
+ if (!isEnabled(o))
+ return false;
+
+ // Checkboxes only have tint when checked.
+ if (o->style()->appearance() == CheckboxPart)
+ return isChecked(o);
+
+ // For now assume other controls have tint if enabled.
+ return true;
+}
+
+NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const
+{
+ int fontSize = style->fontSize();
+ if (fontSize >= 16)
+ return NSRegularControlSize;
+ if (fontSize >= 11)
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
+{
+ NSControlSize size;
+ if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) &&
+ minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
+ size = NSRegularControlSize;
+ else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) &&
+ minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
+ size = NSSmallControlSize;
+ else
+ size = NSMiniControlSize;
+ if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
+ [cell setControlSize:size];
+}
+
+IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
+{
+ if (style->effectiveZoom() != 1.0f) {
+ IntSize result = sizes[controlSizeForFont(style)];
+ return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom());
+ }
+ return sizes[controlSizeForFont(style)];
+}
+
+IntSize RenderThemeMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
+{
+ if (style->effectiveZoom() != 1.0f) {
+ IntSize result = sizes[controlSizeForSystemFont(style)];
+ return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom());
+ }
+ return sizes[controlSizeForSystemFont(style)];
+}
+
+void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
+{
+ // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
+ IntSize size = sizeForFont(style, sizes);
+ if (style->width().isIntrinsicOrAuto() && size.width() > 0)
+ style->setWidth(Length(size.width(), Fixed));
+ if (style->height().isAuto() && size.height() > 0)
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+void RenderThemeMac::setFontFromControlSize(CSSStyleSelector*, RenderStyle* style, NSControlSize controlSize) const
+{
+ FontDescription fontDescription;
+ fontDescription.setIsAbsoluteSize(true);
+ fontDescription.setGenericFamily(FontDescription::SerifFamily);
+
+ NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
+ fontDescription.firstFamily().setFamily([font familyName]);
+ fontDescription.setComputedSize([font pointSize] * style->effectiveZoom());
+ fontDescription.setSpecifiedSize([font pointSize] * style->effectiveZoom());
+
+ // Reset line height
+ style->setLineHeight(RenderStyle::initialLineHeight());
+
+ if (style->setFontDescription(fontDescription))
+ style->font().update(0);
+}
+
+NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle* style) const
+{
+ int fontSize = style->fontSize();
+ if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
+ return NSRegularControlSize;
+ if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+bool RenderThemeMac::paintTextField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o));
+ return false;
+}
+
+void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+bool RenderThemeMac::paintCapsLockIndicator(RenderObject*, const PaintInfo& paintInfo, const IntRect& r)
+{
+ if (paintInfo.context->paintingDisabled())
+ return true;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r);
+
+ return false;
+}
+
+bool RenderThemeMac::paintTextArea(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
+ return false;
+}
+
+void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+const int* RenderThemeMac::popupButtonMargins() const
+{
+ static const int margins[3][4] =
+ {
+ { 0, 3, 1, 3 },
+ { 0, 3, 2, 3 },
+ { 0, 1, 0, 1 }
+ };
+ return margins[[popupButton() controlSize]];
+}
+
+const IntSize* RenderThemeMac::popupButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
+ return sizes;
+}
+
+const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
+{
+ static const int padding[3][4] =
+ {
+ { 2, 26, 3, 8 },
+ { 2, 23, 3, 8 },
+ { 2, 22, 3, 10 }
+ };
+ return padding[size];
+}
+
+bool RenderThemeMac::paintMenuList(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ setPopupButtonCellState(o, r);
+
+ NSPopUpButtonCell* popupButton = this->popupButton();
+
+ float zoomLevel = o->style()->effectiveZoom();
+ IntSize size = popupButtonSizes()[[popupButton controlSize]];
+ size.setHeight(size.height() * zoomLevel);
+ size.setWidth(r.width());
+
+ // Now inflate it to account for the shadow.
+ IntRect inflatedRect = r;
+ if (r.width() >= minimumMenuListSize(o->style()))
+ inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel);
+
+ paintInfo.context->save();
+
+#ifndef BUILDING_ON_TIGER
+ // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
+ paintInfo.context->clip(inflatedRect);
+#endif
+
+ if (zoomLevel != 1.0f) {
+ inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
+ inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
+ paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
+ }
+
+ [popupButton drawWithFrame:inflatedRect inView:documentViewFor(o)];
+ [popupButton setControlView:nil];
+
+ paintInfo.context->restore();
+
+ return false;
+}
+
+#if ENABLE(METER_TAG)
+
+IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter* renderMeter, const IntRect& bounds) const
+{
+ if (NoControlPart == renderMeter->style()->appearance())
+ return bounds.size();
+
+ NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter);
+ // Makes enough room for cell's intrinsic size.
+ NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
+ return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
+ bounds.height() < cellSize.height ? cellSize.height : bounds.height());
+}
+
+bool RenderThemeMac::paintMeter(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ if (!renderObject->isMeter())
+ return true;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+
+ // Becaue NSLevelIndicatorCell doesn't support vertical gauge, we use a portable version
+ if (rect.width() < rect.height())
+ return RenderTheme::paintMeter(renderObject, paintInfo, rect);
+
+ NSLevelIndicatorCell* cell = levelIndicatorFor(toRenderMeter(renderObject));
+ paintInfo.context->save();
+ [cell drawWithFrame:rect inView:documentViewFor(renderObject)];
+ [cell setControlView:nil];
+ paintInfo.context->restore();
+
+ return false;
+}
+
+bool RenderThemeMac::supportsMeter(ControlPart part, bool isHorizontal) const
+{
+ switch (part) {
+ case RelevancyLevelIndicatorPart:
+ case DiscreteCapacityLevelIndicatorPart:
+ case RatingLevelIndicatorPart:
+ case MeterPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ return isHorizontal;
+ default:
+ return false;
+ }
+}
+
+NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const
+{
+ switch (part) {
+ case RelevancyLevelIndicatorPart:
+ return NSRelevancyLevelIndicatorStyle;
+ case DiscreteCapacityLevelIndicatorPart:
+ return NSDiscreteCapacityLevelIndicatorStyle;
+ case RatingLevelIndicatorPart:
+ return NSRatingLevelIndicatorStyle;
+ case MeterPart:
+ case ContinuousCapacityLevelIndicatorPart:
+ default:
+ return NSContinuousCapacityLevelIndicatorStyle;
+ }
+
+}
+
+NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter* renderMeter) const
+{
+ RenderStyle* style = renderMeter->style();
+ ASSERT(style->appearance() != NoControlPart);
+
+ if (!m_levelIndicator)
+ m_levelIndicator.adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
+ NSLevelIndicatorCell* cell = m_levelIndicator.get();
+
+ HTMLMeterElement* element = static_cast<HTMLMeterElement*>(renderMeter->node());
+ double value = element->value();
+
+ // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring,
+ // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is.
+ switch (element->gaugeRegion()) {
+ case HTMLMeterElement::GaugeRegionOptimum:
+ // Make meter the green
+ [cell setWarningValue:value + 1];
+ [cell setCriticalValue:value + 2];
+ break;
+ case HTMLMeterElement::GaugeRegionSuboptimal:
+ // Make the meter yellow
+ [cell setWarningValue:value - 1];
+ [cell setCriticalValue:value + 1];
+ break;
+ case HTMLMeterElement::GaugeRegionEvenLessGood:
+ // Make the meter red
+ [cell setWarningValue:value - 2];
+ [cell setCriticalValue:value - 1];
+ break;
+ }
+
+ [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style->appearance())];
+ [cell setBaseWritingDirection:style->isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
+ [cell setMinValue:element->min()];
+ [cell setMaxValue:element->max()];
+ RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
+ [cell setObjectValue:valueObject.get()];
+
+ return cell;
+}
+
+#endif
+
+#if ENABLE(PROGRESS_TAG)
+
+double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress*) const
+{
+ return progressAnimationFrameRate;
+}
+
+double RenderThemeMac::animationDurationForProgressBar(RenderProgress*) const
+{
+ return progressAnimationNumFrames * progressAnimationFrameRate;
+}
+
+void RenderThemeMac::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+bool RenderThemeMac::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
+{
+ if (!renderObject->isProgress())
+ return true;
+
+ RenderProgress* renderProgress = toRenderProgress(renderObject);
+ HIThemeTrackDrawInfo trackInfo;
+ trackInfo.version = 0;
+ trackInfo.kind = renderProgress->position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar;
+ trackInfo.bounds = IntRect(IntPoint(), rect.size());
+ trackInfo.min = 0;
+ trackInfo.max = numeric_limits<SInt32>::max();
+ trackInfo.value = lround(renderProgress->position() * nextafter(trackInfo.max, 0));
+ trackInfo.trackInfo.progress.phase = lround(renderProgress->animationProgress() * nextafter(progressAnimationNumFrames, 0));
+ trackInfo.attributes = kThemeTrackHorizontal;
+ trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive;
+ trackInfo.reserved = 0;
+ trackInfo.filler1 = 0;
+
+ OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(rect.size());
+ if (!imageBuffer)
+ return true;
+
+ HIThemeDrawTrack(&trackInfo, 0, imageBuffer->context()->platformContext(), kHIThemeOrientationNormal);
+
+ paintInfo.context->save();
+
+ if (!renderProgress->style()->isLeftToRightDirection()) {
+ paintInfo.context->translate(2 * rect.x() + rect.width(), 0);
+ paintInfo.context->scale(FloatSize(-1, 1));
+ }
+
+ paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, rect.location());
+
+ paintInfo.context->restore();
+ return false;
+}
+#endif
+
+const float baseFontSize = 11.0f;
+const float baseArrowHeight = 4.0f;
+const float baseArrowWidth = 5.0f;
+const float baseSpaceBetweenArrows = 2.0f;
+const int arrowPaddingLeft = 6;
+const int arrowPaddingRight = 6;
+const int paddingBeforeSeparator = 4;
+const int baseBorderRadius = 5;
+const int styledPopupPaddingLeft = 8;
+const int styledPopupPaddingTop = 1;
+const int styledPopupPaddingBottom = 2;
+
+static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
+ static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
+ static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
+ static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
+ static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ if (r.isEmpty())
+ return;
+
+ CGContextRef context = paintInfo.context->platformContext();
+
+ paintInfo.context->save();
+
+ IntSize topLeftRadius;
+ IntSize topRightRadius;
+ IntSize bottomLeftRadius;
+ IntSize bottomRightRadius;
+
+ o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+
+ int radius = topLeftRadius.width();
+
+ CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
+
+ FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
+ struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
+ RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
+
+ FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
+ struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
+ RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
+
+ struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
+ RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
+
+ RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
+
+ RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
+ paintInfo.context->save();
+ CGContextClipToRect(context, r);
+ paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, mainShading.get());
+ paintInfo.context->restore();
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, topGradient);
+ paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize());
+ CGContextDrawShading(context, topShading.get());
+ paintInfo.context->restore();
+
+ if (!bottomGradient.isEmpty()) {
+ paintInfo.context->save();
+ CGContextClipToRect(context, bottomGradient);
+ paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, bottomShading.get());
+ paintInfo.context->restore();
+ }
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, r);
+ paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, leftShading.get());
+ CGContextDrawShading(context, rightShading.get());
+ paintInfo.context->restore();
+
+ paintInfo.context->restore();
+}
+
+bool RenderThemeMac::paintMenuListButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
+ r.y() + o->style()->borderTopWidth(),
+ r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
+ r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
+ // Draw the gradients to give the styled popup menu a button appearance
+ paintMenuListButtonGradients(o, paintInfo, bounds);
+
+ // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds
+ float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
+ float centerY = bounds.y() + bounds.height() / 2.0f;
+ float arrowHeight = baseArrowHeight * fontScale;
+ float arrowWidth = baseArrowWidth * fontScale;
+ float leftEdge = bounds.right() - arrowPaddingRight * o->style()->effectiveZoom() - arrowWidth;
+ float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
+
+ if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom())
+ return false;
+
+ paintInfo.context->save();
+
+ paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), o->style()->colorSpace());
+ paintInfo.context->setStrokeStyle(NoStroke);
+
+ FloatPoint arrow1[3];
+ arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
+ arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
+ arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
+
+ // Draw the top arrow
+ paintInfo.context->drawConvexPolygon(3, arrow1, true);
+
+ FloatPoint arrow2[3];
+ arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
+ arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
+ arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
+
+ // Draw the bottom arrow
+ paintInfo.context->drawConvexPolygon(3, arrow2, true);
+
+ Color leftSeparatorColor(0, 0, 0, 40);
+ Color rightSeparatorColor(255, 255, 255, 40);
+
+ // FIXME: Should the separator thickness and space be scaled up by fontScale?
+ int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
+ int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * o->style()->effectiveZoom()); // FIXME: Round?
+
+ // Draw the separator to the left of the arrows
+ paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin.
+ paintInfo.context->setStrokeStyle(SolidStroke);
+ paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
+ paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
+ IntPoint(leftEdgeOfSeparator, bounds.bottom()));
+
+ paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
+ paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
+ IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
+
+ paintInfo.context->restore();
+ return false;
+}
+
+static const IntSize* menuListButtonSizes()
+{
+ static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
+ return sizes;
+}
+
+void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ NSControlSize controlSize = controlSizeForFont(style);
+
+ style->resetBorder();
+ style->resetPadding();
+
+ // Height is locked to auto.
+ style->setHeight(Length(Auto));
+
+ // White-space is locked to pre
+ style->setWhiteSpace(PRE);
+
+ // Set the foreground color to black or gray when we have the aqua look.
+ // Cast to RGB32 is to work around a compiler bug.
+ style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
+
+ // Set the button's vertical size.
+ setSizeFromFont(style, menuListButtonSizes());
+
+ // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
+ // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
+ // system font for the control size instead.
+ setFontFromControlSize(selector, style, controlSize);
+
+ style->setBoxShadow(0);
+}
+
+int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style->effectiveZoom();
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingLeft * style->effectiveZoom();
+ return 0;
+}
+
+int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style->effectiveZoom();
+ if (style->appearance() == MenulistButtonPart) {
+ float fontScale = style->fontSize() / baseFontSize;
+ float arrowWidth = baseArrowWidth * fontScale;
+ return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style->effectiveZoom()));
+ }
+ return 0;
+}
+
+int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[topPadding] * style->effectiveZoom();
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingTop * style->effectiveZoom();
+ return 0;
+}
+
+int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style->effectiveZoom();
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingBottom * style->effectiveZoom();
+ return 0;
+}
+
+void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ float fontScale = style->fontSize() / baseFontSize;
+
+ style->resetPadding();
+ style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
+
+ const int minHeight = 15;
+ style->setMinHeight(Length(minHeight, Fixed));
+
+ style->setLineHeight(RenderStyle::initialLineHeight());
+}
+
+void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
+{
+ NSPopUpButtonCell* popupButton = this->popupButton();
+
+ // Set the control size based off the rectangle we're painting into.
+ setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom());
+
+ // Update the various states we respond to.
+ updateActiveState(popupButton, o);
+ updateCheckedState(popupButton, o);
+ updateEnabledState(popupButton, o);
+ updatePressedState(popupButton, o);
+ updateFocusedState(popupButton, o);
+}
+
+const IntSize* RenderThemeMac::menuListSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
+ return sizes;
+}
+
+int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
+{
+ return sizeForSystemFont(style, menuListSizes()).width();
+}
+
+const int trackWidth = 5;
+const int trackRadius = 2;
+
+void RenderThemeMac::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ style->setBoxShadow(0);
+}
+
+bool RenderThemeMac::paintSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = r;
+ float zoomLevel = o->style()->effectiveZoom();
+ float zoomedTrackWidth = trackWidth * zoomLevel;
+
+ if (o->style()->appearance() == SliderHorizontalPart || o->style()->appearance() == MediaSliderPart) {
+ bounds.setHeight(zoomedTrackWidth);
+ bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
+ } else if (o->style()->appearance() == SliderVerticalPart) {
+ bounds.setWidth(zoomedTrackWidth);
+ bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
+ }
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ CGContextRef context = paintInfo.context->platformContext();
+ CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, bounds);
+
+ struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
+ RetainPtr<CGShadingRef> mainShading;
+ if (o->style()->appearance() == SliderVerticalPart)
+ mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
+ else
+ mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
+
+ IntSize radius(trackRadius, trackRadius);
+ paintInfo.context->addRoundedRectClip(bounds,
+ radius, radius,
+ radius, radius);
+ CGContextDrawShading(context, mainShading.get());
+ paintInfo.context->restore();
+
+ return false;
+}
+
+void RenderThemeMac::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ style->setBoxShadow(0);
+}
+
+const float verticalSliderHeightPadding = 0.1f;
+
+bool RenderThemeMac::paintSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(o->parent()->isSlider());
+
+ NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalPart
+ ? sliderThumbVertical()
+ : sliderThumbHorizontal();
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+
+ // Update the various states we respond to.
+ updateActiveState(sliderThumbCell, o->parent());
+ updateEnabledState(sliderThumbCell, o->parent());
+ updateFocusedState(sliderThumbCell, o->parent());
+
+ // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
+ bool oldPressed;
+ if (o->style()->appearance() == SliderThumbVerticalPart)
+ oldPressed = m_isSliderThumbVerticalPressed;
+ else
+ oldPressed = m_isSliderThumbHorizontalPressed;
+
+ bool pressed = toRenderSlider(o->parent())->inDragMode();
+
+ if (o->style()->appearance() == SliderThumbVerticalPart)
+ m_isSliderThumbVerticalPressed = pressed;
+ else
+ m_isSliderThumbHorizontalPressed = pressed;
+
+ if (pressed != oldPressed) {
+ if (pressed)
+ [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
+ else
+ [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
+ }
+
+ FloatRect bounds = r;
+ // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
+ if (o->style()->appearance() == SliderThumbVerticalPart)
+ bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o->style()->effectiveZoom());
+
+ paintInfo.context->save();
+ float zoomLevel = o->style()->effectiveZoom();
+
+ FloatRect unzoomedRect = bounds;
+ if (zoomLevel != 1.0f) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+
+ [sliderThumbCell drawWithFrame:unzoomedRect inView:documentViewFor(o)];
+ [sliderThumbCell setControlView:nil];
+
+ paintInfo.context->restore();
+
+ return false;
+}
+
+bool RenderThemeMac::paintSearchField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ NSSearchFieldCell* search = this->search();
+
+ setSearchCellState(o, r);
+
+ paintInfo.context->save();
+
+ float zoomLevel = o->style()->effectiveZoom();
+
+ IntRect unzoomedRect = r;
+
+ if (zoomLevel != 1.0f) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+
+ // Set the search button to nil before drawing. Then reset it so we can draw it later.
+ [search setSearchButtonCell:nil];
+
+ [search drawWithFrame:NSRect(unzoomedRect) inView:documentViewFor(o)];
+#ifdef BUILDING_ON_TIGER
+ if ([search showsFirstResponder])
+ wkDrawTextFieldCellFocusRing(search, NSRect(unzoomedRect));
+#endif
+
+ [search setControlView:nil];
+ [search resetSearchButtonCell];
+
+ paintInfo.context->restore();
+
+ return false;
+}
+
+void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect&)
+{
+ NSSearchFieldCell* search = this->search();
+
+ [search setControlSize:controlSizeForFont(o->style())];
+
+ // Update the various states we respond to.
+ updateActiveState(search, o);
+ updateEnabledState(search, o);
+ updateFocusedState(search, o);
+}
+
+const IntSize* RenderThemeMac::searchFieldSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
+ return sizes;
+}
+
+void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // Use the font size to determine the intrinsic width of the control.
+ setSizeFromFont(style, searchFieldSizes());
+}
+
+void RenderThemeMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element*) const
+{
+ // Override border.
+ style->resetBorder();
+ const short borderWidth = 2 * style->effectiveZoom();
+ style->setBorderLeftWidth(borderWidth);
+ style->setBorderLeftStyle(INSET);
+ style->setBorderRightWidth(borderWidth);
+ style->setBorderRightStyle(INSET);
+ style->setBorderBottomWidth(borderWidth);
+ style->setBorderBottomStyle(INSET);
+ style->setBorderTopWidth(borderWidth);
+ style->setBorderTopStyle(INSET);
+
+ // Override height.
+ style->setHeight(Length(Auto));
+ setSearchFieldSize(style);
+
+ // Override padding size to match AppKit text positioning.
+ const int padding = 1 * style->effectiveZoom();
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(padding, Fixed));
+ style->setPaddingBottom(Length(padding, Fixed));
+
+ NSControlSize controlSize = controlSizeForFont(style);
+ setFontFromControlSize(selector, style, controlSize);
+
+ style->setBoxShadow(0);
+}
+
+bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* input = o->node()->shadowAncestorNode();
+ if (!input->renderer()->isBox())
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ setSearchCellState(input->renderer(), r);
+
+ NSSearchFieldCell* search = this->search();
+
+ updateActiveState([search cancelButtonCell], o);
+ updatePressedState([search cancelButtonCell], o);
+
+ paintInfo.context->save();
+
+ float zoomLevel = o->style()->effectiveZoom();
+
+ FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())];
+
+#if ENABLE(INPUT_SPEECH)
+ // Take care of cases where the cancel button was not aligned with the right border of the input element (for e.g.
+ // when speech input is enabled for the input element.
+ IntRect absBoundingBox = input->renderer()->absoluteBoundingBoxRect();
+ int absRight = absBoundingBox.x() + absBoundingBox.width() - input->renderBox()->paddingRight() - input->renderBox()->borderRight();
+ int spaceToRightOfCancelButton = absRight - (r.x() + r.width());
+ localBounds.setX(localBounds.x() - spaceToRightOfCancelButton);
+#endif
+
+ localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r);
+
+ FloatRect unzoomedRect(localBounds);
+ if (zoomLevel != 1.0f) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+
+ [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
+ [[search cancelButtonCell] setControlView:nil];
+
+ paintInfo.context->restore();
+ return false;
+}
+
+const IntSize* RenderThemeMac::cancelButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
+ return sizes;
+}
+
+void RenderThemeMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ IntSize size = sizeForSystemFont(style, cancelButtonSizes());
+ style->setWidth(Length(size.width(), Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+ style->setBoxShadow(0);
+}
+
+const IntSize* RenderThemeMac::resultsButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
+ return sizes;
+}
+
+const int emptyResultsOffset = 9;
+void RenderThemeMac::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+ style->setBoxShadow(0);
+}
+
+bool RenderThemeMac::paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&)
+{
+ return false;
+}
+
+void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width(), Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+ style->setBoxShadow(0);
+}
+
+bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* input = o->node()->shadowAncestorNode();
+ if (!input->renderer()->isBox())
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ setSearchCellState(input->renderer(), r);
+
+ NSSearchFieldCell* search = this->search();
+
+ if ([search searchMenuTemplate] != nil)
+ [search setSearchMenuTemplate:nil];
+
+ updateActiveState([search searchButtonCell], o);
+
+ FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())];
+ localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r);
+
+ [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)];
+ [[search searchButtonCell] setControlView:nil];
+ return false;
+}
+
+const int resultsArrowWidth = 5;
+void RenderThemeMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+ style->setBoxShadow(0);
+}
+
+bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* input = o->node()->shadowAncestorNode();
+ if (!input->renderer()->isBox())
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ setSearchCellState(input->renderer(), r);
+
+ NSSearchFieldCell* search = this->search();
+
+ updateActiveState([search searchButtonCell], o);
+
+ if (![search searchMenuTemplate])
+ [search setSearchMenuTemplate:searchMenuTemplate()];
+
+ paintInfo.context->save();
+
+ float zoomLevel = o->style()->effectiveZoom();
+
+ FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->borderBoxRect())];
+ localBounds = convertToPaintingRect(input->renderer(), o, localBounds, r);
+
+ IntRect unzoomedRect(localBounds);
+ if (zoomLevel != 1.0f) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+
+ [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
+ [[search searchButtonCell] setControlView:nil];
+
+ paintInfo.context->restore();
+
+ return false;
+}
+
+#if ENABLE(VIDEO)
+typedef enum {
+ MediaControllerThemeClassic = 1,
+ MediaControllerThemeQuickTime = 2
+} MediaControllerThemeStyle;
+
+static int mediaControllerTheme()
+{
+ static int controllerTheme = -1;
+
+ if (controllerTheme != -1)
+ return controllerTheme;
+
+ controllerTheme = MediaControllerThemeClassic;
+
+ Boolean validKey;
+ Boolean useQTMediaUIPref = CFPreferencesGetAppBooleanValue(CFSTR("UseQuickTimeMediaUI"), CFSTR("com.apple.WebCore"), &validKey);
+
+#if !defined(BUILDING_ON_TIGER)
+ if (validKey && !useQTMediaUIPref)
+ return controllerTheme;
+#else
+ if (!validKey || !useQTMediaUIPref)
+ return controllerTheme;
+#endif
+
+ controllerTheme = MediaControllerThemeQuickTime;
+ return controllerTheme;
+}
+#endif
+
+const int sliderThumbWidth = 15;
+const int sliderThumbHeight = 15;
+const int mediaSliderThumbWidth = 13;
+const int mediaSliderThumbHeight = 14;
+
+void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const
+{
+ float zoomLevel = o->style()->effectiveZoom();
+ if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
+ o->style()->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
+ }
+
+#if ENABLE(VIDEO)
+ adjustMediaSliderThumbSize(o);
+#endif
+}
+
+#if ENABLE(VIDEO)
+
+void RenderThemeMac::adjustMediaSliderThumbSize(RenderObject* o) const
+{
+ ControlPart part = o->style()->appearance();
+
+ if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) {
+ int width = mediaSliderThumbWidth;
+ int height = mediaSliderThumbHeight;
+
+ if (mediaControllerTheme() == MediaControllerThemeQuickTime) {
+ CGSize size;
+
+ wkMeasureMediaUIPart(part == MediaSliderThumbPart ? MediaSliderThumb : MediaVolumeSliderThumb, MediaControllerThemeQuickTime, NULL, &size);
+ width = size.width;
+ height = size.height;
+ }
+
+ float zoomLevel = o->style()->effectiveZoom();
+ o->style()->setWidth(Length(static_cast<int>(width * zoomLevel), Fixed));
+ o->style()->setHeight(Length(static_cast<int>(height * zoomLevel), Fixed));
+ }
+}
+
+enum WKMediaControllerThemeState {
+ MediaUIPartDisabledFlag = 1 << 0,
+ MediaUIPartPressedFlag = 1 << 1,
+ MediaUIPartDrawEndCapsFlag = 1 << 3,
+};
+
+static unsigned getMediaUIPartStateFlags(Node* node)
+{
+ unsigned flags = 0;
+
+ if (node->disabled())
+ flags |= MediaUIPartDisabledFlag;
+ else if (node->active())
+ flags |= MediaUIPartPressedFlag;
+ return flags;
+}
+
+// Utility to scale when the UI part are not scaled by wkDrawMediaUIPart
+static FloatRect getUnzoomedRectAndAdjustCurrentContext(RenderObject* o, const PaintInfo& paintInfo, const IntRect &originalRect)
+{
+ float zoomLevel = o->style()->effectiveZoom();
+ FloatRect unzoomedRect(originalRect);
+ if (zoomLevel != 1.0f && mediaControllerTheme() == MediaControllerThemeQuickTime) {
+ unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
+ unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
+ paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
+ paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
+ paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
+ }
+ return unzoomedRect;
+}
+
+
+bool RenderThemeMac::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaFullscreenButton, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ Node* mediaNode = node ? node->shadowAncestorNode() : 0;
+ if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
+ return false;
+
+ if (MediaControlMuteButtonElement* btn = static_cast<MediaControlMuteButtonElement*>(node)) {
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(btn->displayType(), mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+
+ }
+ return false;
+}
+
+bool RenderThemeMac::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ Node* mediaNode = node ? node->shadowAncestorNode() : 0;
+ if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
+ return false;
+
+ if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(node)) {
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(btn->displayType(), mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ }
+ return false;
+}
+
+bool RenderThemeMac::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaSeekBackButton, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaSeekForwardButton, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ Node* mediaNode = node ? node->shadowAncestorNode() : 0;
+ if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
+ return false;
+
+ HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode);
+ if (!mediaElement)
+ return false;
+
+ RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
+ ExceptionCode ignoredException;
+ float timeLoaded = timeRanges->length() ? timeRanges->end(0, ignoredException) : 0;
+ float currentTime = mediaElement->currentTime();
+ float duration = mediaElement->duration();
+ if (isnan(duration))
+ duration = 0;
+
+ paintInfo.context->save();
+ FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r);
+ wkDrawMediaSliderTrack(mediaControllerTheme(), paintInfo.context->platformContext(), unzoomedRect,
+ timeLoaded, currentTime, duration, getMediaUIPartStateFlags(node));
+
+ paintInfo.context->restore();
+ return false;
+}
+
+bool RenderThemeMac::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaSliderThumb, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaRewindButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaRewindButton, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaReturnToRealtimeButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaReturnToRealtimeButton, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ HTMLInputElement* node = static_cast<HTMLInputElement*>(o->node());
+ if (!node)
+ return false;
+
+ MediaControlToggleClosedCaptionsButtonElement* btn = static_cast<MediaControlToggleClosedCaptionsButtonElement*>(node);
+ if (!btn)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(btn->displayType(), mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+
+ return false;
+}
+
+bool RenderThemeMac::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaTimelineContainer, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaCurrentTime(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ paintInfo.context->save();
+ FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r);
+ wkDrawMediaUIPart(MediaCurrentTimeDisplay, mediaControllerTheme(), paintInfo.context->platformContext(), unzoomedRect, getMediaUIPartStateFlags(node));
+ paintInfo.context->restore();
+ return false;
+}
+
+bool RenderThemeMac::paintMediaTimeRemaining(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ paintInfo.context->save();
+ FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r);
+ wkDrawMediaUIPart(MediaTimeRemainingDisplay, mediaControllerTheme(), paintInfo.context->platformContext(), unzoomedRect, getMediaUIPartStateFlags(node));
+ paintInfo.context->restore();
+ return false;
+}
+
+bool RenderThemeMac::paintMediaVolumeSliderContainer(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaVolumeSliderContainer, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaVolumeSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaVolumeSlider, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+bool RenderThemeMac::paintMediaVolumeSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Node* node = o->node();
+ if (!node)
+ return false;
+
+ LocalCurrentGraphicsContext localContext(paintInfo.context);
+ wkDrawMediaUIPart(MediaVolumeSliderThumb, mediaControllerTheme(), paintInfo.context->platformContext(), r, getMediaUIPartStateFlags(node));
+ return false;
+}
+
+String RenderThemeMac::extraMediaControlsStyleSheet()
+{
+#if PLATFORM(MAC)
+ if (mediaControllerTheme() == MediaControllerThemeQuickTime)
+ return String(mediaControlsQuickTimeUserAgentStyleSheet, sizeof(mediaControlsQuickTimeUserAgentStyleSheet));
+
+ return String();
+#else
+ ASSERT_NOT_REACHED();
+ return String();
+#endif
+}
+
+bool RenderThemeMac::shouldRenderMediaControlPart(ControlPart part, Element* element)
+{
+ switch (part) {
+ case MediaVolumeSliderContainerPart:
+ case MediaVolumeSliderPart:
+ case MediaVolumeSliderMuteButtonPart:
+ case MediaVolumeSliderThumbPart: {
+ HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(element);
+ return mediaControllerTheme() == MediaControllerThemeQuickTime && mediaElement->hasAudio();
+ }
+ case MediaToggleClosedCaptionsButtonPart:
+ // We rely on QTKit to render captions so don't enable the button unless it will be able to do so.
+ if (!element->hasTagName(videoTag))
+ return false;
+ default:
+ break;
+ }
+
+ return RenderTheme::shouldRenderMediaControlPart(part, element);
+}
+
+IntPoint RenderThemeMac::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size) const
+{
+ static const int xOffset = -4;
+ static const int yOffset = 5;
+
+ float zoomLevel = muteButton->renderer()->style()->effectiveZoom();
+ int y = yOffset * zoomLevel + muteButton->renderBox()->offsetHeight() - size.height();
+ FloatPoint absPoint = muteButton->renderer()->localToAbsolute(FloatPoint(muteButton->renderBox()->offsetLeft(), y), true, true);
+ if (absPoint.y() < 0)
+ y = muteButton->renderBox()->height();
+ return IntPoint(xOffset * zoomLevel, y);
+}
+
+#endif // ENABLE(VIDEO)
+
+NSPopUpButtonCell* RenderThemeMac::popupButton() const
+{
+ if (!m_popupButton) {
+ m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
+ [m_popupButton.get() setUsesItemFromMenu:NO];
+ [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ return m_popupButton.get();
+}
+
+NSSearchFieldCell* RenderThemeMac::search() const
+{
+ if (!m_search) {
+ m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
+ [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
+ [m_search.get() setBezeled:YES];
+ [m_search.get() setEditable:YES];
+ [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ return m_search.get();
+}
+
+NSMenu* RenderThemeMac::searchMenuTemplate() const
+{
+ if (!m_searchMenuTemplate)
+ m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]);
+
+ return m_searchMenuTemplate.get();
+}
+
+NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
+{
+ if (!m_sliderThumbHorizontal) {
+ m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]);
+ [m_sliderThumbHorizontal.get() setTitle:nil];
+ [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
+ [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
+ [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ return m_sliderThumbHorizontal.get();
+}
+
+NSSliderCell* RenderThemeMac::sliderThumbVertical() const
+{
+ if (!m_sliderThumbVertical) {
+ m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]);
+ [m_sliderThumbVertical.get() setTitle:nil];
+ [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
+ [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
+ [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
+ }
+
+ return m_sliderThumbVertical.get();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeSafari.cpp b/Source/WebCore/rendering/RenderThemeSafari.cpp
new file mode 100644
index 0000000..6d5d322
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeSafari.cpp
@@ -0,0 +1,1215 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Apple Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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 "RenderThemeSafari.h"
+#include "RenderThemeWin.h"
+#include "Settings.h"
+
+#if USE(SAFARI_THEME)
+
+#include "CSSValueKeywords.h"
+#include "Document.h"
+#include "Element.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContextCG.h"
+#include "HTMLInputElement.h"
+#include "HTMLMediaElement.h"
+#include "HTMLNames.h"
+#include "RenderMediaControls.h"
+#include "RenderSlider.h"
+#include "RenderView.h"
+#include "RetainPtr.h"
+#include "SoftLinking.h"
+#include "cssstyleselector.h"
+#include <CoreGraphics/CoreGraphics.h>
+
+using std::min;
+
+// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeMac.
+
+namespace WebCore {
+
+using namespace HTMLNames;
+using namespace SafariTheme;
+
+enum {
+ topMargin,
+ rightMargin,
+ bottomMargin,
+ leftMargin
+};
+
+enum {
+ topPadding,
+ rightPadding,
+ bottomPadding,
+ leftPadding
+};
+
+PassRefPtr<RenderTheme> RenderThemeSafari::create()
+{
+ return adoptRef(new RenderThemeSafari);
+}
+
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+ static RenderTheme* safariTheme = RenderThemeSafari::create().releaseRef();
+ static RenderTheme* windowsTheme = RenderThemeWin::create().releaseRef();
+
+ // FIXME: This is called before Settings has been initialized by WebKit, so will return a
+ // potentially wrong answer the very first time it's called (see
+ // <https://bugs.webkit.org/show_bug.cgi?id=26493>).
+ if (Settings::shouldPaintNativeControls()) {
+ RenderTheme::setCustomFocusRingColor(safariTheme->platformFocusRingColor());
+ return windowsTheme; // keep the reference of one.
+ }
+ return safariTheme; // keep the reference of one.
+}
+
+#ifdef DEBUG_ALL
+SOFT_LINK_DEBUG_LIBRARY(SafariTheme)
+#else
+SOFT_LINK_LIBRARY(SafariTheme)
+#endif
+
+SOFT_LINK(SafariTheme, paintThemePart, void, __stdcall, (ThemePart part, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state), (part, context, rect, size, state))
+#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2
+SOFT_LINK(SafariTheme, STPaintProgressIndicator, void, APIENTRY, (ProgressIndicatorType type, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state, float value), (type, context, rect, size, state, value))
+#endif
+SOFT_LINK_OPTIONAL(SafariTheme, STCopyThemeColor, CGColorRef, APIENTRY, (unsigned color, SafariTheme::ThemeControlState));
+
+static const unsigned stFocusRingColorID = 4;
+
+static const unsigned aquaFocusRingColor = 0xFF7DADD9;
+
+static RGBA32 makeRGBAFromCGColor(CGColorRef color)
+{
+ const CGFloat* components = CGColorGetComponents(color);
+ return makeRGBA(255 * components[0], 255 * components[1], 255 * components[2], 255 * components[3]);
+}
+
+ThemeControlState RenderThemeSafari::determineState(RenderObject* o) const
+{
+ ThemeControlState result = 0;
+ if (isActive(o))
+ result |= SafariTheme::ActiveState;
+ if (isEnabled(o) && !isReadOnlyControl(o))
+ result |= SafariTheme::EnabledState;
+ if (isPressed(o))
+ result |= SafariTheme::PressedState;
+ if (isChecked(o))
+ result |= SafariTheme::CheckedState;
+ if (isIndeterminate(o))
+ result |= SafariTheme::IndeterminateCheckedState;
+ if (isFocused(o))
+ result |= SafariTheme::FocusedState;
+ if (isDefault(o))
+ result |= SafariTheme::DefaultState;
+ return result;
+}
+
+static NSControlSize controlSizeFromRect(const IntRect& rect, const IntSize sizes[])
+{
+ if (sizes[NSRegularControlSize].height() == rect.height())
+ return NSRegularControlSize;
+ else if (sizes[NSMiniControlSize].height() == rect.height())
+ return NSMiniControlSize;
+
+ return NSSmallControlSize;
+}
+
+RenderThemeSafari::RenderThemeSafari()
+{
+}
+
+RenderThemeSafari::~RenderThemeSafari()
+{
+}
+
+Color RenderThemeSafari::platformActiveSelectionBackgroundColor() const
+{
+ return Color(181, 213, 255);
+}
+
+Color RenderThemeSafari::platformInactiveSelectionBackgroundColor() const
+{
+ return Color(212, 212, 212);
+}
+
+Color RenderThemeSafari::activeListBoxSelectionBackgroundColor() const
+{
+ // FIXME: This should probably just be a darker version of the platformActiveSelectionBackgroundColor
+ return Color(56, 117, 215);
+}
+
+Color RenderThemeSafari::platformFocusRingColor() const
+{
+ static Color focusRingColor;
+
+ if (!focusRingColor.isValid()) {
+ if (STCopyThemeColorPtr()) {
+ RetainPtr<CGColorRef> color(AdoptCF, STCopyThemeColorPtr()(stFocusRingColorID, SafariTheme::ActiveState));
+ focusRingColor = makeRGBAFromCGColor(color.get());
+ }
+ if (!focusRingColor.isValid())
+ focusRingColor = aquaFocusRingColor;
+ }
+
+ return focusRingColor;
+}
+
+static float systemFontSizeForControlSize(NSControlSize controlSize)
+{
+ static float sizes[] = { 13.0f, 11.0f, 9.0f };
+
+ return sizes[controlSize];
+}
+
+void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) const
+{
+ static FontDescription systemFont;
+ static FontDescription smallSystemFont;
+ static FontDescription menuFont;
+ static FontDescription labelFont;
+ static FontDescription miniControlFont;
+ static FontDescription smallControlFont;
+ static FontDescription controlFont;
+
+ FontDescription* cachedDesc;
+ float fontSize = 0;
+ switch (propId) {
+ case CSSValueSmallCaption:
+ cachedDesc = &smallSystemFont;
+ if (!smallSystemFont.isAbsoluteSize())
+ fontSize = systemFontSizeForControlSize(NSSmallControlSize);
+ break;
+ case CSSValueMenu:
+ cachedDesc = &menuFont;
+ if (!menuFont.isAbsoluteSize())
+ fontSize = systemFontSizeForControlSize(NSRegularControlSize);
+ break;
+ case CSSValueStatusBar:
+ cachedDesc = &labelFont;
+ if (!labelFont.isAbsoluteSize())
+ fontSize = 10.0f;
+ break;
+ case CSSValueWebkitMiniControl:
+ cachedDesc = &miniControlFont;
+ if (!miniControlFont.isAbsoluteSize())
+ fontSize = systemFontSizeForControlSize(NSMiniControlSize);
+ break;
+ case CSSValueWebkitSmallControl:
+ cachedDesc = &smallControlFont;
+ if (!smallControlFont.isAbsoluteSize())
+ fontSize = systemFontSizeForControlSize(NSSmallControlSize);
+ break;
+ case CSSValueWebkitControl:
+ cachedDesc = &controlFont;
+ if (!controlFont.isAbsoluteSize())
+ fontSize = systemFontSizeForControlSize(NSRegularControlSize);
+ break;
+ default:
+ cachedDesc = &systemFont;
+ if (!systemFont.isAbsoluteSize())
+ fontSize = 13.0f;
+ }
+
+ if (fontSize) {
+ cachedDesc->setIsAbsoluteSize(true);
+ cachedDesc->setGenericFamily(FontDescription::NoFamily);
+ cachedDesc->firstFamily().setFamily("Lucida Grande");
+ cachedDesc->setSpecifiedSize(fontSize);
+ cachedDesc->setWeight(FontWeightNormal);
+ cachedDesc->setItalic(false);
+ }
+ fontDescription = *cachedDesc;
+}
+
+bool RenderThemeSafari::isControlStyled(const RenderStyle* style, const BorderData& border,
+ const FillLayer& background, const Color& backgroundColor) const
+{
+ // If we didn't find SafariTheme.dll we won't be able to paint any themed controls.
+ if (!SafariThemeLibrary())
+ return true;
+
+ if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart)
+ return style->border() != border;
+ return RenderTheme::isControlStyled(style, border, background, backgroundColor);
+}
+
+void RenderThemeSafari::adjustRepaintRect(const RenderObject* o, IntRect& r)
+{
+ NSControlSize controlSize = controlSizeForFont(o->style());
+
+ switch (o->style()->appearance()) {
+ case CheckboxPart: {
+ // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
+ // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
+ r = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize));
+ break;
+ }
+ case RadioPart: {
+ // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
+ // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
+ r = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize));
+ break;
+ }
+ case PushButtonPart:
+ case DefaultButtonPart:
+ case ButtonPart: {
+ // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
+ // shadow" and the check. We don't consider this part of the bounds of the control in WebKit.
+ if (r.height() <= buttonSizes()[NSRegularControlSize].height())
+ r = inflateRect(r, buttonSizes()[controlSize], buttonMargins(controlSize));
+ break;
+ }
+ case MenulistPart: {
+ r = inflateRect(r, popupButtonSizes()[controlSize], popupButtonMargins(controlSize));
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+IntRect RenderThemeSafari::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
+{
+ // Only do the inflation if the available width/height are too small. Otherwise try to
+ // fit the glow/check space into the available box's width/height.
+ int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
+ int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
+ IntRect result(r);
+ if (widthDelta < 0) {
+ result.setX(result.x() - margins[leftMargin]);
+ result.setWidth(result.width() - widthDelta);
+ }
+ if (heightDelta < 0) {
+ result.setY(result.y() - margins[topMargin]);
+ result.setHeight(result.height() - heightDelta);
+ }
+ return result;
+}
+
+int RenderThemeSafari::baselinePosition(const RenderObject* o) const
+{
+ if (!o->isBox())
+ return 0;
+
+ if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) {
+ const RenderBox* box = toRenderBox(o);
+ return box->marginTop() + box->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
+ }
+
+ return RenderTheme::baselinePosition(o);
+}
+
+bool RenderThemeSafari::controlSupportsTints(const RenderObject* o) const
+{
+ if (!isEnabled(o))
+ return false;
+
+ // Checkboxes only have tint when checked.
+ if (o->style()->appearance() == CheckboxPart)
+ return isChecked(o);
+
+ // For now assume other controls have tint if enabled.
+ return true;
+}
+
+NSControlSize RenderThemeSafari::controlSizeForFont(RenderStyle* style) const
+{
+ int fontSize = style->fontSize();
+ if (fontSize >= 16)
+ return NSRegularControlSize;
+ if (fontSize >= 11)
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+/*
+void RenderThemeSafari::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
+{
+ NSControlSize size;
+ if (minSize.width() >= sizes[NSRegularControlSize].width() &&
+ minSize.height() >= sizes[NSRegularControlSize].height())
+ size = NSRegularControlSize;
+ else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
+ minSize.height() >= sizes[NSSmallControlSize].height())
+ size = NSSmallControlSize;
+ else
+ size = NSMiniControlSize;
+ if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
+ [cell setControlSize:size];
+}
+*/
+IntSize RenderThemeSafari::sizeForFont(RenderStyle* style, const IntSize* sizes) const
+{
+ return sizes[controlSizeForFont(style)];
+}
+
+IntSize RenderThemeSafari::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
+{
+ return sizes[controlSizeForSystemFont(style)];
+}
+
+void RenderThemeSafari::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
+{
+ // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
+ IntSize size = sizeForFont(style, sizes);
+ if (style->width().isIntrinsicOrAuto() && size.width() > 0)
+ style->setWidth(Length(size.width(), Fixed));
+ if (style->height().isAuto() && size.height() > 0)
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+void RenderThemeSafari::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
+{
+ FontDescription fontDescription;
+ fontDescription.setIsAbsoluteSize(true);
+ fontDescription.setGenericFamily(FontDescription::SerifFamily);
+
+ float fontSize = systemFontSizeForControlSize(controlSize);
+ fontDescription.firstFamily().setFamily("Lucida Grande");
+ fontDescription.setComputedSize(fontSize);
+ fontDescription.setSpecifiedSize(fontSize);
+
+ // Reset line height
+ style->setLineHeight(RenderStyle::initialLineHeight());
+
+ if (style->setFontDescription(fontDescription))
+ style->font().update(selector->fontSelector());
+}
+
+NSControlSize RenderThemeSafari::controlSizeForSystemFont(RenderStyle* style) const
+{
+ int fontSize = style->fontSize();
+ if (fontSize >= 13)
+ return NSRegularControlSize;
+ if (fontSize >= 11)
+ return NSSmallControlSize;
+ return NSMiniControlSize;
+}
+
+bool RenderThemeSafari::paintCheckbox(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ NSControlSize controlSize = controlSizeForFont(o->style());
+
+ IntRect inflatedRect = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize));
+ paintThemePart(SafariTheme::CheckboxPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
+
+ return false;
+}
+
+const IntSize* RenderThemeSafari::checkboxSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
+ return sizes;
+}
+
+const int* RenderThemeSafari::checkboxMargins(NSControlSize controlSize) const
+{
+ static const int margins[3][4] =
+ {
+ { 2, 2, 2, 2 },
+ { 2, 2, 2, 1 },
+ { 1, 0, 0, 0 },
+ };
+ return margins[controlSize];
+}
+
+void RenderThemeSafari::setCheckboxSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // Use the font size to determine the intrinsic width of the control.
+ setSizeFromFont(style, checkboxSizes());
+}
+
+bool RenderThemeSafari::paintRadio(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ NSControlSize controlSize = controlSizeForFont(o->style());
+
+ IntRect inflatedRect = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize));
+ paintThemePart(RadioButtonPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
+
+ return false;
+}
+
+const IntSize* RenderThemeSafari::radioSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
+ return sizes;
+}
+
+const int* RenderThemeSafari::radioMargins(NSControlSize controlSize) const
+{
+ static const int margins[3][4] =
+ {
+ { 1, 2, 2, 2 },
+ { 0, 1, 2, 1 },
+ { 0, 0, 1, 0 },
+ };
+ return margins[controlSize];
+}
+
+void RenderThemeSafari::setRadioSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // Use the font size to determine the intrinsic width of the control.
+ setSizeFromFont(style, radioSizes());
+}
+
+void RenderThemeSafari::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
+{
+ // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large
+ // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
+ // by definition constrained, since we select mini only for small cramped environments.
+ // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
+ // padding.
+ const int padding = 8;
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(0, Fixed));
+ style->setPaddingBottom(Length(0, Fixed));
+}
+
+void RenderThemeSafari::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // There are three appearance constants for buttons.
+ // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow
+ // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't
+ // scale vertically.
+ // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically.
+ // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will
+ // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination
+ // solely on the rectangle of the control.
+
+ // Determine our control size based off our font.
+ NSControlSize controlSize = controlSizeForFont(style);
+
+ if (style->appearance() == PushButtonPart) {
+ // Ditch the border.
+ style->resetBorder();
+
+ // Height is locked to auto.
+ style->setHeight(Length(Auto));
+
+ // White-space is locked to pre
+ style->setWhiteSpace(PRE);
+
+ // Set the button's vertical size.
+ setButtonSize(style);
+
+ // Add in the padding that we'd like to use.
+ setButtonPaddingFromControlSize(style, controlSize);
+
+ // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
+ // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
+ // system font for the control size instead.
+ setFontFromControlSize(selector, style, controlSize);
+ } else {
+ // Set a min-height so that we can't get smaller than the mini button.
+ style->setMinHeight(Length(15, Fixed));
+
+ // Reset the top and bottom borders.
+ style->resetBorderTop();
+ style->resetBorderBottom();
+ }
+}
+
+const IntSize* RenderThemeSafari::buttonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
+ return sizes;
+}
+
+const int* RenderThemeSafari::buttonMargins(NSControlSize controlSize) const
+{
+ static const int margins[3][4] =
+ {
+ { 4, 6, 7, 6 },
+ { 4, 5, 6, 5 },
+ { 0, 1, 1, 1 },
+ };
+ return margins[controlSize];
+}
+
+void RenderThemeSafari::setButtonSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // Use the font size to determine the intrinsic width of the control.
+ setSizeFromFont(style, buttonSizes());
+}
+
+bool RenderThemeSafari::paintButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ // We inflate the rect as needed to account for padding included in the cell to accommodate the button
+ // shadow. We don't consider this part of the bounds of the control in WebKit.
+
+ NSControlSize controlSize = controlSizeFromRect(r, buttonSizes());
+ IntRect inflatedRect = r;
+
+ ThemePart part;
+ if (r.height() <= buttonSizes()[NSRegularControlSize].height()) {
+ // Push button
+ part = SafariTheme::PushButtonPart;
+
+ IntSize size = buttonSizes()[controlSize];
+ size.setWidth(r.width());
+
+ // Center the button within the available space.
+ if (inflatedRect.height() > size.height()) {
+ inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
+ inflatedRect.setHeight(size.height());
+ }
+
+ // Now inflate it to account for the shadow.
+ inflatedRect = inflateRect(inflatedRect, size, buttonMargins(controlSize));
+ } else
+ part = SafariTheme::SquareButtonPart;
+
+ paintThemePart(part, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o));
+ return false;
+}
+
+bool RenderThemeSafari::paintTextField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ paintThemePart(SafariTheme::TextFieldPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState);
+ return false;
+}
+
+void RenderThemeSafari::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+bool RenderThemeSafari::paintCapsLockIndicator(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 1
+ ASSERT(SafariThemeLibrary());
+
+ if (paintInfo.context->paintingDisabled())
+ return true;
+
+ paintThemePart(CapsLockPart, paintInfo.context->platformContext(), r, (NSControlSize)0, (ThemeControlState)0);
+
+ return false;
+#else
+ return true;
+#endif
+}
+
+bool RenderThemeSafari::paintTextArea(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ paintThemePart(SafariTheme::TextAreaPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState);
+ return false;
+}
+
+void RenderThemeSafari::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
+{
+}
+
+const int* RenderThemeSafari::popupButtonMargins(NSControlSize size) const
+{
+ static const int margins[3][4] =
+ {
+ { 2, 3, 3, 3 },
+ { 1, 3, 3, 3 },
+ { 0, 1, 0, 1 }
+ };
+ return margins[size];
+}
+
+const IntSize* RenderThemeSafari::popupButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
+ return sizes;
+}
+
+const int* RenderThemeSafari::popupButtonPadding(NSControlSize size) const
+{
+ static const int padding[3][4] =
+ {
+ { 2, 26, 3, 8 },
+ { 2, 23, 3, 8 },
+ { 2, 22, 3, 10 }
+ };
+ return padding[size];
+}
+
+bool RenderThemeSafari::paintMenuList(RenderObject* o, const PaintInfo& info, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ NSControlSize controlSize = controlSizeFromRect(r, popupButtonSizes());
+ IntRect inflatedRect = r;
+ IntSize size = popupButtonSizes()[controlSize];
+ size.setWidth(r.width());
+
+ // Now inflate it to account for the shadow.
+ if (r.width() >= minimumMenuListSize(o->style()))
+ inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(controlSize));
+
+ paintThemePart(DropDownButtonPart, info.context->platformContext(), inflatedRect, controlSize, determineState(o));
+
+ return false;
+}
+
+const float baseFontSize = 11.0f;
+const float baseArrowHeight = 5.0f;
+const float baseArrowWidth = 7.0f;
+const int arrowPaddingLeft = 5;
+const int arrowPaddingRight = 5;
+const int paddingBeforeSeparator = 4;
+const int baseBorderRadius = 5;
+const int styledPopupPaddingLeft = 8;
+const int styledPopupPaddingTop = 1;
+const int styledPopupPaddingBottom = 2;
+
+static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
+ static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
+ static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
+ static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
+{
+ static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
+ static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
+ float a = inData[0];
+ int i = 0;
+ for (i = 0; i < 4; i++)
+ outData[i] = (1.0f - a) * dark[i] + a * light[i];
+}
+
+void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ if (r.isEmpty())
+ return;
+
+ CGContextRef context = paintInfo.context->platformContext();
+
+ paintInfo.context->save();
+
+ IntSize topLeftRadius;
+ IntSize topRightRadius;
+ IntSize bottomLeftRadius;
+ IntSize bottomRightRadius;
+
+ o->style()->getBorderRadiiForRect(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+
+ int radius = topLeftRadius.width();
+
+ CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
+
+ FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
+ struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
+ RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
+
+ FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
+ struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
+ RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
+
+ struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
+ RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
+
+ RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
+
+ RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace, CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
+ paintInfo.context->save();
+ CGContextClipToRect(context, r);
+ paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, mainShading.get());
+ paintInfo.context->restore();
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, topGradient);
+ paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), topLeftRadius, topRightRadius, IntSize(), IntSize());
+ CGContextDrawShading(context, topShading.get());
+ paintInfo.context->restore();
+
+ if (!bottomGradient.isEmpty()) {
+ paintInfo.context->save();
+ CGContextClipToRect(context, bottomGradient);
+ paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), IntSize(), IntSize(), bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, bottomShading.get());
+ paintInfo.context->restore();
+ }
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, r);
+ paintInfo.context->addRoundedRectClip(r, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
+ CGContextDrawShading(context, leftShading.get());
+ CGContextDrawShading(context, rightShading.get());
+ paintInfo.context->restore();
+
+ paintInfo.context->restore();
+}
+
+bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
+ r.y() + o->style()->borderTopWidth(),
+ r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
+ r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
+ // Draw the gradients to give the styled popup menu a button appearance
+ paintMenuListButtonGradients(o, paintInfo, bounds);
+
+ // Since we actually know the size of the control here, we restrict the font scale to make sure the arrow will fit vertically in the bounds
+ float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / baseArrowHeight);
+ float centerY = bounds.y() + bounds.height() / 2.0f;
+ float arrowHeight = baseArrowHeight * fontScale;
+ float arrowWidth = baseArrowWidth * fontScale;
+ float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
+
+ if (bounds.width() < arrowWidth + arrowPaddingLeft)
+ return false;
+
+ paintInfo.context->save();
+
+ paintInfo.context->setFillColor(o->style()->visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
+ paintInfo.context->setStrokeColor(NoStroke, ColorSpaceDeviceRGB);
+
+ FloatPoint arrow[3];
+ arrow[0] = FloatPoint(leftEdge, centerY - arrowHeight / 2.0f);
+ arrow[1] = FloatPoint(leftEdge + arrowWidth, centerY - arrowHeight / 2.0f);
+ arrow[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + arrowHeight / 2.0f);
+
+ // Draw the arrow
+ paintInfo.context->drawConvexPolygon(3, arrow, true);
+
+ Color leftSeparatorColor(0, 0, 0, 40);
+ Color rightSeparatorColor(255, 255, 255, 40);
+
+ // FIXME: Should the separator thickness and space be scaled up by fontScale?
+ int separatorSpace = 2;
+ int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round?
+
+ // Draw the separator to the left of the arrows
+ paintInfo.context->setStrokeThickness(1.0f);
+ paintInfo.context->setStrokeStyle(SolidStroke);
+ paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
+ paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
+ IntPoint(leftEdgeOfSeparator, bounds.bottom()));
+
+ paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
+ paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
+ IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
+
+ paintInfo.context->restore();
+ return false;
+}
+
+void RenderThemeSafari::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ NSControlSize controlSize = controlSizeForFont(style);
+
+ style->resetBorder();
+ style->resetPadding();
+
+ // Height is locked to auto.
+ style->setHeight(Length(Auto));
+
+ // White-space is locked to pre
+ style->setWhiteSpace(PRE);
+
+ // Set the foreground color to black or gray when we have the aqua look.
+ // Cast to RGB32 is to work around a compiler bug.
+ style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
+
+ // Set the button's vertical size.
+ setButtonSize(style);
+
+ // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
+ // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
+ // system font for the control size instead.
+ setFontFromControlSize(selector, style, controlSize);
+}
+
+int RenderThemeSafari::popupInternalPaddingLeft(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[leftPadding];
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingLeft;
+ return 0;
+}
+
+int RenderThemeSafari::popupInternalPaddingRight(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[rightPadding];
+ if (style->appearance() == MenulistButtonPart) {
+ float fontScale = style->fontSize() / baseFontSize;
+ float arrowWidth = baseArrowWidth * fontScale;
+ return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator));
+ }
+ return 0;
+}
+
+int RenderThemeSafari::popupInternalPaddingTop(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[topPadding];
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingTop;
+ return 0;
+}
+
+int RenderThemeSafari::popupInternalPaddingBottom(RenderStyle* style) const
+{
+ if (style->appearance() == MenulistPart)
+ return popupButtonPadding(controlSizeForFont(style))[bottomPadding];
+ if (style->appearance() == MenulistButtonPart)
+ return styledPopupPaddingBottom;
+ return 0;
+}
+
+void RenderThemeSafari::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ float fontScale = style->fontSize() / baseFontSize;
+
+ style->resetPadding();
+ style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
+
+ const int minHeight = 15;
+ style->setMinHeight(Length(minHeight, Fixed));
+
+ style->setLineHeight(RenderStyle::initialLineHeight());
+}
+
+const IntSize* RenderThemeSafari::menuListSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
+ return sizes;
+}
+
+int RenderThemeSafari::minimumMenuListSize(RenderStyle* style) const
+{
+ return sizeForSystemFont(style, menuListSizes()).width();
+}
+
+const int trackWidth = 5;
+const int trackRadius = 2;
+
+bool RenderThemeSafari::paintSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = r;
+
+ if (o->style()->appearance() == SliderHorizontalPart) {
+ bounds.setHeight(trackWidth);
+ bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
+ } else if (o->style()->appearance() == SliderVerticalPart) {
+ bounds.setWidth(trackWidth);
+ bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
+ }
+
+ CGContextRef context = paintInfo.context->platformContext();
+ CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
+
+ paintInfo.context->save();
+ CGContextClipToRect(context, bounds);
+
+ struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
+ RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
+ RetainPtr<CGShadingRef> mainShading;
+ if (o->style()->appearance() == SliderVerticalPart)
+ mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
+ else
+ mainShading.adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
+
+ IntSize radius(trackRadius, trackRadius);
+ paintInfo.context->addRoundedRectClip(bounds,
+ radius, radius,
+ radius, radius);
+ CGContextDrawShading(context, mainShading.get());
+ paintInfo.context->restore();
+
+ return false;
+}
+
+void RenderThemeSafari::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ style->setBoxShadow(0);
+}
+
+const float verticalSliderHeightPadding = 0.1f;
+
+bool RenderThemeSafari::paintSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ ASSERT(o->parent()->isSlider());
+
+ bool pressed = toRenderSlider(o->parent())->inDragMode();
+ ThemeControlState state = determineState(o->parent());
+ state &= ~SafariTheme::PressedState;
+ if (pressed)
+ state |= SafariTheme::PressedState;
+
+ paintThemePart(SliderThumbPart, paintInfo.context->platformContext(), r, NSSmallControlSize, state);
+ return false;
+}
+
+const int sliderThumbWidth = 15;
+const int sliderThumbHeight = 15;
+
+void RenderThemeSafari::adjustSliderThumbSize(RenderObject* o) const
+{
+ if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(sliderThumbWidth, Fixed));
+ o->style()->setHeight(Length(sliderThumbHeight, Fixed));
+ }
+#if ENABLE(VIDEO)
+ else if (o->style()->appearance() == MediaSliderThumbPart)
+ RenderMediaControls::adjustMediaSliderThumbSize(o);
+#endif
+}
+
+bool RenderThemeSafari::paintSearchField(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ ASSERT(SafariThemeLibrary());
+
+ paintThemePart(SafariTheme::SearchFieldPart, paintInfo.context->platformContext(), r, controlSizeFromRect(r, searchFieldSizes()), determineState(o));
+ return false;
+}
+
+const IntSize* RenderThemeSafari::searchFieldSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 15) };
+ return sizes;
+}
+
+void RenderThemeSafari::setSearchFieldSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // Use the font size to determine the intrinsic width of the control.
+ setSizeFromFont(style, searchFieldSizes());
+}
+
+void RenderThemeSafari::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Override border.
+ style->resetBorder();
+ const short borderWidth = 2;
+ style->setBorderLeftWidth(borderWidth);
+ style->setBorderLeftStyle(INSET);
+ style->setBorderRightWidth(borderWidth);
+ style->setBorderRightStyle(INSET);
+ style->setBorderBottomWidth(borderWidth);
+ style->setBorderBottomStyle(INSET);
+ style->setBorderTopWidth(borderWidth);
+ style->setBorderTopStyle(INSET);
+
+ // Override height.
+ style->setHeight(Length(Auto));
+ setSearchFieldSize(style);
+
+ // Override padding size to match AppKit text positioning.
+ const int padding = 1;
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(padding, Fixed));
+ style->setPaddingBottom(Length(padding, Fixed));
+
+ NSControlSize controlSize = controlSizeForFont(style);
+ setFontFromControlSize(selector, style, controlSize);
+}
+
+bool RenderThemeSafari::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
+{
+ ASSERT(SafariThemeLibrary());
+
+ Node* input = o->node()->shadowAncestorNode();
+ ASSERT(input);
+ RenderObject* renderer = input->renderer();
+ ASSERT(renderer);
+
+ IntRect searchRect = renderer->absoluteBoundingBoxRect();
+
+ paintThemePart(SafariTheme::SearchFieldCancelButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
+ return false;
+}
+
+const IntSize* RenderThemeSafari::cancelButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
+ return sizes;
+}
+
+void RenderThemeSafari::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize size = sizeForSystemFont(style, cancelButtonSizes());
+ style->setWidth(Length(size.width(), Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+const IntSize* RenderThemeSafari::resultsButtonSizes() const
+{
+ static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
+ return sizes;
+}
+
+const int emptyResultsOffset = 9;
+void RenderThemeSafari::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+bool RenderThemeSafari::paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&)
+{
+ return false;
+}
+
+void RenderThemeSafari::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width(), Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+bool RenderThemeSafari::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
+{
+ ASSERT(SafariThemeLibrary());
+
+ Node* input = o->node()->shadowAncestorNode();
+ ASSERT(input);
+ RenderObject* renderer = input->renderer();
+ ASSERT(renderer);
+
+ IntRect searchRect = renderer->absoluteBoundingBoxRect();
+
+ paintThemePart(SafariTheme::SearchFieldResultsDecorationPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
+ return false;
+}
+
+const int resultsArrowWidth = 5;
+void RenderThemeSafari::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize size = sizeForSystemFont(style, resultsButtonSizes());
+ style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
+ style->setHeight(Length(size.height(), Fixed));
+}
+
+bool RenderThemeSafari::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect&)
+{
+ ASSERT(SafariThemeLibrary());
+
+ Node* input = o->node()->shadowAncestorNode();
+ ASSERT(input);
+ RenderObject* renderer = input->renderer();
+ ASSERT(renderer);
+
+ IntRect searchRect = renderer->absoluteBoundingBoxRect();
+
+ paintThemePart(SafariTheme::SearchFieldResultsButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o));
+ return false;
+}
+#if ENABLE(VIDEO)
+bool RenderThemeSafari::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
+}
+
+bool RenderThemeSafari::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
+}
+#endif
+
+} // namespace WebCore
+
+#endif // #if USE(SAFARI_THEME)
diff --git a/Source/WebCore/rendering/RenderThemeSafari.h b/Source/WebCore/rendering/RenderThemeSafari.h
new file mode 100644
index 0000000..b91e28e
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeSafari.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2007, 2008 Apple Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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 RenderThemeSafari_h
+#define RenderThemeSafari_h
+
+#if USE(SAFARI_THEME)
+
+#include "RenderTheme.h"
+
+// If you have an empty placeholder SafariThemeConstants.h, then include SafariTheme.h
+// This is a workaround until a version of WebKitSupportLibrary is released with an updated SafariThemeConstants.h
+#include <SafariTheme/SafariThemeConstants.h>
+#ifndef SafariThemeConstants_h
+#include <SafariTheme/SafariTheme.h>
+#endif
+
+#if PLATFORM(WIN)
+typedef void* HANDLE;
+typedef struct HINSTANCE__* HINSTANCE;
+typedef HINSTANCE HMODULE;
+#endif
+
+namespace WebCore {
+
+using namespace SafariTheme;
+
+class RenderStyle;
+
+class RenderThemeSafari : public RenderTheme {
+public:
+ static PassRefPtr<RenderTheme> create();
+
+ // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline
+ // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of
+ // controls that need to do this.
+ virtual int baselinePosition(const RenderObject*) const;
+
+ // A method asking if the control changes its tint when the window has focus or not.
+ virtual bool controlSupportsTints(const RenderObject*) const;
+
+ // A general method asking if any control tinting is supported at all.
+ virtual bool supportsControlTints() const { return true; }
+
+ virtual void adjustRepaintRect(const RenderObject*, IntRect&);
+
+ virtual bool isControlStyled(const RenderStyle*, const BorderData&,
+ const FillLayer&, const Color& backgroundColor) const;
+
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color activeListBoxSelectionBackgroundColor() const;
+
+ virtual Color platformFocusRingColor() const;
+
+ // System fonts.
+ virtual void systemFont(int propId, FontDescription&) const;
+
+ virtual int minimumMenuListSize(RenderStyle*) const;
+
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+ virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual int popupInternalPaddingLeft(RenderStyle*) const;
+ virtual int popupInternalPaddingRight(RenderStyle*) const;
+ virtual int popupInternalPaddingTop(RenderStyle*) const;
+ virtual int popupInternalPaddingBottom(RenderStyle*) const;
+
+protected:
+ // Methods for each appearance value.
+ virtual bool paintCheckbox(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void setCheckboxSize(RenderStyle*) const;
+
+ virtual bool paintRadio(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void setRadioSize(RenderStyle*) const;
+
+ virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const;
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void setButtonSize(RenderStyle*) const;
+
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintTextArea(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual bool paintSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&);
+
+#if ENABLE(VIDEO)
+ virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+#endif
+
+private:
+ RenderThemeSafari();
+ virtual ~RenderThemeSafari();
+
+ IntRect inflateRect(const IntRect&, const IntSize&, const int* margins) const;
+
+ // Get the control size based off the font. Used by some of the controls (like buttons).
+
+ NSControlSize controlSizeForFont(RenderStyle*) const;
+ NSControlSize controlSizeForSystemFont(RenderStyle*) const;
+ //void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize);
+ void setSizeFromFont(RenderStyle*, const IntSize* sizes) const;
+ IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const;
+ IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const;
+ void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const;
+
+ // Helpers for adjusting appearance and for painting
+ const IntSize* checkboxSizes() const;
+ const int* checkboxMargins(NSControlSize) const;
+
+ const IntSize* radioSizes() const;
+ const int* radioMargins(NSControlSize) const;
+
+ void setButtonPaddingFromControlSize(RenderStyle*, NSControlSize) const;
+ const IntSize* buttonSizes() const;
+ const int* buttonMargins(NSControlSize) const;
+
+ const IntSize* popupButtonSizes() const;
+ const int* popupButtonMargins(NSControlSize) const;
+ const int* popupButtonPadding(NSControlSize) const;
+ void paintMenuListButtonGradients(RenderObject*, const PaintInfo&, const IntRect&);
+ const IntSize* menuListSizes() const;
+
+ const IntSize* searchFieldSizes() const;
+ const IntSize* cancelButtonSizes() const;
+ const IntSize* resultsButtonSizes() const;
+ void setSearchFieldSize(RenderStyle*) const;
+
+ ThemeControlState determineState(RenderObject*) const;
+};
+
+} // namespace WebCore
+
+#endif // #if USE(SAFARI_THEME)
+
+#endif // RenderThemeSafari_h
diff --git a/Source/WebCore/rendering/RenderThemeWin.cpp b/Source/WebCore/rendering/RenderThemeWin.cpp
new file mode 100644
index 0000000..f0f8268
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeWin.cpp
@@ -0,0 +1,1118 @@
+/*
+ * Copyright (C) 2006, 2007 Apple Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderThemeWin.h"
+
+#include "CSSValueKeywords.h"
+#include "Element.h"
+#include "Frame.h"
+#include "GraphicsContext.h"
+#include "LocalWindowsContext.h"
+#include "RenderSlider.h"
+#include "Settings.h"
+#include "SoftLinking.h"
+#include "SystemInfo.h"
+#include "UserAgentStyleSheets.h"
+
+#if ENABLE(VIDEO)
+#include "RenderMediaControls.h"
+#endif
+
+#include <tchar.h>
+
+/*
+ * The following constants are used to determine how a widget is drawn using
+ * Windows' Theme API. For more information on theme parts and states see
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+ */
+
+// Generic state constants
+#define TS_NORMAL 1
+#define TS_HOVER 2
+#define TS_ACTIVE 3
+#define TS_DISABLED 4
+#define TS_FOCUSED 5
+
+// Button constants
+#define BP_BUTTON 1
+#define BP_RADIO 2
+#define BP_CHECKBOX 3
+
+// Textfield constants
+#define TFP_TEXTFIELD 1
+#define EP_EDITBORDER_NOSCROLL 6
+#define TFS_READONLY 6
+
+// ComboBox constants (from vsstyle.h)
+#define CP_DROPDOWNBUTTON 1
+#define CP_BORDER 4
+#define CP_READONLY 5
+#define CP_DROPDOWNBUTTONRIGHT 6
+
+// TrackBar (slider) parts
+#define TKP_TRACK 1
+#define TKP_TRACKVERT 2
+
+// TrackBar (slider) thumb parts
+#define TKP_THUMBBOTTOM 4
+#define TKP_THUMBTOP 5
+#define TKP_THUMBLEFT 7
+#define TKP_THUMBRIGHT 8
+
+// Trackbar (slider) thumb states
+#define TUS_NORMAL 1
+#define TUS_HOT 2
+#define TUS_PRESSED 3
+#define TUS_FOCUSED 4
+#define TUS_DISABLED 5
+
+// button states
+#define PBS_NORMAL 1
+#define PBS_HOT 2
+#define PBS_PRESSED 3
+#define PBS_DISABLED 4
+#define PBS_DEFAULTED 5
+
+// Spin button parts
+#define SPNP_UP 1
+#define SPNP_DOWN 2
+
+// Spin button states
+#define DNS_NORMAL 1
+#define DNS_HOT 2
+#define DNS_PRESSED 3
+#define DNS_DISABLED 4
+#define UPS_NORMAL 1
+#define UPS_HOT 2
+#define UPS_PRESSED 3
+#define UPS_DISABLED 4
+
+
+SOFT_LINK_LIBRARY(uxtheme)
+SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
+SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
+SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect))
+SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ())
+SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId))
+
+static bool haveTheme;
+
+static const unsigned vistaMenuListButtonOutset = 1;
+
+using namespace std;
+
+namespace WebCore {
+
+// This is the fixed width IE and Firefox use for buttons on dropdown menus
+static const int dropDownButtonWidth = 17;
+
+static const int shell32MagnifierIconIndex = 22;
+
+// Default font size to match Firefox.
+static const float defaultControlFontPixelSize = 13;
+
+static const float defaultCancelButtonSize = 9;
+static const float minCancelButtonSize = 5;
+static const float maxCancelButtonSize = 21;
+static const float defaultSearchFieldResultsDecorationSize = 13;
+static const float minSearchFieldResultsDecorationSize = 9;
+static const float maxSearchFieldResultsDecorationSize = 30;
+static const float defaultSearchFieldResultsButtonWidth = 18;
+
+static bool gWebKitIsBeingUnloaded;
+
+static bool documentIsInApplicationChromeMode(const Document* document)
+{
+ Settings* settings = document->settings();
+ return settings && settings->inApplicationChromeMode();
+}
+
+void RenderThemeWin::setWebKitIsBeingUnloaded()
+{
+ gWebKitIsBeingUnloaded = true;
+}
+
+PassRefPtr<RenderTheme> RenderThemeWin::create()
+{
+ return adoptRef(new RenderThemeWin);
+}
+
+#if !USE(SAFARI_THEME)
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+ static RenderTheme* winTheme = RenderThemeWin::create().releaseRef();
+ return winTheme;
+}
+#endif
+
+RenderThemeWin::RenderThemeWin()
+ : m_buttonTheme(0)
+ , m_textFieldTheme(0)
+ , m_menuListTheme(0)
+ , m_sliderTheme(0)
+ , m_spinButtonTheme(0)
+{
+ haveTheme = uxthemeLibrary() && IsThemeActive();
+}
+
+RenderThemeWin::~RenderThemeWin()
+{
+ // If WebKit is being unloaded, then uxtheme.dll is no longer available.
+ if (gWebKitIsBeingUnloaded || !uxthemeLibrary())
+ return;
+ close();
+}
+
+HANDLE RenderThemeWin::buttonTheme() const
+{
+ if (haveTheme && !m_buttonTheme)
+ m_buttonTheme = OpenThemeData(0, L"Button");
+ return m_buttonTheme;
+}
+
+HANDLE RenderThemeWin::textFieldTheme() const
+{
+ if (haveTheme && !m_textFieldTheme)
+ m_textFieldTheme = OpenThemeData(0, L"Edit");
+ return m_textFieldTheme;
+}
+
+HANDLE RenderThemeWin::menuListTheme() const
+{
+ if (haveTheme && !m_menuListTheme)
+ m_menuListTheme = OpenThemeData(0, L"ComboBox");
+ return m_menuListTheme;
+}
+
+HANDLE RenderThemeWin::sliderTheme() const
+{
+ if (haveTheme && !m_sliderTheme)
+ m_sliderTheme = OpenThemeData(0, L"TrackBar");
+ return m_sliderTheme;
+}
+
+HANDLE RenderThemeWin::spinButtonTheme() const
+{
+ if (haveTheme && !m_spinButtonTheme)
+ m_spinButtonTheme = OpenThemeData(0, L"Spin");
+ return m_spinButtonTheme;
+}
+
+void RenderThemeWin::close()
+{
+ // This method will need to be called when the OS theme changes to flush our cached themes.
+ if (m_buttonTheme)
+ CloseThemeData(m_buttonTheme);
+ if (m_textFieldTheme)
+ CloseThemeData(m_textFieldTheme);
+ if (m_menuListTheme)
+ CloseThemeData(m_menuListTheme);
+ if (m_sliderTheme)
+ CloseThemeData(m_sliderTheme);
+ if (m_spinButtonTheme)
+ CloseThemeData(m_spinButtonTheme);
+ m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = m_spinButtonTheme = 0;
+
+ haveTheme = uxthemeLibrary() && IsThemeActive();
+}
+
+void RenderThemeWin::themeChanged()
+{
+ close();
+}
+
+String RenderThemeWin::extraDefaultStyleSheet()
+{
+ return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet));
+}
+
+String RenderThemeWin::extraQuirksStyleSheet()
+{
+ return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet));
+}
+
+bool RenderThemeWin::supportsHover(const RenderStyle*) const
+{
+ // The Classic/2k look has no hover effects.
+ return haveTheme;
+}
+
+Color RenderThemeWin::platformActiveSelectionBackgroundColor() const
+{
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const
+{
+ // This color matches Firefox.
+ return Color(176, 176, 176);
+}
+
+Color RenderThemeWin::platformActiveSelectionForegroundColor() const
+{
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+Color RenderThemeWin::platformInactiveSelectionForegroundColor() const
+{
+ return platformActiveSelectionForegroundColor();
+}
+
+static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize)
+{
+ fontDescription.setIsAbsoluteSize(true);
+ fontDescription.setGenericFamily(FontDescription::NoFamily);
+ fontDescription.firstFamily().setFamily(String(logFont.lfFaceName));
+ fontDescription.setSpecifiedSize(fontSize);
+ fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight.
+ fontDescription.setItalic(logFont.lfItalic);
+}
+
+static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont)
+{
+ fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight));
+}
+
+void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const
+{
+ static FontDescription captionFont;
+ static FontDescription controlFont;
+ static FontDescription smallCaptionFont;
+ static FontDescription menuFont;
+ static FontDescription iconFont;
+ static FontDescription messageBoxFont;
+ static FontDescription statusBarFont;
+ static FontDescription systemFont;
+
+ static bool initialized;
+ static NONCLIENTMETRICS ncm;
+
+ if (!initialized) {
+ initialized = true;
+ ncm.cbSize = sizeof(NONCLIENTMETRICS);
+ ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
+ }
+
+ switch (propId) {
+ case CSSValueIcon: {
+ if (!iconFont.isAbsoluteSize()) {
+ LOGFONT logFont;
+ ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0);
+ fillFontDescription(iconFont, logFont);
+ }
+ fontDescription = iconFont;
+ break;
+ }
+ case CSSValueMenu:
+ if (!menuFont.isAbsoluteSize())
+ fillFontDescription(menuFont, ncm.lfMenuFont);
+ fontDescription = menuFont;
+ break;
+ case CSSValueMessageBox:
+ if (!messageBoxFont.isAbsoluteSize())
+ fillFontDescription(messageBoxFont, ncm.lfMessageFont);
+ fontDescription = messageBoxFont;
+ break;
+ case CSSValueStatusBar:
+ if (!statusBarFont.isAbsoluteSize())
+ fillFontDescription(statusBarFont, ncm.lfStatusFont);
+ fontDescription = statusBarFont;
+ break;
+ case CSSValueCaption:
+ if (!captionFont.isAbsoluteSize())
+ fillFontDescription(captionFont, ncm.lfCaptionFont);
+ fontDescription = captionFont;
+ break;
+ case CSSValueSmallCaption:
+ if (!smallCaptionFont.isAbsoluteSize())
+ fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont);
+ fontDescription = smallCaptionFont;
+ break;
+ case CSSValueWebkitSmallControl:
+ case CSSValueWebkitMiniControl: // Just map to small.
+ case CSSValueWebkitControl: // Just map to small.
+ if (!controlFont.isAbsoluteSize()) {
+ HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+ if (hGDI) {
+ LOGFONT logFont;
+ if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
+ fillFontDescription(controlFont, logFont, defaultControlFontPixelSize);
+ }
+ }
+ fontDescription = controlFont;
+ break;
+ default: { // Everything else uses the stock GUI font.
+ if (!systemFont.isAbsoluteSize()) {
+ HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT);
+ if (hGDI) {
+ LOGFONT logFont;
+ if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0)
+ fillFontDescription(systemFont, logFont);
+ }
+ }
+ fontDescription = systemFont;
+ }
+ }
+}
+
+bool RenderThemeWin::supportsFocus(ControlPart appearance) const
+{
+ switch (appearance) {
+ case PushButtonPart:
+ case ButtonPart:
+ case DefaultButtonPart:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool RenderThemeWin::supportsFocusRing(const RenderStyle* style) const
+{
+ return supportsFocus(style->appearance());
+}
+
+unsigned RenderThemeWin::determineClassicState(RenderObject* o, ControlSubPart subPart)
+{
+ unsigned state = 0;
+ switch (o->style()->appearance()) {
+ case PushButtonPart:
+ case ButtonPart:
+ case DefaultButtonPart:
+ state = DFCS_BUTTONPUSH;
+ if (!isEnabled(o))
+ state |= DFCS_INACTIVE;
+ else if (isPressed(o))
+ state |= DFCS_PUSHED;
+ break;
+ case RadioPart:
+ case CheckboxPart:
+ state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK;
+ if (isChecked(o))
+ state |= DFCS_CHECKED;
+ if (!isEnabled(o))
+ state |= DFCS_INACTIVE;
+ else if (isPressed(o))
+ state |= DFCS_PUSHED;
+ break;
+ case MenulistPart:
+ state = DFCS_SCROLLCOMBOBOX;
+ if (!isEnabled(o))
+ state |= DFCS_INACTIVE;
+ else if (isPressed(o))
+ state |= DFCS_PUSHED;
+ break;
+ case InnerSpinButtonPart: {
+ bool isUpButton = subPart == SpinButtonUp;
+ state = isUpButton ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
+ if (!isEnabled(o) || isReadOnlyControl(o))
+ state |= DFCS_INACTIVE;
+ else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o))
+ state |= DFCS_PUSHED;
+ else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o))
+ state |= DFCS_HOT;
+ break;
+ }
+ default:
+ break;
+ }
+ return state;
+}
+
+unsigned RenderThemeWin::determineState(RenderObject* o)
+{
+ unsigned result = TS_NORMAL;
+ ControlPart appearance = o->style()->appearance();
+ if (!isEnabled(o))
+ result = TS_DISABLED;
+ else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance || SearchFieldPart == appearance))
+ result = TFS_READONLY; // Readonly is supported on textfields.
+ else if (isPressed(o)) // Active overrides hover and focused.
+ result = TS_ACTIVE;
+ else if (supportsFocus(appearance) && isFocused(o))
+ result = TS_FOCUSED;
+ else if (isHovered(o))
+ result = TS_HOVER;
+ if (isChecked(o))
+ result += 4; // 4 unchecked states, 4 checked states.
+ return result;
+}
+
+unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o)
+{
+ unsigned result = TUS_NORMAL;
+ if (!isEnabled(o->parent()))
+ result = TUS_DISABLED;
+ else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent()))
+ result = TUS_FOCUSED;
+ else if (toRenderSlider(o->parent())->inDragMode())
+ result = TUS_PRESSED;
+ else if (isHovered(o))
+ result = TUS_HOT;
+ return result;
+}
+
+unsigned RenderThemeWin::determineButtonState(RenderObject* o)
+{
+ unsigned result = PBS_NORMAL;
+ if (!isEnabled(o))
+ result = PBS_DISABLED;
+ else if (isPressed(o))
+ result = PBS_PRESSED;
+ else if (supportsFocus(o->style()->appearance()) && isFocused(o))
+ result = PBS_DEFAULTED;
+ else if (isHovered(o))
+ result = PBS_HOT;
+ else if (isDefault(o))
+ result = PBS_DEFAULTED;
+ return result;
+}
+
+unsigned RenderThemeWin::determineSpinButtonState(RenderObject* o, ControlSubPart subPart)
+{
+ bool isUpButton = subPart == SpinButtonUp;
+ unsigned result = isUpButton ? UPS_NORMAL : DNS_NORMAL;
+ if (!isEnabled(o) || isReadOnlyControl(o))
+ result = isUpButton ? UPS_DISABLED : DNS_DISABLED;
+ else if (isPressed(o) && isUpButton == isSpinUpButtonPartPressed(o))
+ result = isUpButton ? UPS_PRESSED : DNS_PRESSED;
+ else if (isHovered(o) && isUpButton == isSpinUpButtonPartHovered(o))
+ result = isUpButton ? UPS_HOT : DNS_HOT;
+ return result;
+}
+
+ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o, ControlSubPart subPart)
+{
+ ThemeData result;
+ switch (o->style()->appearance()) {
+ case PushButtonPart:
+ case ButtonPart:
+ case DefaultButtonPart:
+ case CheckboxPart:
+ case RadioPart:
+ result.m_part = DFC_BUTTON;
+ result.m_state = determineClassicState(o);
+ break;
+ case MenulistPart:
+ result.m_part = DFC_SCROLL;
+ result.m_state = determineClassicState(o);
+ break;
+ case SearchFieldPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ result.m_part = TFP_TEXTFIELD;
+ result.m_state = determineState(o);
+ break;
+ case SliderHorizontalPart:
+ result.m_part = TKP_TRACK;
+ result.m_state = TS_NORMAL;
+ break;
+ case SliderVerticalPart:
+ result.m_part = TKP_TRACKVERT;
+ result.m_state = TS_NORMAL;
+ break;
+ case SliderThumbHorizontalPart:
+ result.m_part = TKP_THUMBBOTTOM;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case SliderThumbVerticalPart:
+ result.m_part = TKP_THUMBRIGHT;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case InnerSpinButtonPart:
+ result.m_part = DFC_SCROLL;
+ result.m_state = determineClassicState(o, subPart);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+ThemeData RenderThemeWin::getThemeData(RenderObject* o, ControlSubPart subPart)
+{
+ if (!haveTheme)
+ return getClassicThemeData(o, subPart);
+
+ ThemeData result;
+ switch (o->style()->appearance()) {
+ case PushButtonPart:
+ case ButtonPart:
+ case DefaultButtonPart:
+ result.m_part = BP_BUTTON;
+ result.m_state = determineButtonState(o);
+ break;
+ case CheckboxPart:
+ result.m_part = BP_CHECKBOX;
+ result.m_state = determineState(o);
+ break;
+ case MenulistPart:
+ case MenulistButtonPart:
+ result.m_part = isRunningOnVistaOrLater() ? CP_DROPDOWNBUTTONRIGHT : CP_DROPDOWNBUTTON;
+ if (isRunningOnVistaOrLater() && documentIsInApplicationChromeMode(o->document())) {
+ // The "readonly" look we use in application chrome mode
+ // only uses a "normal" look for the drop down button.
+ result.m_state = TS_NORMAL;
+ } else
+ result.m_state = determineState(o);
+ break;
+ case RadioPart:
+ result.m_part = BP_RADIO;
+ result.m_state = determineState(o);
+ break;
+ case SearchFieldPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ result.m_part = isRunningOnVistaOrLater() ? EP_EDITBORDER_NOSCROLL : TFP_TEXTFIELD;
+ result.m_state = determineState(o);
+ break;
+ case SliderHorizontalPart:
+ result.m_part = TKP_TRACK;
+ result.m_state = TS_NORMAL;
+ break;
+ case SliderVerticalPart:
+ result.m_part = TKP_TRACKVERT;
+ result.m_state = TS_NORMAL;
+ break;
+ case SliderThumbHorizontalPart:
+ result.m_part = TKP_THUMBBOTTOM;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case SliderThumbVerticalPart:
+ result.m_part = TKP_THUMBRIGHT;
+ result.m_state = determineSliderThumbState(o);
+ break;
+ case InnerSpinButtonPart:
+ result.m_part = subPart == SpinButtonUp ? SPNP_UP : SPNP_DOWN;
+ result.m_state = determineSpinButtonState(o, subPart);
+ break;
+ }
+
+ return result;
+}
+
+static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r)
+{
+ bool alphaBlend = false;
+ if (theme)
+ alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state);
+ LocalWindowsContext windowsContext(context, r, alphaBlend);
+ RECT widgetRect = r;
+ if (theme)
+ DrawThemeBackground(theme, windowsContext.hdc(), themeData.m_part, themeData.m_state, &widgetRect, 0);
+ else {
+ HDC hdc = windowsContext.hdc();
+ if (themeData.m_part == TFP_TEXTFIELD) {
+ ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY)
+ ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1));
+ else
+ ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1));
+ } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) {
+ ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
+ } else if ((o->style()->appearance() == SliderThumbHorizontalPart ||
+ o->style()->appearance() == SliderThumbVerticalPart) &&
+ (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP ||
+ themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) {
+ ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+ if (themeData.m_state == TUS_DISABLED) {
+ static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
+ HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits);
+ if (patternBmp) {
+ HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp);
+ COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE));
+ COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT));
+ POINT p;
+ ::GetViewportOrgEx(hdc, &p);
+ ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL);
+ HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush);
+ ::FillRect(hdc, &widgetRect, brush);
+ ::SetTextColor(hdc, oldForeColor);
+ ::SetBkColor(hdc, oldBackColor);
+ ::SelectObject(hdc, oldBrush);
+ ::DeleteObject(brush);
+ } else
+ ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT);
+ ::DeleteObject(patternBmp);
+ }
+ } else {
+ // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists.
+ if (o->style()->appearance() == DefaultButtonPart) {
+ HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
+ ::FrameRect(hdc, &widgetRect, brush);
+ ::InflateRect(&widgetRect, -1, -1);
+ ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE);
+ }
+ ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state);
+ }
+ }
+}
+
+bool RenderThemeWin::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ drawControl(i.context, o, buttonTheme(), getThemeData(o), r);
+ return false;
+}
+
+void RenderThemeWin::adjustInnerSpinButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ int width = ::GetSystemMetrics(SM_CXVSCROLL);
+ if (width <= 0)
+ width = 17; // Vista's default.
+ style->setWidth(Length(width, Fixed));
+ style->setMinWidth(Length(width, Fixed));
+}
+
+bool RenderThemeWin::paintInnerSpinButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ // We split the specified rectangle into two vertically. We can't draw a
+ // spin button of which height is less than 2px.
+ if (r.height() < 2)
+ return false;
+ IntRect upRect(r);
+ upRect.setHeight(r.height() / 2);
+ IntRect downRect(r);
+ downRect.setY(upRect.bottom());
+ downRect.setHeight(r.height() - upRect.height());
+ drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonUp), upRect);
+ drawControl(i.context, o, spinButtonTheme(), getThemeData(o, SpinButtonDown), downRect);
+ return false;
+}
+
+void RenderThemeWin::setCheckboxSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox.
+ // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
+ // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
+ // metrics.
+ if (style->width().isIntrinsicOrAuto())
+ style->setWidth(Length(13, Fixed));
+ if (style->height().isAuto())
+ style->setHeight(Length(13, Fixed));
+}
+
+bool RenderThemeWin::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ drawControl(i.context, o, textFieldTheme(), getThemeData(o), r);
+ return false;
+}
+
+bool RenderThemeWin::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ HANDLE theme;
+ int part;
+ if (haveTheme && isRunningOnVistaOrLater()) {
+ theme = menuListTheme();
+ if (documentIsInApplicationChromeMode(o->document()))
+ part = CP_READONLY;
+ else
+ part = CP_BORDER;
+ } else {
+ theme = textFieldTheme();
+ part = TFP_TEXTFIELD;
+ }
+
+ drawControl(i.context, o, theme, ThemeData(part, determineState(o)), r);
+
+ return paintMenuListButton(o, i, r);
+}
+
+void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ style->resetBorder();
+ adjustMenuListButtonStyle(selector, style, e);
+}
+
+void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // These are the paddings needed to place the text correctly in the <select> box
+ const int dropDownBoxPaddingTop = 2;
+ const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4;
+ const int dropDownBoxPaddingBottom = 2;
+ const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth;
+ // The <select> box must be at least 12px high for the button to render nicely on Windows
+ const int dropDownBoxMinHeight = 12;
+
+ // Position the text correctly within the select box and make the box wide enough to fit the dropdown button
+ style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed));
+ style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed));
+ style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed));
+ style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed));
+
+ // Height is locked to auto
+ style->setHeight(Length(Auto));
+
+ // Calculate our min-height
+ int minHeight = style->font().height();
+ minHeight = max(minHeight, dropDownBoxMinHeight);
+
+ style->setMinHeight(Length(minHeight, Fixed));
+
+ // White-space is locked to pre
+ style->setWhiteSpace(PRE);
+}
+
+bool RenderThemeWin::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border.
+ int borderThickness = haveTheme ? 1 : 2;
+
+ // Paint the dropdown button on the inner edge of the text field,
+ // leaving space for the text field's 1px border
+ IntRect buttonRect(r);
+ buttonRect.inflate(-borderThickness);
+ if (o->style()->direction() == LTR)
+ buttonRect.setX(buttonRect.right() - dropDownButtonWidth);
+ buttonRect.setWidth(dropDownButtonWidth);
+
+ if (isRunningOnVistaOrLater()) {
+ // Outset the top, right, and bottom borders of the button so that they coincide with the <select>'s border.
+ buttonRect.setY(buttonRect.y() - vistaMenuListButtonOutset);
+ buttonRect.setHeight(buttonRect.height() + 2 * vistaMenuListButtonOutset);
+ buttonRect.setWidth(buttonRect.width() + vistaMenuListButtonOutset);
+ }
+
+ drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect);
+
+ return false;
+}
+
+const int trackWidth = 4;
+
+bool RenderThemeWin::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ IntRect bounds = r;
+
+ if (o->style()->appearance() == SliderHorizontalPart) {
+ bounds.setHeight(trackWidth);
+ bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
+ } else if (o->style()->appearance() == SliderVerticalPart) {
+ bounds.setWidth(trackWidth);
+ bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
+ }
+
+ drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds);
+ return false;
+}
+
+bool RenderThemeWin::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ drawControl(i.context, o, sliderTheme(), getThemeData(o), r);
+ return false;
+}
+
+const int sliderThumbWidth = 7;
+const int sliderThumbHeight = 15;
+
+void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const
+{
+ ControlPart part = o->style()->appearance();
+ if (part == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(sliderThumbHeight, Fixed));
+ o->style()->setHeight(Length(sliderThumbWidth, Fixed));
+ } else if (part == SliderThumbHorizontalPart) {
+ o->style()->setWidth(Length(sliderThumbWidth, Fixed));
+ o->style()->setHeight(Length(sliderThumbHeight, Fixed));
+ }
+#if ENABLE(VIDEO)
+ else if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart)
+ RenderMediaControls::adjustMediaSliderThumbSize(o);
+#endif
+}
+
+bool RenderThemeWin::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintTextField(o, i, r);
+}
+
+void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Override paddingSize to match AppKit text positioning.
+ const int padding = 1;
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(padding, Fixed));
+ style->setPaddingBottom(Length(padding, Fixed));
+ if (e && e->focused() && e->document()->frame()->selection()->isFocusedAndActive())
+ style->setOutlineOffset(-2);
+}
+
+bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = r;
+ ASSERT(o->parent());
+ if (!o->parent() || !o->parent()->isBox())
+ return false;
+
+ RenderBox* parentRenderBox = toRenderBox(o->parent());
+
+ IntRect parentBox = parentRenderBox->absoluteContentBox();
+
+ // Make sure the scaled button stays square and will fit in its parent's box
+ bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
+ bounds.setWidth(bounds.height());
+
+ // 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.
+ bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+
+ static Image* cancelImage = Image::loadPlatformResource("searchCancel").releaseRef();
+ static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").releaseRef();
+ paintInfo.context->drawImage(isPressed(o) ? cancelPressedImage : cancelImage, o->style()->colorSpace(), bounds);
+ return false;
+}
+
+void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Scale the button size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int cancelButtonSize = lroundf(min(max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
+ style->setWidth(Length(cancelButtonSize, Fixed));
+ style->setHeight(Length(cancelButtonSize, Fixed));
+}
+
+void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize emptySize(1, 11);
+ style->setWidth(Length(emptySize.width(), Fixed));
+ style->setHeight(Length(emptySize.height(), Fixed));
+}
+
+void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Scale the decoration size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int magnifierSize = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
+ maxSearchFieldResultsDecorationSize));
+ style->setWidth(Length(magnifierSize, Fixed));
+ style->setHeight(Length(magnifierSize, Fixed));
+}
+
+bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = r;
+ ASSERT(o->parent());
+ if (!o->parent() || !o->parent()->isBox())
+ return false;
+
+ RenderBox* parentRenderBox = toRenderBox(o->parent());
+ IntRect parentBox = parentRenderBox->absoluteContentBox();
+
+ // Make sure the scaled decoration stays square and will fit in its parent's box
+ bounds.setHeight(min(parentBox.width(), min(parentBox.height(), bounds.height())));
+ bounds.setWidth(bounds.height());
+
+ // Center the decoration 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.
+ bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+
+ static Image* magnifierImage = Image::loadPlatformResource("searchMagnifier").releaseRef();
+ paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
+ return false;
+}
+
+void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Scale the button size based on the font size
+ float fontScale = style->fontSize() / defaultControlFontPixelSize;
+ int magnifierHeight = lroundf(min(max(minSearchFieldResultsDecorationSize, defaultSearchFieldResultsDecorationSize * fontScale),
+ maxSearchFieldResultsDecorationSize));
+ int magnifierWidth = lroundf(magnifierHeight * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize);
+ style->setWidth(Length(magnifierWidth, Fixed));
+ style->setHeight(Length(magnifierHeight, Fixed));
+}
+
+bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ IntRect bounds = r;
+ ASSERT(o->parent());
+ if (!o->parent())
+ return false;
+ if (!o->parent() || !o->parent()->isBox())
+ return false;
+
+ RenderBox* parentRenderBox = toRenderBox(o->parent());
+ IntRect parentBox = parentRenderBox->absoluteContentBox();
+
+ // Make sure the scaled decoration will fit in its parent's box
+ bounds.setHeight(min(parentBox.height(), bounds.height()));
+ bounds.setWidth(min(parentBox.width(), static_cast<int>(bounds.height() * defaultSearchFieldResultsButtonWidth / defaultSearchFieldResultsDecorationSize)));
+
+ // 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.
+ bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
+
+ static Image* magnifierImage = Image::loadPlatformResource("searchMagnifierResults").releaseRef();
+ paintInfo.context->drawImage(magnifierImage, o->style()->colorSpace(), bounds);
+ return false;
+}
+
+// Map a CSSValue* system color to an index understood by GetSysColor
+static int cssValueIdToSysColorIndex(int cssValueId)
+{
+ switch (cssValueId) {
+ case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
+ case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
+ case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
+ case CSSValueBackground: return COLOR_BACKGROUND;
+ case CSSValueButtonface: return COLOR_BTNFACE;
+ case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
+ case CSSValueButtonshadow: return COLOR_BTNSHADOW;
+ case CSSValueButtontext: return COLOR_BTNTEXT;
+ case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
+ case CSSValueGraytext: return COLOR_GRAYTEXT;
+ case CSSValueHighlight: return COLOR_HIGHLIGHT;
+ case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
+ case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
+ case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
+ case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
+ case CSSValueInfobackground: return COLOR_INFOBK;
+ case CSSValueInfotext: return COLOR_INFOTEXT;
+ case CSSValueMenu: return COLOR_MENU;
+ case CSSValueMenutext: return COLOR_MENUTEXT;
+ case CSSValueScrollbar: return COLOR_SCROLLBAR;
+ case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
+ case CSSValueThreedface: return COLOR_3DFACE;
+ case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
+ case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
+ case CSSValueThreedshadow: return COLOR_3DSHADOW;
+ case CSSValueWindow: return COLOR_WINDOW;
+ case CSSValueWindowframe: return COLOR_WINDOWFRAME;
+ case CSSValueWindowtext: return COLOR_WINDOWTEXT;
+ default: return -1; // Unsupported CSSValue
+ }
+}
+
+Color RenderThemeWin::systemColor(int cssValueId) const
+{
+ int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
+ if (sysColorIndex == -1)
+ return RenderTheme::systemColor(cssValueId);
+
+ COLORREF color = GetSysColor(sysColorIndex);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+#if ENABLE(VIDEO)
+
+String RenderThemeWin::extraMediaControlsStyleSheet()
+{
+ return String(mediaControlsQuickTimeUserAgentStyleSheet, sizeof(mediaControlsQuickTimeUserAgentStyleSheet));
+}
+
+bool RenderThemeWin::shouldRenderMediaControlPart(ControlPart part, Element* element)
+{
+ if (part == MediaToggleClosedCaptionsButtonPart) {
+ // We rely on QuickTime to render captions so only enable the button for a video element.
+#if SAFARI_THEME_VERSION >= 4
+ if (!element->hasTagName(videoTag))
+ return false;
+#else
+ return false;
+#endif
+ }
+
+ return RenderTheme::shouldRenderMediaControlPart(part, element);
+}
+
+
+bool RenderThemeWin::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaFullscreenButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaMuteButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaPlayButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaRewindButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaRewindButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSeekBackButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSeekForwardButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSlider, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaSliderThumb, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaToggleClosedCaptionsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaShowClosedCaptionsButton, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaControlsBackground(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaTimelineContainer, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaVolumeSliderContainer(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderContainer, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaVolumeSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaVolumeSlider, o, paintInfo, r);
+}
+
+bool RenderThemeWin::paintMediaVolumeSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return RenderMediaControls::paintMediaControlsPart(MediaVolumeSliderThumb, o, paintInfo, r);
+}
+
+IntPoint RenderThemeWin::volumeSliderOffsetFromMuteButton(Node* muteButton, const IntSize& size) const
+{
+ return RenderMediaControls::volumeSliderOffsetFromMuteButton(muteButton, size);
+}
+
+
+#endif
+
+}
diff --git a/Source/WebCore/rendering/RenderThemeWin.h b/Source/WebCore/rendering/RenderThemeWin.h
new file mode 100644
index 0000000..c05d2ee
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeWin.h
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006, 2008 Apple Computer, Inc.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ *
+ * 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 RenderThemeWin_h
+#define RenderThemeWin_h
+
+#include "RenderTheme.h"
+
+#if WIN32
+typedef void* HANDLE;
+typedef struct HINSTANCE__* HINSTANCE;
+typedef HINSTANCE HMODULE;
+#endif
+
+namespace WebCore {
+
+struct ThemeData {
+ ThemeData() :m_part(0), m_state(0), m_classicState(0) {}
+ ThemeData(int part, int state)
+ : m_part(part)
+ , m_state(state)
+ , m_classicState(0)
+ { }
+
+ unsigned m_part;
+ unsigned m_state;
+ unsigned m_classicState;
+};
+
+class RenderThemeWin : public RenderTheme {
+public:
+ static PassRefPtr<RenderTheme> create();
+
+ virtual String extraDefaultStyleSheet();
+ virtual String extraQuirksStyleSheet();
+
+ // A method asking if the theme's controls actually care about redrawing when hovered.
+ virtual bool supportsHover(const RenderStyle*) const;
+
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+
+ // System fonts.
+ virtual void systemFont(int propId, FontDescription&) const;
+ virtual Color systemColor(int cssValueId) const;
+
+ virtual bool paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintButton(o, i, r); }
+ virtual void setCheckboxSize(RenderStyle*) const;
+
+ virtual bool paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintButton(o, i, r); }
+ virtual void setRadioSize(RenderStyle* style) const
+ { return setCheckboxSize(style); }
+
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintTextField(o, i, r); }
+
+ virtual void adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const;
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const;
+
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r);
+ virtual bool paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r);
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ virtual bool popupOptionSupportsTextIndent() const { return true; }
+
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&) { return false; }
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void themeChanged();
+
+ virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+ virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+ virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+
+ static void setWebKitIsBeingUnloaded();
+
+ virtual bool supportsFocusRing(const RenderStyle*) const;
+
+#if ENABLE(VIDEO)
+ virtual String extraMediaControlsStyleSheet();
+ virtual bool shouldRenderMediaControlPart(ControlPart, Element*);
+ virtual bool paintMediaControlsBackground(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaRewindButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaToggleClosedCaptionsButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual IntPoint volumeSliderOffsetFromMuteButton(Node*, const IntSize&) const;
+#endif
+
+private:
+ enum ControlSubPart {
+ None,
+ SpinButtonDown,
+ SpinButtonUp,
+ };
+
+ RenderThemeWin();
+ ~RenderThemeWin();
+
+ void addIntrinsicMargins(RenderStyle*) const;
+ void close();
+
+ unsigned determineState(RenderObject*);
+ unsigned determineClassicState(RenderObject*, ControlSubPart = None);
+ unsigned determineSliderThumbState(RenderObject*);
+ unsigned determineButtonState(RenderObject*);
+ unsigned determineSpinButtonState(RenderObject*, ControlSubPart = None);
+
+ bool supportsFocus(ControlPart) const;
+
+ ThemeData getThemeData(RenderObject*, ControlSubPart = None);
+ ThemeData getClassicThemeData(RenderObject* o, ControlSubPart = None);
+
+ HANDLE buttonTheme() const;
+ HANDLE textFieldTheme() const;
+ HANDLE menuListTheme() const;
+ HANDLE sliderTheme() const;
+ HANDLE spinButtonTheme() const;
+
+ mutable HANDLE m_buttonTheme;
+ mutable HANDLE m_textFieldTheme;
+ mutable HANDLE m_menuListTheme;
+ mutable HANDLE m_sliderTheme;
+ mutable HANDLE m_spinButtonTheme;
+};
+
+};
+
+#endif
diff --git a/Source/WebCore/rendering/RenderThemeWinCE.cpp b/Source/WebCore/rendering/RenderThemeWinCE.cpp
new file mode 100644
index 0000000..27b8783
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeWinCE.cpp
@@ -0,0 +1,647 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006, 2007 Apple Computer, Inc.
+ * Copyright (C) 2007-2009 Torch Mobile, 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 "RenderThemeWinCE.h"
+
+#include "CSSStyleSheet.h"
+#include "CSSValueKeywords.h"
+#include "Document.h"
+#include "GraphicsContext.h"
+#include "NotImplemented.h"
+#if ENABLE(VIDEO)
+#include "HTMLMediaElement.h"
+#endif
+
+#include <windows.h>
+
+/*
+ * The following constants are used to determine how a widget is drawn using
+ * Windows' Theme API. For more information on theme parts and states see
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+ */
+#define THEME_COLOR 204
+#define THEME_FONT 210
+
+// Generic state constants
+#define TS_NORMAL 1
+#define TS_HOVER 2
+#define TS_ACTIVE 3
+#define TS_DISABLED 4
+#define TS_FOCUSED 5
+
+// Button constants
+#define BP_BUTTON 1
+#define BP_RADIO 2
+#define BP_CHECKBOX 3
+
+// Textfield constants
+#define TFP_TEXTFIELD 1
+#define TFS_READONLY 6
+
+typedef HANDLE (WINAPI*openThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
+typedef HRESULT (WINAPI*closeThemeDataPtr)(HANDLE hTheme);
+typedef HRESULT (WINAPI*drawThemeBackgroundPtr)(HANDLE hTheme, HDC hdc, int iPartId,
+ int iStateId, const RECT *pRect,
+ const RECT* pClipRect);
+typedef HRESULT (WINAPI*drawThemeEdgePtr)(HANDLE hTheme, HDC hdc, int iPartId,
+ int iStateId, const RECT *pRect,
+ unsigned uEdge, unsigned uFlags,
+ const RECT* pClipRect);
+typedef HRESULT (WINAPI*getThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
+ int iStateId, const RECT* pRect,
+ RECT* pContentRect);
+typedef HRESULT (WINAPI*getThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
+ int iStateId, RECT* prc, int ts,
+ SIZE* psz);
+typedef HRESULT (WINAPI*getThemeSysFontPtr)(HANDLE hTheme, int iFontId, OUT LOGFONT* pFont);
+typedef HRESULT (WINAPI*getThemeColorPtr)(HANDLE hTheme, HDC hdc, int iPartId,
+ int iStateId, int iPropId, OUT COLORREF* pFont);
+
+namespace WebCore {
+
+static const int dropDownButtonWidth = 17;
+static const int trackWidth = 4;
+
+PassRefPtr<RenderTheme> RenderThemeWinCE::create()
+{
+ return adoptRef(new RenderThemeWinCE);
+}
+
+PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
+{
+ static RenderTheme* winceTheme = RenderThemeWinCE::create().releaseRef();
+ return winceTheme;
+}
+
+RenderThemeWinCE::RenderThemeWinCE()
+{
+}
+
+RenderThemeWinCE::~RenderThemeWinCE()
+{
+}
+
+Color RenderThemeWinCE::platformActiveSelectionBackgroundColor() const
+{
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
+}
+
+Color RenderThemeWinCE::platformInactiveSelectionBackgroundColor() const
+{
+ COLORREF color = GetSysColor(COLOR_GRAYTEXT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
+}
+
+Color RenderThemeWinCE::platformActiveSelectionForegroundColor() const
+{
+ COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255);
+}
+
+Color RenderThemeWinCE::platformInactiveSelectionForegroundColor() const
+{
+ return Color::white;
+}
+
+bool RenderThemeWinCE::supportsFocus(ControlPart appearance) const
+{
+ switch (appearance) {
+ case PushButtonPart:
+ case ButtonPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ return true;
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+bool RenderThemeWinCE::supportsFocusRing(const RenderStyle *style) const
+{
+ return supportsFocus(style->appearance());
+}
+
+unsigned RenderThemeWinCE::determineClassicState(RenderObject* o)
+{
+ unsigned result = 0;
+ if (!isEnabled(o) || isReadOnlyControl(o))
+ result = DFCS_INACTIVE;
+ else if (isPressed(o)) // Active supersedes hover
+ result = DFCS_PUSHED;
+
+ if (isChecked(o))
+ result |= DFCS_CHECKED;
+ return result;
+}
+
+ThemeData RenderThemeWinCE::getThemeData(RenderObject* o)
+{
+ ThemeData result;
+ switch (o->style()->appearance()) {
+ case PushButtonPart:
+ case ButtonPart:
+ result.m_part = BP_BUTTON;
+ result.m_classicState = DFCS_BUTTONPUSH;
+ break;
+ case CheckboxPart:
+ result.m_part = BP_CHECKBOX;
+ result.m_classicState = DFCS_BUTTONCHECK;
+ break;
+ case RadioPart:
+ result.m_part = BP_RADIO;
+ result.m_classicState = DFCS_BUTTONRADIO;
+ break;
+ case ListboxPart:
+ case MenulistPart:
+ case TextFieldPart:
+ case TextAreaPart:
+ result.m_part = TFP_TEXTFIELD;
+ break;
+ }
+
+ result.m_classicState |= determineClassicState(o);
+
+ return result;
+}
+
+bool RenderThemeWinCE::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ // Get the correct theme data for a button
+ ThemeData themeData = getThemeData(o);
+
+ // Now paint the button.
+ i.context->drawFrameControl(r, DFC_BUTTON, themeData.m_classicState);
+ if (isFocused(o)) {
+ if (themeData.m_part == BP_BUTTON) {
+ IntRect focusRect(r);
+ focusRect.inflate(-2);
+ i.context->drawFocusRect(focusRect);
+ } else
+ i.context->drawFocusRect(r);
+ }
+
+ return false;
+}
+
+void RenderThemeWinCE::setCheckboxSize(RenderStyle* style) const
+{
+ // If the width and height are both specified, then we have nothing to do.
+ if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
+ return;
+
+ // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox.
+ // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
+ // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
+ // metrics.
+ if (style->width().isIntrinsicOrAuto())
+ style->setWidth(Length(13, Fixed));
+ if (style->height().isAuto())
+ style->setHeight(Length(13, Fixed));
+}
+
+bool RenderThemeWinCE::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ // Get the correct theme data for a textfield
+ ThemeData themeData = getThemeData(o);
+
+ // Now paint the text field.
+ i.context->paintTextField(r, themeData.m_classicState);
+
+ return false;
+}
+
+void RenderThemeWinCE::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ style->resetBorder();
+ adjustMenuListButtonStyle(selector, style, e);
+}
+
+bool RenderThemeWinCE::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ paintTextField(o, i, r);
+ paintMenuListButton(o, i, r);
+ return true;
+}
+
+bool RenderThemeWinCE::paintMenuListButton(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ IntRect buttonRect(r.right() - dropDownButtonWidth - 1, r.y(), dropDownButtonWidth, r.height());
+ buttonRect.inflateY(-1);
+ i.context->drawFrameControl(buttonRect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | determineClassicState(o));
+ return true;
+}
+
+void RenderThemeWinCE::systemFont(int propId, FontDescription& fontDescription) const
+{
+ notImplemented();
+}
+
+void RenderThemeWinCE::themeChanged()
+{
+}
+
+String RenderThemeWinCE::extraDefaultStyleSheet()
+{
+ notImplemented();
+ return String();
+}
+
+String RenderThemeWinCE::extraQuirksStyleSheet()
+{
+ notImplemented();
+ return String();
+}
+
+bool RenderThemeWinCE::supportsHover(const RenderStyle*) const
+{
+ return false;
+}
+
+// Map a CSSValue* system color to an index understood by GetSysColor
+static int cssValueIdToSysColorIndex(int cssValueId)
+{
+ switch (cssValueId) {
+ case CSSValueActiveborder: return COLOR_ACTIVEBORDER;
+ case CSSValueActivecaption: return COLOR_ACTIVECAPTION;
+ case CSSValueAppworkspace: return COLOR_APPWORKSPACE;
+ case CSSValueBackground: return COLOR_BACKGROUND;
+ case CSSValueButtonface: return COLOR_BTNFACE;
+ case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT;
+ case CSSValueButtonshadow: return COLOR_BTNSHADOW;
+ case CSSValueButtontext: return COLOR_BTNTEXT;
+ case CSSValueCaptiontext: return COLOR_CAPTIONTEXT;
+ case CSSValueGraytext: return COLOR_GRAYTEXT;
+ case CSSValueHighlight: return COLOR_HIGHLIGHT;
+ case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT;
+ case CSSValueInactiveborder: return COLOR_INACTIVEBORDER;
+ case CSSValueInactivecaption: return COLOR_INACTIVECAPTION;
+ case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT;
+ case CSSValueInfobackground: return COLOR_INFOBK;
+ case CSSValueInfotext: return COLOR_INFOTEXT;
+ case CSSValueMenu: return COLOR_MENU;
+ case CSSValueMenutext: return COLOR_MENUTEXT;
+ case CSSValueScrollbar: return COLOR_SCROLLBAR;
+ case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW;
+ case CSSValueThreedface: return COLOR_3DFACE;
+ case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT;
+ case CSSValueThreedlightshadow: return COLOR_3DLIGHT;
+ case CSSValueThreedshadow: return COLOR_3DSHADOW;
+ case CSSValueWindow: return COLOR_WINDOW;
+ case CSSValueWindowframe: return COLOR_WINDOWFRAME;
+ case CSSValueWindowtext: return COLOR_WINDOWTEXT;
+ default: return -1; // Unsupported CSSValue
+ }
+}
+
+Color RenderThemeWinCE::systemColor(int cssValueId) const
+{
+ int sysColorIndex = cssValueIdToSysColorIndex(cssValueId);
+ if (sysColorIndex == -1)
+ return RenderTheme::systemColor(cssValueId);
+
+ COLORREF color = GetSysColor(sysColorIndex);
+ return Color(GetRValue(color), GetGValue(color), GetBValue(color));
+}
+
+const int sliderThumbWidth = 7;
+const int sliderThumbHeight = 15;
+
+void RenderThemeWinCE::adjustSliderThumbSize(RenderObject* o) const
+{
+ if (o->style()->appearance() == SliderThumbVerticalPart) {
+ o->style()->setWidth(Length(sliderThumbHeight, Fixed));
+ o->style()->setHeight(Length(sliderThumbWidth, Fixed));
+ } else if (o->style()->appearance() == SliderThumbHorizontalPart) {
+ o->style()->setWidth(Length(sliderThumbWidth, Fixed));
+ o->style()->setHeight(Length(sliderThumbHeight, Fixed));
+ }
+}
+
+#if 0
+void RenderThemeWinCE::adjustButtonInnerStyle(RenderStyle* style) const
+{
+ // This inner padding matches Firefox.
+ style->setPaddingTop(Length(1, Fixed));
+ style->setPaddingRight(Length(3, Fixed));
+ style->setPaddingBottom(Length(1, Fixed));
+ style->setPaddingLeft(Length(3, Fixed));
+}
+
+void RenderThemeWinCE::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // Override padding size to match AppKit text positioning.
+ const int padding = 1;
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(padding, Fixed));
+ style->setPaddingBottom(Length(padding, Fixed));
+}
+#endif
+
+bool RenderThemeWinCE::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ return paintTextField(o, i, r);
+}
+
+bool RenderThemeWinCE::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ Color buttonColor = (o->node() && o->node()->active()) ? Color(138, 138, 138) : Color(186, 186, 186);
+
+ IntSize cancelSize(10, 10);
+ IntSize cancelRadius(cancelSize.width() / 2, cancelSize.height() / 2);
+ int x = r.x() + (r.width() - cancelSize.width()) / 2;
+ int y = r.y() + (r.height() - cancelSize.height()) / 2 + 1;
+ IntRect cancelBounds(IntPoint(x, y), cancelSize);
+ paintInfo.context->save();
+ paintInfo.context->addRoundedRectClip(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius);
+ paintInfo.context->fillRect(cancelBounds, buttonColor, ColorSpaceDeviceRGB);
+
+ // Draw the 'x'
+ IntSize xSize(3, 3);
+ IntRect xBounds(cancelBounds.location() + IntSize(3, 3), xSize);
+ paintInfo.context->setStrokeColor(Color::white, ColorSpaceDeviceRGB);
+ paintInfo.context->drawLine(xBounds.location(), xBounds.location() + xBounds.size());
+ paintInfo.context->drawLine(IntPoint(xBounds.right(), xBounds.y()), IntPoint(xBounds.x(), xBounds.bottom()));
+
+ paintInfo.context->restore();
+ return false;
+}
+
+void RenderThemeWinCE::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize cancelSize(13, 11);
+ style->setWidth(Length(cancelSize.width(), Fixed));
+ style->setHeight(Length(cancelSize.height(), Fixed));
+}
+
+void RenderThemeWinCE::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize emptySize(1, 11);
+ style->setWidth(Length(emptySize.width(), Fixed));
+ style->setHeight(Length(emptySize.height(), Fixed));
+}
+
+void RenderThemeWinCE::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize magnifierSize(15, 11);
+ style->setWidth(Length(magnifierSize.width(), Fixed));
+ style->setHeight(Length(magnifierSize.height(), Fixed));
+}
+
+bool RenderThemeWinCE::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ notImplemented();
+ return false;
+}
+
+void RenderThemeWinCE::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ IntSize magnifierSize(15, 11);
+ style->setWidth(Length(magnifierSize.width(), Fixed));
+ style->setHeight(Length(magnifierSize.height(), Fixed));
+}
+
+bool RenderThemeWinCE::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ paintSearchFieldResultsDecoration(o, paintInfo, r);
+ return false;
+}
+
+void RenderThemeWinCE::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ // These are the paddings needed to place the text correctly in the <select> box
+ const int dropDownBoxPaddingTop = 2;
+ const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4;
+ const int dropDownBoxPaddingBottom = 2;
+ const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth;
+ // The <select> box must be at least 12px high for the button to render nicely on Windows
+ const int dropDownBoxMinHeight = 12;
+
+ // Position the text correctly within the select box and make the box wide enough to fit the dropdown button
+ style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed));
+ style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed));
+ style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed));
+ style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed));
+
+ // Height is locked to auto
+ style->setHeight(Length(Auto));
+
+ // Calculate our min-height
+ int minHeight = style->font().height();
+ minHeight = max(minHeight, dropDownBoxMinHeight);
+
+ style->setMinHeight(Length(minHeight, Fixed));
+
+ // White-space is locked to pre
+ style->setWhiteSpace(PRE);
+
+ DWORD colorMenu = GetSysColor(COLOR_MENU);
+ DWORD colorMenuText = GetSysColor(COLOR_MENUTEXT);
+ Color bgColor(GetRValue(colorMenu), GetGValue(colorMenu), GetBValue(colorMenu), 255);
+ Color textColor(GetRValue(colorMenuText), GetGValue(colorMenuText), GetBValue(colorMenuText), 255);
+ if (bgColor == textColor)
+ textColor.setRGB((~bgColor.rgb()) | 0xFF000000);
+ style->clearBackgroundLayers();
+ style->accessBackgroundLayers()->setClip(ContentFillBox);
+ style->setBackgroundColor(bgColor);
+ style->setColor(textColor);
+}
+
+#if ENABLE(VIDEO)
+// Attempt to retrieve a HTMLMediaElement from a Node. Returns 0 if one cannot be found.
+static HTMLMediaElement* mediaElementParent(Node* node)
+{
+ if (!node)
+ return 0;
+ Node* mediaNode = node->shadowAncestorNode();
+ if (!mediaNode || (!mediaNode->hasTagName(HTMLNames::videoTag) && !mediaNode->hasTagName(HTMLNames::audioTag)))
+ return 0;
+
+ return static_cast<HTMLMediaElement*>(mediaNode);
+}
+#endif
+
+bool RenderThemeWinCE::paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ bool rc = RenderTheme::paintSliderTrack(o, i, r);
+ IntPoint left = IntPoint(r.x() + 2, (r.y() + r.bottom()) / 2);
+ i.context->save();
+ i.context->setStrokeColor(Color::gray, ColorSpaceDeviceRGB);
+ i.context->setFillColor(Color::gray, ColorSpaceDeviceRGB);
+ i.context->fillRect(r);
+#if ENABLE(VIDEO)
+ HTMLMediaElement* mediaElement = mediaElementParent(o->node());
+ if (mediaElement) {
+ i.context->setStrokeColor(Color(0, 0xff, 0));
+ IntPoint right = IntPoint(left.x() + mediaElement->percentLoaded() * (r.right() - r.x() - 4), (r.y() + r.bottom()) / 2);
+ i.context->drawLine(left, right);
+ left = right;
+ }
+#endif
+ i.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
+ i.context->drawLine(left, IntPoint(r.right() - 2, left.y()));
+ i.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r)
+{
+ bool rc = RenderTheme::paintSliderThumb(o, i, r);
+ i.context->save();
+ i.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
+ i.context->setFillColor(Color::black, ColorSpaceDeviceRGB);
+#if ENABLE(VIDEO)
+ HTMLMediaElement* mediaElement = mediaElementParent(o->node());
+ if (mediaElement) {
+ float pt = (mediaElement->currentTime() - mediaElement->startTime()) / mediaElement->duration();
+ FloatRect intRect = r;
+ intRect.setX(intRect.x() + intRect.width() * pt - 2);
+ intRect.setWidth(5);
+ i.context->fillRect(intRect);
+ }
+#endif
+ i.context->restore();
+ return rc;
+}
+
+void RenderThemeWinCE::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
+{
+ const int padding = 1;
+ style->setPaddingLeft(Length(padding, Fixed));
+ style->setPaddingRight(Length(padding, Fixed));
+ style->setPaddingTop(Length(padding, Fixed));
+ style->setPaddingBottom(Length(padding, Fixed));
+}
+
+#if ENABLE(VIDEO)
+
+bool RenderThemeWinCE::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ bool rc = paintButton(o, paintInfo, r);
+ FloatRect imRect = r;
+ imRect.inflate(-2);
+ paintInfo.context->save();
+ paintInfo.context->setStrokeColor(Color::black);
+ paintInfo.context->setFillColor(Color::gray);
+ paintInfo.context->fillRect(imRect);
+ paintInfo.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ bool rc = paintButton(o, paintInfo, r);
+ HTMLMediaElement* mediaElement = mediaElementParent(o->node());
+ bool muted = !mediaElement || mediaElement->muted();
+ FloatRect imRect = r;
+ imRect.inflate(-2);
+ paintInfo.context->save();
+ paintInfo.context->setStrokeColor(Color::black);
+ paintInfo.context->setFillColor(Color::black);
+ FloatPoint pts[6] = {
+ FloatPoint(imRect.x() + 1, imRect.y() + imRect.height() / 3.0),
+ FloatPoint(imRect.x() + 1 + imRect.width() / 2.0, imRect.y() + imRect.height() / 3.0),
+ FloatPoint(imRect.right() - 1, imRect.y()),
+ FloatPoint(imRect.right() - 1, imRect.bottom()),
+ FloatPoint(imRect.x() + 1 + imRect.width() / 2.0, imRect.y() + 2.0 * imRect.height() / 3.0),
+ FloatPoint(imRect.x() + 1, imRect.y() + 2.0 * imRect.height() / 3.0)
+ };
+ paintInfo.context->drawConvexPolygon(6, pts);
+ if (muted)
+ paintInfo.context->drawLine(IntPoint(imRect.right(), imRect.y()), IntPoint(imRect.x(), imRect.bottom()));
+ paintInfo.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ bool rc = paintButton(o, paintInfo, r);
+ FloatRect imRect = r;
+ imRect.inflate(-3);
+ paintInfo.context->save();
+ paintInfo.context->setStrokeColor(Color::black);
+ paintInfo.context->setFillColor(Color::black);
+ HTMLMediaElement* mediaElement = mediaElementParent(o->node());
+ bool paused = !mediaElement || mediaElement->paused();
+ if (paused) {
+ float width = imRect.width();
+ imRect.setWidth(width / 3.0);
+ paintInfo.context->fillRect(imRect);
+ imRect.move(2.0 * width / 3.0, 0);
+ paintInfo.context->fillRect(imRect);
+ } else {
+ FloatPoint pts[3] = { FloatPoint(imRect.x(), imRect.y()), FloatPoint(imRect.right(), (imRect.y() + imRect.bottom()) / 2.0), FloatPoint(imRect.x(), imRect.bottom()) };
+ paintInfo.context->drawConvexPolygon(3, pts);
+ }
+ paintInfo.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ bool rc = paintButton(o, paintInfo, r);
+ FloatRect imRect = r;
+ imRect.inflate(-3);
+ FloatPoint pts[3] = { FloatPoint((imRect.x() + imRect.right()) / 2.0, imRect.y()), FloatPoint(imRect.x(), (imRect.y() + imRect.bottom()) / 2.0), FloatPoint((imRect.x() + imRect.right()) / 2.0, imRect.bottom()) };
+ FloatPoint pts2[3] = { FloatPoint(imRect.right(), imRect.y()), FloatPoint((imRect.x() + imRect.right()) / 2.0, (imRect.y() + imRect.bottom()) / 2.0), FloatPoint(imRect.right(), imRect.bottom()) };
+ paintInfo.context->save();
+ paintInfo.context->setStrokeColor(Color::black);
+ paintInfo.context->setFillColor(Color::black);
+ paintInfo.context->drawConvexPolygon(3, pts);
+ paintInfo.context->drawConvexPolygon(3, pts2);
+ paintInfo.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ bool rc = paintButton(o, paintInfo, r);
+ FloatRect imRect = r;
+ imRect.inflate(-3);
+ FloatPoint pts[3] = { FloatPoint(imRect.x(), imRect.y()), FloatPoint((imRect.x() + imRect.right()) / 2.0, (imRect.y() + imRect.bottom()) / 2.0), FloatPoint(imRect.x(), imRect.bottom()) };
+ FloatPoint pts2[3] = { FloatPoint((imRect.x() + imRect.right()) / 2.0, imRect.y()), FloatPoint(imRect.right(), (imRect.y() + imRect.bottom()) / 2.0), FloatPoint((imRect.x() + imRect.right()) / 2.0, imRect.bottom()) };
+ paintInfo.context->save();
+ paintInfo.context->setStrokeColor(Color::black);
+ paintInfo.context->setFillColor(Color::black);
+ paintInfo.context->drawConvexPolygon(3, pts);
+ paintInfo.context->drawConvexPolygon(3, pts2);
+ paintInfo.context->restore();
+ return rc;
+}
+
+bool RenderThemeWinCE::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return paintSliderTrack(o, paintInfo, r);
+}
+
+bool RenderThemeWinCE::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
+{
+ return paintSliderThumb(o, paintInfo, r);
+}
+#endif
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderThemeWinCE.h b/Source/WebCore/rendering/RenderThemeWinCE.h
new file mode 100644
index 0000000..14a5b12
--- /dev/null
+++ b/Source/WebCore/rendering/RenderThemeWinCE.h
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006, 2008 Apple Computer, Inc.
+ * Copyright (C) 2009 Torch Mobile, 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 RenderThemeWinCE_h
+#define RenderThemeWinCE_h
+
+#include "RenderTheme.h"
+
+typedef void* HANDLE;
+typedef struct HINSTANCE__* HINSTANCE;
+typedef HINSTANCE HMODULE;
+
+namespace WebCore {
+
+ struct ThemeData {
+ ThemeData() :m_part(0), m_state(0), m_classicState(0) {}
+ ThemeData(int part, int state)
+ : m_part(part)
+ , m_state(state)
+ , m_classicState(0)
+ { }
+
+ unsigned m_part;
+ unsigned m_state;
+ unsigned m_classicState;
+ };
+
+ class RenderThemeWinCE : public RenderTheme {
+ public:
+ static PassRefPtr<RenderTheme> create();
+ ~RenderThemeWinCE();
+
+ virtual String extraDefaultStyleSheet();
+ virtual String extraQuirksStyleSheet();
+
+ // A method asking if the theme's controls actually care about redrawing when hovered.
+ virtual bool supportsHover(const RenderStyle*) const;
+
+ virtual Color platformActiveSelectionBackgroundColor() const;
+ virtual Color platformInactiveSelectionBackgroundColor() const;
+ virtual Color platformActiveSelectionForegroundColor() const;
+ virtual Color platformInactiveSelectionForegroundColor() const;
+
+ // System fonts.
+ virtual void systemFont(int propId, FontDescription&) const;
+ virtual Color systemColor(int cssValueId) const;
+
+ virtual bool paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintButton(o, i, r); }
+ virtual void setCheckboxSize(RenderStyle*) const;
+
+ virtual bool paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintButton(o, i, r); }
+ virtual void setRadioSize(RenderStyle* style) const
+ { return setCheckboxSize(style); }
+
+ virtual bool paintButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintTextField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
+ { return paintTextField(o, i, r); }
+
+ virtual void adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const;
+ virtual bool paintMenuList(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual void adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const;
+
+ virtual bool paintMenuListButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual bool paintSliderTrack(RenderObject* o, const PaintInfo& i, const IntRect& r);
+ virtual bool paintSliderThumb(RenderObject* o, const PaintInfo& i, const IntRect& r);
+ virtual void adjustSliderThumbSize(RenderObject*) const;
+
+ virtual bool popupOptionSupportsTextIndent() const { return true; }
+
+ virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchField(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldCancelButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldDecoration(RenderObject*, const PaintInfo&, const IntRect&) { return false; }
+
+ virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsDecoration(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
+ virtual bool paintSearchFieldResultsButton(RenderObject*, const PaintInfo&, const IntRect&);
+
+ virtual void themeChanged();
+
+ virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+ virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+ virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {}
+
+ static void setWebKitIsBeingUnloaded();
+
+ virtual bool supportsFocusRing(const RenderStyle*) const;
+
+ #if ENABLE(VIDEO)
+ virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&);
+ virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&);
+ #endif
+
+ private:
+ RenderThemeWinCE();
+
+ unsigned determineClassicState(RenderObject*);
+ bool supportsFocus(ControlPart) const;
+
+ ThemeData getThemeData(RenderObject*);
+ };
+
+};
+
+#endif // RenderThemeWinCE_h
diff --git a/Source/WebCore/rendering/RenderTreeAsText.cpp b/Source/WebCore/rendering/RenderTreeAsText.cpp
new file mode 100644
index 0000000..2e64999
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTreeAsText.cpp
@@ -0,0 +1,796 @@
+/*
+ * 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 "RenderTreeAsText.h"
+
+#include "CSSMutableStyleDeclaration.h"
+#include "CharacterNames.h"
+#include "Document.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "HTMLElement.h"
+#include "HTMLNames.h"
+#include "InlineTextBox.h"
+#include "PrintContext.h"
+#include "RenderBR.h"
+#include "RenderFileUploadControl.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderListItem.h"
+#include "RenderListMarker.h"
+#include "RenderPart.h"
+#include "RenderTableCell.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
+#include "SelectionController.h"
+#include <wtf/UnusedParam.h>
+#include <wtf/Vector.h>
+
+#if ENABLE(SVG)
+#include "RenderSVGContainer.h"
+#include "RenderSVGGradientStop.h"
+#include "RenderSVGImage.h"
+#include "RenderSVGInlineText.h"
+#include "RenderSVGPath.h"
+#include "RenderSVGRoot.h"
+#include "RenderSVGText.h"
+#include "SVGRenderTreeAsText.h"
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerBacking.h"
+#endif
+
+#if PLATFORM(QT)
+#include <QWidget>
+#endif
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal);
+
+bool hasFractions(double val)
+{
+ static const double s_epsilon = 0.0001;
+ int ival = static_cast<int>(val);
+ double dval = static_cast<double>(ival);
+ return fabs(val - dval) > s_epsilon;
+}
+
+TextStream& operator<<(TextStream& ts, const IntRect& r)
+{
+ return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
+}
+
+TextStream& operator<<(TextStream& ts, const IntPoint& p)
+{
+ return ts << "(" << p.x() << "," << p.y() << ")";
+}
+
+TextStream& operator<<(TextStream& ts, const FloatPoint& p)
+{
+ ts << "(";
+ if (hasFractions(p.x()))
+ ts << p.x();
+ else
+ ts << int(p.x());
+ ts << ",";
+ if (hasFractions(p.y()))
+ ts << p.y();
+ else
+ ts << int(p.y());
+ return ts << ")";
+}
+
+TextStream& operator<<(TextStream& ts, const FloatSize& s)
+{
+ ts << "width=";
+ if (hasFractions(s.width()))
+ ts << s.width();
+ else
+ ts << int(s.width());
+ ts << " height=";
+ if (hasFractions(s.height()))
+ ts << s.height();
+ else
+ ts << int(s.height());
+ return ts;
+}
+
+void writeIndent(TextStream& ts, int indent)
+{
+ for (int i = 0; i != indent; ++i)
+ ts << " ";
+}
+
+static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
+{
+ switch (borderStyle) {
+ case BNONE:
+ ts << "none";
+ break;
+ case BHIDDEN:
+ ts << "hidden";
+ break;
+ case INSET:
+ ts << "inset";
+ break;
+ case GROOVE:
+ ts << "groove";
+ break;
+ case RIDGE:
+ ts << "ridge";
+ break;
+ case OUTSET:
+ ts << "outset";
+ break;
+ case DOTTED:
+ ts << "dotted";
+ break;
+ case DASHED:
+ ts << "dashed";
+ break;
+ case SOLID:
+ ts << "solid";
+ break;
+ case DOUBLE:
+ ts << "double";
+ break;
+ }
+
+ ts << " ";
+}
+
+static String getTagName(Node* n)
+{
+ if (n->isDocumentNode())
+ return "";
+ if (n->isCommentNode())
+ return "COMMENT";
+ return n->nodeName();
+}
+
+static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
+{
+ if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
+ return false;
+
+ const HTMLElement* elem = static_cast<const HTMLElement*>(node);
+ if (elem->getAttribute(classAttr) != "Apple-style-span")
+ return false;
+
+ if (!node->hasChildNodes())
+ return true;
+
+ CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
+ return (!inlineStyleDecl || inlineStyleDecl->length() == 0);
+}
+
+String quoteAndEscapeNonPrintables(const String& s)
+{
+ Vector<UChar> result;
+ result.append('"');
+ for (unsigned i = 0; i != s.length(); ++i) {
+ UChar c = s[i];
+ if (c == '\\') {
+ result.append('\\');
+ result.append('\\');
+ } else if (c == '"') {
+ result.append('\\');
+ result.append('"');
+ } else if (c == '\n' || c == noBreakSpace)
+ result.append(' ');
+ else {
+ if (c >= 0x20 && c < 0x7F)
+ result.append(c);
+ else {
+ unsigned u = c;
+ String hex = String::format("\\x{%X}", u);
+ unsigned len = hex.length();
+ for (unsigned i = 0; i < len; ++i)
+ result.append(hex[i]);
+ }
+ }
+ }
+ result.append('"');
+ return String::adopt(result);
+}
+
+void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
+{
+ ts << o.renderName();
+
+ if (behavior & RenderAsTextShowAddresses)
+ ts << " " << static_cast<const void*>(&o);
+
+ if (o.style() && o.style()->zIndex())
+ ts << " zI: " << o.style()->zIndex();
+
+ if (o.node()) {
+ String tagName = getTagName(o.node());
+ if (!tagName.isEmpty()) {
+ ts << " {" << tagName << "}";
+ // flag empty or unstyled AppleStyleSpan because we never
+ // want to leave them in the DOM
+ if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
+ ts << " *empty or unstyled AppleStyleSpan*";
+ }
+ }
+
+ bool adjustForTableCells = o.containingBlock()->isTableCell();
+
+ IntRect r;
+ if (o.isText()) {
+ // FIXME: Would be better to dump the bounding box x and y rather than the first run's x and y, but that would involve updating
+ // many test results.
+ const RenderText& text = *toRenderText(&o);
+ IntRect linesBox = text.linesBoundingBox();
+ r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
+ if (adjustForTableCells && !text.firstTextBox())
+ adjustForTableCells = false;
+ } else if (o.isRenderInline()) {
+ // FIXME: Would be better not to just dump 0, 0 as the x and y here.
+ const RenderInline& inlineFlow = *toRenderInline(&o);
+ r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
+ adjustForTableCells = false;
+ } else if (o.isTableCell()) {
+ // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
+ // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
+ // captured by the results.
+ const RenderTableCell& cell = *toRenderTableCell(&o);
+ r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
+ } else if (o.isBox())
+ r = toRenderBox(&o)->frameRect();
+
+ // FIXME: Temporary in order to ensure compatibility with existing layout test results.
+ if (adjustForTableCells)
+ r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
+
+ ts << " " << r;
+
+ if (!(o.isText() && !o.isBR())) {
+ if (o.isFileUploadControl())
+ ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
+
+ if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
+ ts << " [color=" << o.style()->color().name() << "]";
+
+ if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
+ o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
+ // Do not dump invalid or transparent backgrounds, since that is the default.
+ ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
+
+ if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) &&
+ o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() &&
+ o.style()->textFillColor().rgb())
+ ts << " [textFillColor=" << o.style()->textFillColor().name() << "]";
+
+ if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) &&
+ o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() &&
+ o.style()->textStrokeColor().rgb())
+ ts << " [textStrokeColor=" << o.style()->textStrokeColor().name() << "]";
+
+ if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) &&
+ o.style()->textStrokeWidth() > 0)
+ ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
+
+ if (!o.isBoxModelObject())
+ return;
+
+ const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
+ if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
+ ts << " [border:";
+
+ BorderValue prevBorder;
+ if (o.style()->borderTop() != prevBorder) {
+ prevBorder = o.style()->borderTop();
+ if (!box.borderTop())
+ ts << " none";
+ else {
+ ts << " (" << box.borderTop() << "px ";
+ printBorderStyle(ts, o.style()->borderTopStyle());
+ Color col = o.style()->borderTopColor();
+ if (!col.isValid())
+ col = o.style()->color();
+ ts << col.name() << ")";
+ }
+ }
+
+ if (o.style()->borderRight() != prevBorder) {
+ prevBorder = o.style()->borderRight();
+ if (!box.borderRight())
+ ts << " none";
+ else {
+ ts << " (" << box.borderRight() << "px ";
+ printBorderStyle(ts, o.style()->borderRightStyle());
+ Color col = o.style()->borderRightColor();
+ if (!col.isValid())
+ col = o.style()->color();
+ ts << col.name() << ")";
+ }
+ }
+
+ if (o.style()->borderBottom() != prevBorder) {
+ prevBorder = box.style()->borderBottom();
+ if (!box.borderBottom())
+ ts << " none";
+ else {
+ ts << " (" << box.borderBottom() << "px ";
+ printBorderStyle(ts, o.style()->borderBottomStyle());
+ Color col = o.style()->borderBottomColor();
+ if (!col.isValid())
+ col = o.style()->color();
+ ts << col.name() << ")";
+ }
+ }
+
+ if (o.style()->borderLeft() != prevBorder) {
+ prevBorder = o.style()->borderLeft();
+ if (!box.borderLeft())
+ ts << " none";
+ else {
+ ts << " (" << box.borderLeft() << "px ";
+ printBorderStyle(ts, o.style()->borderLeftStyle());
+ Color col = o.style()->borderLeftColor();
+ if (!col.isValid())
+ col = o.style()->color();
+ ts << col.name() << ")";
+ }
+ }
+
+ ts << "]";
+ }
+ }
+
+ if (o.isTableCell()) {
+ const RenderTableCell& c = *toRenderTableCell(&o);
+ ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
+ }
+
+ if (o.isListMarker()) {
+ String text = toRenderListMarker(&o)->text();
+ if (!text.isEmpty()) {
+ if (text.length() != 1)
+ text = quoteAndEscapeNonPrintables(text);
+ else {
+ switch (text[0]) {
+ case bullet:
+ text = "bullet";
+ break;
+ case blackSquare:
+ text = "black square";
+ break;
+ case whiteBullet:
+ text = "white bullet";
+ break;
+ default:
+ text = quoteAndEscapeNonPrintables(text);
+ }
+ }
+ ts << ": " << text;
+ }
+ }
+
+ if (behavior & RenderAsTextShowIDAndClass) {
+ if (Node* node = o.node()) {
+ if (node->hasID())
+ ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
+
+ if (node->hasClass()) {
+ StyledElement* styledElement = static_cast<StyledElement*>(node);
+ String classes;
+ for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
+ if (i > 0)
+ classes += " ";
+ classes += styledElement->classNames()[i];
+ }
+ ts << " class=\"" + classes + "\"";
+ }
+ }
+ }
+
+ if (behavior & RenderAsTextShowLayoutState) {
+ bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
+ if (needsLayout)
+ ts << " (needs layout:";
+
+ bool havePrevious = false;
+ if (o.selfNeedsLayout()) {
+ ts << " self";
+ havePrevious = true;
+ }
+
+ if (o.needsPositionedMovementLayout()) {
+ if (havePrevious)
+ ts << ",";
+ havePrevious = true;
+ ts << " positioned movement";
+ }
+
+ if (o.normalChildNeedsLayout()) {
+ if (havePrevious)
+ ts << ",";
+ havePrevious = true;
+ ts << " child";
+ }
+
+ if (o.posChildNeedsLayout()) {
+ if (havePrevious)
+ ts << ",";
+ ts << " positioned child";
+ }
+
+ if (needsLayout)
+ ts << ")";
+ }
+
+#if PLATFORM(QT)
+ // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
+ // is invisible the QWidget should be invisible too.
+ if (o.isRenderPart()) {
+ const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
+ if (part->widget() && part->widget()->platformWidget()) {
+ QWidget* wid = part->widget()->platformWidget();
+
+ ts << " [QT: ";
+ ts << "geometry: {" << wid->geometry() << "} ";
+ ts << "isHidden: " << wid->isHidden() << " ";
+ ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
+ ts << "isParentVisible: " << part->widget()->isParentVisible() << " ";
+ ts << "mask: {" << wid->mask().boundingRect() << "} ] ";
+ }
+ }
+#endif
+}
+
+static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
+{
+ // FIXME: Table cell adjustment is temporary until results can be updated.
+ int y = run.m_y;
+ if (o.containingBlock()->isTableCell())
+ y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
+ ts << "text run at (" << run.m_x << "," << y << ") width " << run.m_logicalWidth;
+ if (!run.isLeftToRightDirection() || run.m_dirOverride) {
+ ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
+ if (run.m_dirOverride)
+ ts << " override";
+ }
+ ts << ": "
+ << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()))
+ << "\n";
+}
+
+void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
+{
+#if ENABLE(SVG)
+ if (o.isSVGPath()) {
+ write(ts, *toRenderSVGPath(&o), indent);
+ return;
+ }
+ if (o.isSVGGradientStop()) {
+ writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
+ return;
+ }
+ if (o.isSVGResourceContainer()) {
+ writeSVGResourceContainer(ts, o, indent);
+ return;
+ }
+ if (o.isSVGContainer()) {
+ writeSVGContainer(ts, o, indent);
+ return;
+ }
+ if (o.isSVGRoot()) {
+ write(ts, *toRenderSVGRoot(&o), indent);
+ return;
+ }
+ if (o.isSVGText()) {
+ writeSVGText(ts, *toRenderBlock(&o), indent);
+ return;
+ }
+ if (o.isSVGInlineText()) {
+ writeSVGInlineText(ts, *toRenderText(&o), indent);
+ return;
+ }
+ if (o.isSVGImage()) {
+ writeSVGImage(ts, *toRenderSVGImage(&o), indent);
+ return;
+ }
+#endif
+
+ writeIndent(ts, indent);
+
+ RenderTreeAsText::writeRenderObject(ts, o, behavior);
+ ts << "\n";
+
+ if (o.isText() && !o.isBR()) {
+ const RenderText& text = *toRenderText(&o);
+ for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
+ writeIndent(ts, indent + 1);
+ writeTextRun(ts, text, *box);
+ }
+ }
+
+ for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
+ if (child->hasLayer())
+ continue;
+ write(ts, *child, indent + 1, behavior);
+ }
+
+ if (o.isWidget()) {
+ Widget* widget = toRenderWidget(&o)->widget();
+ if (widget && widget->isFrameView()) {
+ FrameView* view = static_cast<FrameView*>(widget);
+ RenderView* root = view->frame()->contentRenderer();
+ if (root) {
+ view->layout();
+ RenderLayer* l = root->layer();
+ if (l)
+ writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior);
+ }
+ }
+ }
+}
+
+enum LayerPaintPhase {
+ LayerPaintPhaseAll = 0,
+ LayerPaintPhaseBackground = -1,
+ LayerPaintPhaseForeground = 1
+};
+
+static void write(TextStream& ts, RenderLayer& l,
+ const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
+ LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
+{
+ writeIndent(ts, indent);
+
+ ts << "layer ";
+
+ if (behavior & RenderAsTextShowAddresses)
+ ts << static_cast<const void*>(&l) << " ";
+
+ ts << layerBounds;
+
+ if (!layerBounds.isEmpty()) {
+ if (!backgroundClipRect.contains(layerBounds))
+ ts << " backgroundClip " << backgroundClipRect;
+ if (!clipRect.contains(layerBounds))
+ ts << " clip " << clipRect;
+ if (!outlineClipRect.contains(layerBounds))
+ ts << " outlineClip " << outlineClipRect;
+ }
+
+ if (l.renderer()->hasOverflowClip()) {
+ if (l.scrollXOffset())
+ ts << " scrollX " << l.scrollXOffset();
+ if (l.scrollYOffset())
+ ts << " scrollY " << l.scrollYOffset();
+ if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth())
+ ts << " scrollWidth " << l.scrollWidth();
+ if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight())
+ ts << " scrollHeight " << l.scrollHeight();
+ }
+
+ if (paintPhase == LayerPaintPhaseBackground)
+ ts << " layerType: background only";
+ else if (paintPhase == LayerPaintPhaseForeground)
+ ts << " layerType: foreground only";
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (behavior & RenderAsTextShowCompositedLayers) {
+ if (l.isComposited())
+ ts << " (composited, bounds " << l.backing()->compositedBounds() << ")";
+ }
+#else
+ UNUSED_PARAM(behavior);
+#endif
+
+ ts << "\n";
+
+ if (paintPhase != LayerPaintPhaseBackground)
+ write(ts, *l.renderer(), indent + 1, behavior);
+}
+
+static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
+ const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
+{
+ // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
+ IntRect paintDirtyRect(paintRect);
+ if (rootLayer == l) {
+ paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->rightLayoutOverflow()));
+ paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->bottomLayoutOverflow()));
+ l->setWidth(max(l->width(), l->renderBox()->rightLayoutOverflow()));
+ l->setHeight(max(l->height(), l->renderBox()->bottomLayoutOverflow()));
+ }
+
+ // Calculate the clip rects we should use.
+ IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
+ l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true);
+
+ // Ensure our lists are up-to-date.
+ l->updateZOrderLists();
+ l->updateNormalFlowList();
+
+ bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer);
+ Vector<RenderLayer*>* negList = l->negZOrderList();
+ bool paintsBackgroundSeparately = negList && negList->size() > 0;
+ if (shouldPaint && paintsBackgroundSeparately)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior);
+
+ if (negList) {
+ int currIndent = indent;
+ if (behavior & RenderAsTextShowLayerNesting) {
+ writeIndent(ts, indent);
+ ts << " negative z-order list(" << negList->size() << ")\n";
+ ++currIndent;
+ }
+ for (unsigned i = 0; i != negList->size(); ++i)
+ writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
+ }
+
+ if (shouldPaint)
+ write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
+
+ if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
+ int currIndent = indent;
+ if (behavior & RenderAsTextShowLayerNesting) {
+ writeIndent(ts, indent);
+ ts << " normal flow list(" << normalFlowList->size() << ")\n";
+ ++currIndent;
+ }
+ for (unsigned i = 0; i != normalFlowList->size(); ++i)
+ writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
+ }
+
+ if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
+ int currIndent = indent;
+ if (behavior & RenderAsTextShowLayerNesting) {
+ writeIndent(ts, indent);
+ ts << " positive z-order list(" << posList->size() << ")\n";
+ ++currIndent;
+ }
+ for (unsigned i = 0; i != posList->size(); ++i)
+ writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
+ }
+}
+
+static String nodePosition(Node* node)
+{
+ String result;
+
+ Element* body = node->document()->body();
+ Node* parent;
+ for (Node* n = node; n; n = parent) {
+ parent = n->parentOrHostNode();
+ if (n != node)
+ result += " of ";
+ if (parent) {
+ if (body && n == body) {
+ // We don't care what offset body may be in the document.
+ result += "body";
+ break;
+ }
+ result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
+ } else
+ result += "document";
+ }
+
+ return result;
+}
+
+static void writeSelection(TextStream& ts, const RenderObject* o)
+{
+ Node* n = o->node();
+ if (!n || !n->isDocumentNode())
+ return;
+
+ Document* doc = static_cast<Document*>(n);
+ Frame* frame = doc->frame();
+ if (!frame)
+ return;
+
+ VisibleSelection selection = frame->selection()->selection();
+ if (selection.isCaret()) {
+ ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().node());
+ if (selection.affinity() == UPSTREAM)
+ ts << " (upstream affinity)";
+ ts << "\n";
+ } else if (selection.isRange())
+ ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().node()) << "\n"
+ << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().node()) << "\n";
+}
+
+String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
+{
+ PrintContext printContext(frame);
+ if (behavior & RenderAsTextPrintingMode) {
+ if (!frame->contentRenderer())
+ return String();
+ printContext.begin(frame->contentRenderer()->width());
+ }
+
+ if (!(behavior & RenderAsTextDontUpdateLayout))
+ frame->document()->updateLayout();
+
+ RenderObject* o = frame->contentRenderer();
+ if (!o)
+ return String();
+
+ TextStream ts;
+ if (o->hasLayer()) {
+ RenderLayer* l = toRenderBox(o)->layer();
+ writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior);
+ writeSelection(ts, o);
+ }
+ return ts.release();
+}
+
+static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
+{
+ for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
+ if (child->isCounter()) {
+ if (!isFirstCounter)
+ stream << " ";
+ isFirstCounter = false;
+ String str(toRenderText(child)->text());
+ stream << str;
+ }
+ }
+}
+
+String counterValueForElement(Element* element)
+{
+ // Make sure the element is not freed during the layout.
+ RefPtr<Element> elementRef(element);
+ element->document()->updateLayout();
+ TextStream stream;
+ bool isFirstCounter = true;
+ // The counter renderers should be children of anonymous children
+ // (i.e., :before or :after pseudo-elements).
+ if (RenderObject* renderer = element->renderer()) {
+ for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) {
+ if (child->isAnonymous())
+ writeCounterValuesFromChildren(stream, child, isFirstCounter);
+ }
+ }
+ return stream.release();
+}
+
+String markerTextForListItem(Element* element)
+{
+ // Make sure the element is not freed during the layout.
+ RefPtr<Element> elementRef(element);
+ element->document()->updateLayout();
+
+ RenderObject* renderer = element->renderer();
+ if (!renderer || !renderer->isListItem())
+ return String();
+
+ return toRenderListItem(renderer)->markerText();
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderTreeAsText.h b/Source/WebCore/rendering/RenderTreeAsText.h
new file mode 100644
index 0000000..4c8b145
--- /dev/null
+++ b/Source/WebCore/rendering/RenderTreeAsText.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2003, 2006, 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.
+ *
+ * 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 RenderTreeAsText_h
+#define RenderTreeAsText_h
+#include "TextStream.h"
+
+#include <wtf/Forward.h>
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+class Element;
+class FloatPoint;
+class FloatSize;
+class Frame;
+class IntPoint;
+class IntRect;
+class RenderObject;
+class TextStream;
+
+enum RenderAsTextBehaviorFlags {
+ RenderAsTextBehaviorNormal = 0,
+ RenderAsTextShowAllLayers = 1 << 0, // Dump all layers, not just those that would paint.
+ RenderAsTextShowLayerNesting = 1 << 1, // Annotate the layer lists.
+ RenderAsTextShowCompositedLayers = 1 << 2, // Show which layers are composited.
+ RenderAsTextShowAddresses = 1 << 3, // Show layer and renderer addresses.
+ RenderAsTextShowIDAndClass = 1 << 4, // Show id and class attributes
+ RenderAsTextPrintingMode = 1 << 5, // Dump the tree in printing mode.
+ RenderAsTextDontUpdateLayout = 1 << 6, // Don't update layout, to make it safe to call showLayerTree() from the debugger inside layout or painting code.
+ RenderAsTextShowLayoutState = 1 << 7 // Print the various 'needs layout' bits on renderers.
+};
+typedef unsigned RenderAsTextBehavior;
+
+// You don't need pageWidthInPixels if you don't specify RenderAsTextInPrintingMode.
+String externalRepresentation(Frame*, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
+void write(TextStream&, const RenderObject&, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
+void writeIndent(TextStream&, int indent);
+
+class RenderTreeAsText {
+// FIXME: This is a cheesy hack to allow easy access to RenderStyle colors. It won't be needed if we convert
+// it to use visitedDependentColor instead. (This just involves rebaselining many results though, so for now it's
+// not being done).
+public:
+static void writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior);
+};
+
+TextStream& operator<<(TextStream&, const IntPoint&);
+TextStream& operator<<(TextStream&, const IntRect&);
+TextStream& operator<<(TextStream&, const FloatPoint&);
+TextStream& operator<<(TextStream&, const FloatSize&);
+
+template<typename Item>
+TextStream& operator<<(TextStream& ts, const Vector<Item>& vector)
+{
+ ts << "[";
+
+ unsigned size = vector.size();
+ for (unsigned i = 0; i < size; ++i) {
+ ts << vector[i];
+ if (i < size - 1)
+ ts << ", ";
+ }
+
+ ts << "]";
+ return ts;
+}
+
+// Helper function shared with SVGRenderTreeAsText
+String quoteAndEscapeNonPrintables(const String&);
+
+String counterValueForElement(Element*);
+
+String markerTextForListItem(Element*);
+
+bool hasFractions(double val);
+
+} // namespace WebCore
+
+#endif // RenderTreeAsText_h
diff --git a/Source/WebCore/rendering/RenderVideo.cpp b/Source/WebCore/rendering/RenderVideo.cpp
new file mode 100644
index 0000000..5b82deb
--- /dev/null
+++ b/Source/WebCore/rendering/RenderVideo.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2007, 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.
+ *
+ * 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"
+
+#if ENABLE(VIDEO)
+#include "RenderVideo.h"
+
+#include "Document.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLNames.h"
+#include "HTMLVideoElement.h"
+#include "MediaPlayer.h"
+#include "RenderView.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayer.h"
+#include "RenderLayerBacking.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderVideo::RenderVideo(HTMLVideoElement* video)
+ : RenderMedia(video)
+{
+ setIntrinsicSize(calculateIntrinsicSize());
+}
+
+RenderVideo::~RenderVideo()
+{
+ if (MediaPlayer* p = player()) {
+ p->setVisible(false);
+ p->setFrameView(0);
+ }
+}
+
+IntSize RenderVideo::defaultSize()
+{
+ // These values are specified in the spec.
+ static const int cDefaultWidth = 300;
+ static const int cDefaultHeight = 150;
+
+ return IntSize(cDefaultWidth, cDefaultHeight);
+}
+
+void RenderVideo::intrinsicSizeChanged()
+{
+ if (videoElement()->shouldDisplayPosterImage())
+ RenderMedia::intrinsicSizeChanged();
+ updateIntrinsicSize();
+}
+
+void RenderVideo::updateIntrinsicSize()
+{
+ IntSize size = calculateIntrinsicSize();
+ size.scale(style()->effectiveZoom());
+
+ // Never set the element size to zero when in a media document.
+ if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument())
+ return;
+
+ if (size == intrinsicSize())
+ return;
+
+ setIntrinsicSize(size);
+ setPreferredLogicalWidthsDirty(true);
+ setNeedsLayout(true);
+}
+
+IntSize RenderVideo::calculateIntrinsicSize()
+{
+ HTMLVideoElement* video = videoElement();
+
+ // Spec text from 4.8.6
+ //
+ // The intrinsic width of a video element's playback area is the intrinsic width
+ // of the video resource, if that is available; otherwise it is the intrinsic
+ // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
+ //
+ // The intrinsic height of a video element's playback area is the intrinsic height
+ // of the video resource, if that is available; otherwise it is the intrinsic
+ // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
+
+ if (player() && video->readyState() >= HTMLVideoElement::HAVE_METADATA)
+ return player()->naturalSize();
+
+ if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred())
+ return m_cachedImageSize;
+
+ // When the natural size of the video is unavailable, we use the provided
+ // width and height attributes of the video element as the intrinsic size until
+ // better values become available.
+ if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr))
+ return IntSize(video->width(), video->height());
+
+ // <video> in standalone media documents should not use the default 300x150
+ // size since they also have audio-only files. By setting the intrinsic
+ // size to 300x1 the video will resize itself in these cases, and audio will
+ // have the correct height (it needs to be > 0 for controls to render properly).
+ if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
+ return IntSize(defaultSize().width(), 1);
+
+ return defaultSize();
+}
+
+void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
+{
+ RenderMedia::imageChanged(newImage, rect);
+
+ // Cache the image intrinsic size so we can continue to use it to draw the image correctly
+ // even if we know the video intrinsic size but aren't able to draw video frames yet
+ // (we don't want to scale the poster to the video size without keeping aspect ratio).
+ if (videoElement()->shouldDisplayPosterImage())
+ m_cachedImageSize = intrinsicSize();
+
+ // The intrinsic size is now that of the image, but in case we already had the
+ // intrinsic size of the video we call this here to restore the video size.
+ updateIntrinsicSize();
+}
+
+IntRect RenderVideo::videoBox() const
+{
+ if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
+ return IntRect();
+
+ IntSize elementSize;
+ if (videoElement()->shouldDisplayPosterImage())
+ elementSize = m_cachedImageSize;
+ else
+ elementSize = intrinsicSize();
+
+ IntRect contentRect = contentBoxRect();
+ if (elementSize.isEmpty() || contentRect.isEmpty())
+ return IntRect();
+
+ IntRect renderBox = contentRect;
+ int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
+ if (ratio > 0) {
+ int newWidth = renderBox.height() * elementSize.width() / elementSize.height();
+ // Just fill the whole area if the difference is one pixel or less (in both sides)
+ if (renderBox.width() - newWidth > 2)
+ renderBox.setWidth(newWidth);
+ renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
+ } else if (ratio < 0) {
+ int newHeight = renderBox.width() * elementSize.height() / elementSize.width();
+ if (renderBox.height() - newHeight > 2)
+ renderBox.setHeight(newHeight);
+ renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
+ }
+
+ return renderBox;
+}
+
+bool RenderVideo::shouldDisplayVideo() const
+{
+ return !videoElement()->shouldDisplayPosterImage();
+}
+
+void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
+{
+ MediaPlayer* mediaPlayer = player();
+ bool displayingPoster = videoElement()->shouldDisplayPosterImage();
+
+ if (!displayingPoster) {
+ if (!mediaPlayer)
+ return;
+ updatePlayer();
+ }
+
+ IntRect rect = videoBox();
+ if (rect.isEmpty())
+ return;
+ rect.move(tx, ty);
+
+ if (displayingPoster)
+ paintIntoRect(paintInfo.context, rect);
+ else
+ mediaPlayer->paint(paintInfo.context, rect);
+}
+
+void RenderVideo::layout()
+{
+ RenderMedia::layout();
+ updatePlayer();
+}
+
+HTMLVideoElement* RenderVideo::videoElement() const
+{
+ ASSERT(node()->hasTagName(videoTag));
+ return static_cast<HTMLVideoElement*>(node());
+}
+
+void RenderVideo::updateFromElement()
+{
+ RenderMedia::updateFromElement();
+ updatePlayer();
+}
+
+void RenderVideo::updatePlayer()
+{
+ updateIntrinsicSize();
+
+ MediaPlayer* mediaPlayer = player();
+ if (!mediaPlayer)
+ return;
+
+ if (!videoElement()->inActiveDocument()) {
+ mediaPlayer->setVisible(false);
+ return;
+ }
+
+#if USE(ACCELERATED_COMPOSITING)
+ layer()->contentChanged(RenderLayer::VideoChanged);
+#endif
+
+ IntRect videoBounds = videoBox();
+ mediaPlayer->setFrameView(document()->view());
+ mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
+ mediaPlayer->setVisible(true);
+}
+
+int RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const
+{
+ return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
+}
+
+int RenderVideo::computeReplacedLogicalHeight() const
+{
+ return RenderReplaced::computeReplacedLogicalHeight();
+}
+
+int RenderVideo::minimumReplacedHeight() const
+{
+ return RenderReplaced::minimumReplacedHeight();
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+bool RenderVideo::supportsAcceleratedRendering() const
+{
+ MediaPlayer* p = player();
+ if (p)
+ return p->supportsAcceleratedRendering();
+
+ return false;
+}
+
+void RenderVideo::acceleratedRenderingStateChanged()
+{
+ MediaPlayer* p = player();
+ if (p)
+ p->acceleratedRenderingStateChanged();
+}
+#endif // USE(ACCELERATED_COMPOSITING)
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/RenderVideo.h b/Source/WebCore/rendering/RenderVideo.h
new file mode 100644
index 0000000..9091490
--- /dev/null
+++ b/Source/WebCore/rendering/RenderVideo.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007, 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 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 RenderVideo_h
+#define RenderVideo_h
+
+#if ENABLE(VIDEO)
+
+#include "RenderMedia.h"
+
+namespace WebCore {
+
+class HTMLMediaElement;
+class HTMLVideoElement;
+
+class RenderVideo : public RenderMedia {
+public:
+ RenderVideo(HTMLVideoElement*);
+ virtual ~RenderVideo();
+
+ IntRect videoBox() const;
+
+ static IntSize defaultSize();
+
+#if USE(ACCELERATED_COMPOSITING)
+ bool supportsAcceleratedRendering() const;
+ void acceleratedRenderingStateChanged();
+#endif
+
+ virtual bool shouldDisplayVideo() const;
+
+private:
+ virtual void updateFromElement();
+ inline HTMLVideoElement* videoElement() const;
+
+ virtual void intrinsicSizeChanged();
+ IntSize calculateIntrinsicSize();
+ void updateIntrinsicSize();
+
+ virtual void imageChanged(WrappedImagePtr, const IntRect*);
+
+ virtual const char* renderName() const { return "RenderVideo"; }
+
+ virtual bool requiresLayer() const { return true; }
+ virtual bool isVideo() const { return true; }
+
+ virtual void paintReplaced(PaintInfo&, int tx, int ty);
+
+ virtual void layout();
+
+ virtual int computeReplacedLogicalWidth(bool includeMaxWidth = true) const;
+ virtual int computeReplacedLogicalHeight() const;
+ virtual int minimumReplacedHeight() const;
+
+ void updatePlayer();
+
+ IntSize m_cachedImageSize;
+};
+
+inline RenderVideo* toRenderVideo(RenderObject* object)
+{
+ ASSERT(!object || object->isVideo());
+ return static_cast<RenderVideo*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderVideo(const RenderVideo*);
+
+} // namespace WebCore
+
+#endif
+#endif // RenderVideo_h
diff --git a/Source/WebCore/rendering/RenderView.cpp b/Source/WebCore/rendering/RenderView.cpp
new file mode 100644
index 0000000..6820e34
--- /dev/null
+++ b/Source/WebCore/rendering/RenderView.cpp
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 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 "RenderView.h"
+
+#include "Document.h"
+#include "Element.h"
+#include "FloatQuad.h"
+#include "Frame.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HitTestResult.h"
+#include "RenderLayer.h"
+#include "RenderSelectionInfo.h"
+#include "RenderWidget.h"
+#include "RenderWidgetProtector.h"
+#include "TransformState.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerCompositor.h"
+#endif
+
+#if defined(ANDROID_LAYOUT) || defined(ANDROID_FIXED_ELEMENTS)
+#include "Settings.h"
+#endif
+
+namespace WebCore {
+
+RenderView::RenderView(Node* node, FrameView* view)
+ : RenderBlock(node)
+ , m_frameView(view)
+ , m_selectionStart(0)
+ , m_selectionEnd(0)
+ , m_selectionStartPos(-1)
+ , m_selectionEndPos(-1)
+ , m_maximalOutlineSize(0)
+ , m_pageLogicalHeight(0)
+ , m_pageLogicalHeightChanged(false)
+ , m_layoutState(0)
+ , m_layoutStateDisableCount(0)
+{
+ // Clear our anonymous bit, set because RenderObject assumes
+ // any renderer with document as the node is anonymous.
+ setIsAnonymous(false);
+
+ // init RenderObject attributes
+ setInline(false);
+
+ m_minPreferredLogicalWidth = 0;
+ m_maxPreferredLogicalWidth = 0;
+
+ setPreferredLogicalWidthsDirty(true, false);
+
+ setPositioned(true); // to 0,0 :)
+}
+
+RenderView::~RenderView()
+{
+}
+
+void RenderView::computeLogicalHeight()
+{
+ if (!printing() && m_frameView)
+ setLogicalHeight(viewLogicalHeight());
+}
+
+void RenderView::computeLogicalWidth()
+{
+ if (!printing() && m_frameView)
+ setLogicalWidth(viewLogicalWidth());
+#ifdef ANDROID_LAYOUT
+ setVisibleWidth(m_frameView->textWrapWidth());
+ const Settings * settings = document()->settings();
+ ASSERT(settings);
+ if (settings->useWideViewport() && settings->viewportWidth() == -1 && width() < minPreferredLogicalWidth())
+ setWidth(m_minPreferredLogicalWidth);
+#endif
+}
+
+void RenderView::computePreferredLogicalWidths()
+{
+ ASSERT(preferredLogicalWidthsDirty());
+
+ RenderBlock::computePreferredLogicalWidths();
+
+ m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth;
+}
+
+bool RenderView::isChildAllowed(RenderObject* child, RenderStyle*) const
+{
+ return child->isBox();
+}
+
+void RenderView::layout()
+{
+ if (!document()->paginated())
+ setPageLogicalHeight(0);
+
+ if (printing())
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = width();
+
+ // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account.
+ bool relayoutChildren = !printing() && (!m_frameView || width() != viewWidth() || height() != viewHeight());
+ if (relayoutChildren) {
+ setChildNeedsLayout(true, false);
+ for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+ if (child->style()->logicalHeight().isPercent() || child->style()->logicalMinHeight().isPercent() || child->style()->logicalMaxHeight().isPercent())
+ child->setChildNeedsLayout(true, false);
+ }
+ }
+
+ ASSERT(!m_layoutState);
+ LayoutState state;
+ // FIXME: May be better to push a clip and avoid issuing offscreen repaints.
+ state.m_clipped = false;
+ state.m_pageLogicalHeight = m_pageLogicalHeight;
+ state.m_pageLogicalHeightChanged = m_pageLogicalHeightChanged;
+ m_pageLogicalHeightChanged = false;
+ m_layoutState = &state;
+
+ if (needsLayout())
+ RenderBlock::layout();
+
+ ASSERT(layoutDelta() == IntSize());
+ ASSERT(m_layoutStateDisableCount == 0);
+ ASSERT(m_layoutState == &state);
+ m_layoutState = 0;
+ setNeedsLayout(false);
+}
+
+void RenderView::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool /*useTransforms*/, TransformState& transformState) const
+{
+ // If a container was specified, and was not 0 or the RenderView,
+ // then we should have found it by now.
+ ASSERT_UNUSED(repaintContainer, !repaintContainer || repaintContainer == this);
+
+ if (fixed && m_frameView)
+ transformState.move(m_frameView->scrollOffset());
+}
+
+void RenderView::mapAbsoluteToLocalPoint(bool fixed, bool /*useTransforms*/, TransformState& transformState) const
+{
+ if (fixed && m_frameView)
+ transformState.move(-m_frameView->scrollOffset());
+}
+
+void RenderView::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ // If we ever require layout but receive a paint anyway, something has gone horribly wrong.
+ ASSERT(!needsLayout());
+ paintObject(paintInfo, tx, ty);
+}
+
+static inline bool isComposited(RenderObject* object)
+{
+ return object->hasLayer() && toRenderBoxModelObject(object)->layer()->isComposited();
+}
+
+static inline bool rendererObscuresBackground(RenderObject* object)
+{
+ return object && object->style()->visibility() == VISIBLE
+ && object->style()->opacity() == 1
+ && !object->style()->hasTransform()
+ && !isComposited(object);
+}
+
+void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int)
+{
+ // Check to see if we are enclosed by a layer that requires complex painting rules. If so, we cannot blit
+ // when scrolling, and we need to use slow repaints. Examples of layers that require this are transparent layers,
+ // layers with reflections, or transformed layers.
+ // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being inside
+ // a transform, transparency layer, etc.
+ Element* elt;
+ for (elt = document()->ownerElement(); view() && elt && elt->renderer(); elt = elt->document()->ownerElement()) {
+ RenderLayer* layer = elt->renderer()->enclosingLayer();
+ if (layer->requiresSlowRepaints()) {
+ frameView()->setUseSlowRepaints();
+ break;
+ }
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (RenderLayer* compositingLayer = layer->enclosingCompositingLayer()) {
+ if (!compositingLayer->backing()->paintingGoesToWindow()) {
+ frameView()->setUseSlowRepaints();
+ break;
+ }
+ }
+#endif
+ }
+
+ if (document()->ownerElement() || !view())
+ return;
+
+ bool rootFillsViewport = false;
+ Node* documentElement = document()->documentElement();
+ if (RenderObject* rootRenderer = documentElement ? documentElement->renderer() : 0) {
+ // The document element's renderer is currently forced to be a block, but may not always be.
+ RenderBox* rootBox = rootRenderer->isBox() ? toRenderBox(rootRenderer) : 0;
+ rootFillsViewport = rootBox && !rootBox->x() && !rootBox->y() && rootBox->width() >= width() && rootBox->height() >= height();
+ }
+
+ // If painting will entirely fill the view, no need to fill the background.
+ if (rootFillsViewport && rendererObscuresBackground(firstChild()))
+ return;
+
+ // This code typically only executes if the root element's visibility has been set to hidden,
+ // or there is a transform on the <html>.
+ // Only fill with the base background color (typically white) if we're the root document,
+ // since iframes/frames with no background in the child document should show the parent's background.
+ if (frameView()->isTransparent()) // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent.
+ frameView()->setUseSlowRepaints(); // The parent must show behind the child.
+ else {
+ Color baseColor = frameView()->baseBackgroundColor();
+ if (baseColor.alpha() > 0) {
+ CompositeOperator previousOperator = paintInfo.context->compositeOperation();
+ paintInfo.context->setCompositeOperation(CompositeCopy);
+ paintInfo.context->fillRect(paintInfo.rect, baseColor, style()->colorSpace());
+ paintInfo.context->setCompositeOperation(previousOperator);
+ } else
+ paintInfo.context->clearRect(paintInfo.rect);
+ }
+}
+
+bool RenderView::shouldRepaint(const IntRect& r) const
+{
+ if (printing() || r.width() == 0 || r.height() == 0)
+ return false;
+
+ if (!m_frameView)
+ return false;
+
+ return true;
+}
+
+void RenderView::repaintViewRectangle(const IntRect& ur, bool immediate)
+{
+ if (!shouldRepaint(ur))
+ return;
+
+ // We always just invalidate the root view, since we could be an iframe that is clipped out
+ // or even invisible.
+ Element* elt = document()->ownerElement();
+ if (!elt)
+ m_frameView->repaintContentRectangle(ur, immediate);
+ else if (RenderBox* obj = elt->renderBox()) {
+ IntRect vr = viewRect();
+ IntRect r = intersection(ur, vr);
+
+ // Subtract out the contentsX and contentsY offsets to get our coords within the viewing
+ // rectangle.
+ r.move(-vr.x(), -vr.y());
+
+ // FIXME: Hardcoded offsets here are not good.
+ r.move(obj->borderLeft() + obj->paddingLeft(),
+ obj->borderTop() + obj->paddingTop());
+ obj->repaintRectangle(r, immediate);
+ }
+}
+
+void RenderView::repaintRectangleInViewAndCompositedLayers(const IntRect& ur, bool immediate)
+{
+ if (!shouldRepaint(ur))
+ return;
+
+ repaintViewRectangle(ur, immediate);
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (compositor()->inCompositingMode())
+ compositor()->repaintCompositedLayersAbsoluteRect(ur);
+#endif
+}
+
+void RenderView::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
+{
+ // If a container was specified, and was not 0 or the RenderView,
+ // then we should have found it by now.
+ ASSERT_UNUSED(repaintContainer, !repaintContainer || repaintContainer == this);
+
+ if (printing())
+ return;
+
+ if (style()->isFlippedBlocksWritingMode()) {
+ // We have to flip by hand since the view's logical height has not been determined. We
+ // can use the viewport width and height.
+ if (style()->isHorizontalWritingMode())
+ rect.setY(viewHeight() - rect.bottom());
+ else
+ rect.setX(viewWidth() - rect.right());
+ }
+
+ if (fixed && m_frameView)
+ rect.move(m_frameView->scrollX(), m_frameView->scrollY());
+
+ // Apply our transform if we have one (because of full page zooming).
+ if (m_layer && m_layer->transform())
+ rect = m_layer->transform()->mapRect(rect);
+}
+
+void RenderView::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
+{
+ rects.append(IntRect(tx, ty, m_layer->width(), m_layer->height()));
+}
+
+void RenderView::absoluteQuads(Vector<FloatQuad>& quads)
+{
+ quads.append(FloatRect(0, 0, m_layer->width(), m_layer->height()));
+}
+
+static RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset)
+{
+ if (!object)
+ return 0;
+
+ RenderObject* child = object->childAt(offset);
+ return child ? child : object->nextInPreOrderAfterChildren();
+}
+
+IntRect RenderView::selectionBounds(bool clipToVisibleContent) const
+{
+ document()->updateStyleIfNeeded();
+
+ typedef HashMap<RenderObject*, RenderSelectionInfo*> SelectionMap;
+ SelectionMap selectedObjects;
+
+ RenderObject* os = m_selectionStart;
+ RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
+ while (os && os != stop) {
+ if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
+ // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
+ selectedObjects.set(os, new RenderSelectionInfo(os, clipToVisibleContent));
+ RenderBlock* cb = os->containingBlock();
+ while (cb && !cb->isRenderView()) {
+ RenderSelectionInfo* blockInfo = selectedObjects.get(cb);
+ if (blockInfo)
+ break;
+ selectedObjects.set(cb, new RenderSelectionInfo(cb, clipToVisibleContent));
+ cb = cb->containingBlock();
+ }
+ }
+
+ os = os->nextInPreOrder();
+ }
+
+ // Now create a single bounding box rect that encloses the whole selection.
+ IntRect selRect;
+ SelectionMap::iterator end = selectedObjects.end();
+ for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) {
+ RenderSelectionInfo* info = i->second;
+ // RenderSelectionInfo::rect() is in the coordinates of the repaintContainer, so map to page coordinates.
+ IntRect currRect = info->rect();
+ if (RenderBoxModelObject* repaintContainer = info->repaintContainer()) {
+ FloatQuad absQuad = repaintContainer->localToAbsoluteQuad(FloatRect(currRect));
+ currRect = absQuad.enclosingBoundingBox();
+ }
+ selRect.unite(currRect);
+ delete info;
+ }
+ return selRect;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+// Compositing layer dimensions take outline size into account, so we have to recompute layer
+// bounds when it changes.
+// FIXME: This is ugly; it would be nice to have a better way to do this.
+void RenderView::setMaximalOutlineSize(int o)
+{
+ if (o != m_maximalOutlineSize) {
+ m_maximalOutlineSize = o;
+
+ // maximalOutlineSize affects compositing layer dimensions.
+ compositor()->setCompositingLayersNeedRebuild(); // FIXME: this really just needs to be a geometry update.
+ }
+}
+#endif
+
+void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode blockRepaintMode)
+{
+ // Make sure both our start and end objects are defined.
+ // Check www.msnbc.com and try clicking around to find the case where this happened.
+ if ((start && !end) || (end && !start))
+ return;
+
+ // Just return if the selection hasn't changed.
+ if (m_selectionStart == start && m_selectionStartPos == startPos &&
+ m_selectionEnd == end && m_selectionEndPos == endPos)
+ return;
+
+ // Record the old selected objects. These will be used later
+ // when we compare against the new selected objects.
+ int oldStartPos = m_selectionStartPos;
+ int oldEndPos = m_selectionEndPos;
+
+ // Objects each have a single selection rect to examine.
+ typedef HashMap<RenderObject*, RenderSelectionInfo*> SelectedObjectMap;
+ SelectedObjectMap oldSelectedObjects;
+ SelectedObjectMap newSelectedObjects;
+
+ // Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks.
+ // In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise
+ // the union of those rects might remain the same even when changes have occurred.
+ typedef HashMap<RenderBlock*, RenderBlockSelectionInfo*> SelectedBlockMap;
+ SelectedBlockMap oldSelectedBlocks;
+ SelectedBlockMap newSelectedBlocks;
+
+ RenderObject* os = m_selectionStart;
+ RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos);
+ while (os && os != stop) {
+ if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
+ // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
+ oldSelectedObjects.set(os, new RenderSelectionInfo(os, true));
+ if (blockRepaintMode == RepaintNewXOROld) {
+ RenderBlock* cb = os->containingBlock();
+ while (cb && !cb->isRenderView()) {
+ RenderBlockSelectionInfo* blockInfo = oldSelectedBlocks.get(cb);
+ if (blockInfo)
+ break;
+ oldSelectedBlocks.set(cb, new RenderBlockSelectionInfo(cb));
+ cb = cb->containingBlock();
+ }
+ }
+ }
+
+ os = os->nextInPreOrder();
+ }
+
+ // Now clear the selection.
+ SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end();
+ for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i)
+ i->first->setSelectionState(SelectionNone);
+
+ // set selection start and end
+ m_selectionStart = start;
+ m_selectionStartPos = startPos;
+ m_selectionEnd = end;
+ m_selectionEndPos = endPos;
+
+ // Update the selection status of all objects between m_selectionStart and m_selectionEnd
+ if (start && start == end)
+ start->setSelectionState(SelectionBoth);
+ else {
+ if (start)
+ start->setSelectionState(SelectionStart);
+ if (end)
+ end->setSelectionState(SelectionEnd);
+ }
+
+ RenderObject* o = start;
+ stop = rendererAfterPosition(end, endPos);
+
+ while (o && o != stop) {
+ if (o != start && o != end && o->canBeSelectionLeaf())
+ o->setSelectionState(SelectionInside);
+ o = o->nextInPreOrder();
+ }
+
+ m_layer->clearBlockSelectionGapsBounds();
+
+ // Now that the selection state has been updated for the new objects, walk them again and
+ // put them in the new objects list.
+ o = start;
+ while (o && o != stop) {
+ if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) {
+ newSelectedObjects.set(o, new RenderSelectionInfo(o, true));
+ RenderBlock* cb = o->containingBlock();
+ while (cb && !cb->isRenderView()) {
+ RenderBlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb);
+ if (blockInfo)
+ break;
+ newSelectedBlocks.set(cb, new RenderBlockSelectionInfo(cb));
+ cb = cb->containingBlock();
+ }
+ }
+
+ o = o->nextInPreOrder();
+ }
+
+ if (!m_frameView) {
+ // We built the maps, but we aren't going to use them.
+ // We need to delete the values, otherwise they'll all leak!
+ deleteAllValues(oldSelectedObjects);
+ deleteAllValues(newSelectedObjects);
+ deleteAllValues(oldSelectedBlocks);
+ deleteAllValues(newSelectedBlocks);
+ return;
+ }
+
+ m_frameView->beginDeferredRepaints();
+
+ // Have any of the old selected objects changed compared to the new selection?
+ for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) {
+ RenderObject* obj = i->first;
+ RenderSelectionInfo* newInfo = newSelectedObjects.get(obj);
+ RenderSelectionInfo* oldInfo = i->second;
+ if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() ||
+ (m_selectionStart == obj && oldStartPos != m_selectionStartPos) ||
+ (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) {
+ oldInfo->repaint();
+ if (newInfo) {
+ newInfo->repaint();
+ newSelectedObjects.remove(obj);
+ delete newInfo;
+ }
+ }
+ delete oldInfo;
+ }
+
+ // Any new objects that remain were not found in the old objects dict, and so they need to be updated.
+ SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end();
+ for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) {
+ RenderSelectionInfo* newInfo = i->second;
+ newInfo->repaint();
+ delete newInfo;
+ }
+
+ // Have any of the old blocks changed?
+ SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end();
+ for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) {
+ RenderBlock* block = i->first;
+ RenderBlockSelectionInfo* newInfo = newSelectedBlocks.get(block);
+ RenderBlockSelectionInfo* oldInfo = i->second;
+ if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) {
+ oldInfo->repaint();
+ if (newInfo) {
+ newInfo->repaint();
+ newSelectedBlocks.remove(block);
+ delete newInfo;
+ }
+ }
+ delete oldInfo;
+ }
+
+ // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated.
+ SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end();
+ for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) {
+ RenderBlockSelectionInfo* newInfo = i->second;
+ newInfo->repaint();
+ delete newInfo;
+ }
+
+ m_frameView->endDeferredRepaints();
+}
+
+void RenderView::clearSelection()
+{
+ m_layer->repaintBlockSelectionGaps();
+ setSelection(0, -1, 0, -1, RepaintNewMinusOld);
+}
+
+void RenderView::selectionStartEnd(int& startPos, int& endPos) const
+{
+ startPos = m_selectionStartPos;
+ endPos = m_selectionEndPos;
+}
+
+bool RenderView::printing() const
+{
+ return document()->printing();
+}
+
+size_t RenderView::getRetainedWidgets(Vector<RenderWidget*>& renderWidgets)
+{
+ size_t size = m_widgets.size();
+
+ renderWidgets.reserveCapacity(size);
+
+ RenderWidgetSet::const_iterator end = m_widgets.end();
+ for (RenderWidgetSet::const_iterator it = m_widgets.begin(); it != end; ++it) {
+ renderWidgets.uncheckedAppend(*it);
+ (*it)->ref();
+ }
+
+ return size;
+}
+
+void RenderView::releaseWidgets(Vector<RenderWidget*>& renderWidgets)
+{
+ size_t size = renderWidgets.size();
+
+ for (size_t i = 0; i < size; ++i)
+ renderWidgets[i]->deref(renderArena());
+}
+
+void RenderView::updateWidgetPositions()
+{
+ // updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running
+ // scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets
+ // alive during enumeration.
+
+ Vector<RenderWidget*> renderWidgets;
+ size_t size = getRetainedWidgets(renderWidgets);
+
+ for (size_t i = 0; i < size; ++i)
+ renderWidgets[i]->updateWidgetPosition();
+
+ for (size_t i = 0; i < size; ++i)
+ renderWidgets[i]->widgetPositionsUpdated();
+
+ releaseWidgets(renderWidgets);
+}
+
+void RenderView::addWidget(RenderWidget* o)
+{
+ m_widgets.add(o);
+}
+
+void RenderView::removeWidget(RenderWidget* o)
+{
+ m_widgets.remove(o);
+}
+
+void RenderView::notifyWidgets(WidgetNotification notification)
+{
+ Vector<RenderWidget*> renderWidgets;
+ size_t size = getRetainedWidgets(renderWidgets);
+
+ for (size_t i = 0; i < size; ++i)
+ renderWidgets[i]->notifyWidget(notification);
+
+ releaseWidgets(renderWidgets);
+}
+
+IntRect RenderView::viewRect() const
+{
+ if (printing())
+ return IntRect(0, 0, width(), height());
+ if (m_frameView)
+ return m_frameView->visibleContentRect();
+ return IntRect();
+}
+
+int RenderView::docTop() const
+{
+ IntRect overflowRect(0, topLayoutOverflow(), 0, bottomLayoutOverflow() - topLayoutOverflow());
+ flipForWritingMode(overflowRect);
+ if (hasTransform())
+ overflowRect = layer()->currentTransform().mapRect(overflowRect);
+ return overflowRect.y();
+}
+
+int RenderView::docBottom() const
+{
+ IntRect overflowRect(layoutOverflowRect());
+ flipForWritingMode(overflowRect);
+ if (hasTransform())
+ overflowRect = layer()->currentTransform().mapRect(overflowRect);
+ return overflowRect.bottom();
+}
+
+int RenderView::docLeft() const
+{
+ IntRect overflowRect(layoutOverflowRect());
+ flipForWritingMode(overflowRect);
+ if (hasTransform())
+ overflowRect = layer()->currentTransform().mapRect(overflowRect);
+ return overflowRect.x();
+}
+
+int RenderView::docRight() const
+{
+ IntRect overflowRect(layoutOverflowRect());
+ flipForWritingMode(overflowRect);
+ if (hasTransform())
+ overflowRect = layer()->currentTransform().mapRect(overflowRect);
+ return overflowRect.right();
+}
+
+int RenderView::viewHeight() const
+{
+ int height = 0;
+ if (!printing() && m_frameView) {
+ height = m_frameView->layoutHeight();
+ height = m_frameView->useFixedLayout() ? ceilf(style()->effectiveZoom() * float(height)) : height;
+ }
+ return height;
+}
+
+int RenderView::viewWidth() const
+{
+ int width = 0;
+ if (!printing() && m_frameView) {
+ width = m_frameView->layoutWidth();
+ width = m_frameView->useFixedLayout() ? ceilf(style()->effectiveZoom() * float(width)) : width;
+ }
+ return width;
+}
+
+float RenderView::zoomFactor() const
+{
+ Frame* frame = m_frameView->frame();
+ return frame ? frame->pageZoomFactor() : 1;
+}
+
+void RenderView::pushLayoutState(RenderObject* root)
+{
+ ASSERT(m_layoutStateDisableCount == 0);
+ ASSERT(m_layoutState == 0);
+
+ m_layoutState = new (renderArena()) LayoutState(root);
+}
+
+bool RenderView::shouldDisableLayoutStateForSubtree(RenderObject* renderer) const
+{
+ RenderObject* o = renderer;
+ while (o) {
+ if (o->hasColumns() || o->hasTransform() || o->hasReflection())
+ return true;
+ o = o->container();
+ }
+ return false;
+}
+
+void RenderView::updateHitTestResult(HitTestResult& result, const IntPoint& point)
+{
+ if (result.innerNode())
+ return;
+
+ Node* node = document()->documentElement();
+ if (node) {
+ result.setInnerNode(node);
+ if (!result.innerNonSharedNode())
+ result.setInnerNonSharedNode(node);
+ result.setLocalPoint(point);
+ }
+}
+
+// FIXME: This function is obsolete and only used by embedded WebViews inside AppKit NSViews.
+// Do not add callers of this function!
+// The idea here is to take into account what object is moving the pagination point, and
+// thus choose the best place to chop it.
+void RenderView::setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak)
+{
+ // Nobody else can set a page break once we have a forced break.
+ if (m_legacyPrinting.m_forcedPageBreak)
+ return;
+
+ // Forced breaks always win over unforced breaks.
+ if (forcedBreak) {
+ m_legacyPrinting.m_forcedPageBreak = true;
+ m_legacyPrinting.m_bestTruncatedAt = y;
+ return;
+ }
+
+ // Prefer the widest object that tries to move the pagination point
+ IntRect boundingBox = forRenderer->borderBoundingBox();
+ if (boundingBox.width() > m_legacyPrinting.m_truncatorWidth) {
+ m_legacyPrinting.m_truncatorWidth = boundingBox.width();
+ m_legacyPrinting.m_bestTruncatedAt = y;
+ }
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+bool RenderView::usesCompositing() const
+{
+ return m_compositor && m_compositor->inCompositingMode();
+}
+
+RenderLayerCompositor* RenderView::compositor()
+{
+ if (!m_compositor)
+ m_compositor.set(new RenderLayerCompositor(this));
+
+ return m_compositor.get();
+}
+#endif
+
+void RenderView::didMoveOnscreen()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (m_compositor)
+ m_compositor->didMoveOnscreen();
+#endif
+}
+
+void RenderView::willMoveOffscreen()
+{
+#if USE(ACCELERATED_COMPOSITING)
+ if (m_compositor)
+ m_compositor->willMoveOffscreen();
+#endif
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderView.h b/Source/WebCore/rendering/RenderView.h
new file mode 100644
index 0000000..2915998
--- /dev/null
+++ b/Source/WebCore/rendering/RenderView.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 1999 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.
+ *
+ */
+
+#ifndef RenderView_h
+#define RenderView_h
+
+#include "FrameView.h"
+#include "LayoutState.h"
+#include "RenderBlock.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class RenderWidget;
+
+#if USE(ACCELERATED_COMPOSITING)
+class RenderLayerCompositor;
+#endif
+
+class RenderView : public RenderBlock {
+public:
+ RenderView(Node*, FrameView*);
+ virtual ~RenderView();
+
+ virtual const char* renderName() const { return "RenderView"; }
+
+ virtual bool isRenderView() const { return true; }
+
+ virtual bool requiresLayer() const { return true; }
+
+ virtual bool isChildAllowed(RenderObject*, RenderStyle*) const;
+
+ virtual void layout();
+ virtual void computeLogicalWidth();
+ virtual void computeLogicalHeight();
+ virtual void computePreferredLogicalWidths();
+
+ // The same as the FrameView's layoutHeight/layoutWidth but with null check guards.
+ int viewHeight() const;
+ int viewWidth() const;
+ int viewLogicalWidth() const { return style()->isHorizontalWritingMode() ? viewWidth() : viewHeight(); }
+ int viewLogicalHeight() const { return style()->isHorizontalWritingMode() ? viewHeight() : viewWidth(); }
+
+ float zoomFactor() const;
+
+ FrameView* frameView() const { return m_frameView; }
+
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+ virtual void repaintViewRectangle(const IntRect&, bool immediate = false);
+ // Repaint the view, and all composited layers that intersect the given absolute rectangle.
+ // FIXME: ideally we'd never have to do this, if all repaints are container-relative.
+ virtual void repaintRectangleInViewAndCompositedLayers(const IntRect&, bool immediate = false);
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual void paintBoxDecorations(PaintInfo&, int tx, int ty);
+
+ enum SelectionRepaintMode { RepaintNewXOROld, RepaintNewMinusOld };
+ void setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos, SelectionRepaintMode = RepaintNewXOROld);
+ void clearSelection();
+ RenderObject* selectionStart() const { return m_selectionStart; }
+ RenderObject* selectionEnd() const { return m_selectionEnd; }
+ IntRect selectionBounds(bool clipToVisibleContent = true) const;
+ void selectionStartEnd(int& startPos, int& endPos) const;
+
+ bool printing() const;
+
+ virtual void absoluteRects(Vector<IntRect>&, int tx, int ty);
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+#if USE(ACCELERATED_COMPOSITING)
+ void setMaximalOutlineSize(int o);
+#else
+ void setMaximalOutlineSize(int o) { m_maximalOutlineSize = o; }
+#endif
+ int maximalOutlineSize() const { return m_maximalOutlineSize; }
+
+ virtual IntRect viewRect() const;
+
+ void updateWidgetPositions();
+ void addWidget(RenderWidget*);
+ void removeWidget(RenderWidget*);
+
+ void notifyWidgets(WidgetNotification);
+#ifdef ANDROID_PLUGINS
+ const HashSet<RenderWidget*>& widgets() const { return m_widgets; }
+#endif
+
+ // layoutDelta is used transiently during layout to store how far an object has moved from its
+ // last layout location, in order to repaint correctly.
+ // If we're doing a full repaint m_layoutState will be 0, but in that case layoutDelta doesn't matter.
+ IntSize layoutDelta() const
+ {
+ return m_layoutState ? m_layoutState->m_layoutDelta : IntSize();
+ }
+ void addLayoutDelta(const IntSize& delta)
+ {
+ if (m_layoutState)
+ m_layoutState->m_layoutDelta += delta;
+ }
+
+ bool doingFullRepaint() const { return m_frameView->needsFullRepaint(); }
+
+ // Subtree push/pop
+ void pushLayoutState(RenderObject*);
+ void popLayoutState(RenderObject*) { return popLayoutState(); } // Just doing this to keep popLayoutState() private and to make the subtree calls symmetrical.
+
+ bool shouldDisableLayoutStateForSubtree(RenderObject*) const;
+
+ // Returns true if layoutState should be used for its cached offset and clip.
+ bool layoutStateEnabled() const { return m_layoutStateDisableCount == 0 && m_layoutState; }
+ LayoutState* layoutState() const { return m_layoutState; }
+
+ // Suspends the LayoutState optimization. Used under transforms that cannot be represented by
+ // LayoutState (common in SVG) and when manipulating the render tree during layout in ways
+ // that can trigger repaint of a non-child (e.g. when a list item moves its list marker around).
+ // Note that even when disabled, LayoutState is still used to store layoutDelta.
+ void disableLayoutState() { m_layoutStateDisableCount++; }
+ void enableLayoutState() { ASSERT(m_layoutStateDisableCount > 0); m_layoutStateDisableCount--; }
+
+ virtual void updateHitTestResult(HitTestResult&, const IntPoint&);
+
+ unsigned pageLogicalHeight() const { return m_pageLogicalHeight; }
+ void setPageLogicalHeight(unsigned height)
+ {
+ if (m_pageLogicalHeight != height) {
+ m_pageLogicalHeight = height;
+ m_pageLogicalHeightChanged = true;
+ }
+ }
+
+ // FIXME: These functions are deprecated. No code should be added that uses these.
+ int bestTruncatedAt() const { return m_legacyPrinting.m_bestTruncatedAt; }
+ void setBestTruncatedAt(int y, RenderBoxModelObject* forRenderer, bool forcedBreak = false);
+ int truncatedAt() const { return m_legacyPrinting.m_truncatedAt; }
+ void setTruncatedAt(int y)
+ {
+ m_legacyPrinting.m_truncatedAt = y;
+ m_legacyPrinting.m_bestTruncatedAt = 0;
+ m_legacyPrinting.m_truncatorWidth = 0;
+ m_legacyPrinting.m_forcedPageBreak = false;
+ }
+ const IntRect& printRect() const { return m_legacyPrinting.m_printRect; }
+ void setPrintRect(const IntRect& r) { m_legacyPrinting.m_printRect = r; }
+ // End deprecated functions.
+
+ // Notifications that this view became visible in a window, or will be
+ // removed from the window.
+ void didMoveOnscreen();
+ void willMoveOffscreen();
+
+#if USE(ACCELERATED_COMPOSITING)
+ RenderLayerCompositor* compositor();
+ bool usesCompositing() const;
+#endif
+
+ int docTop() const;
+ int docBottom() const;
+ int docHeight() const { return docBottom() - docTop(); }
+ int docLeft() const;
+ int docRight() const;
+ int docWidth() const { return docRight() - docLeft(); }
+ IntRect documentRect() const { return IntRect(docLeft(), docTop(), docWidth(), docHeight()); }
+
+protected:
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+ virtual void mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState&) const;
+
+private:
+ bool shouldRepaint(const IntRect& r) const;
+
+ // These functions may only be accessed by LayoutStateMaintainer.
+ bool pushLayoutState(RenderBox* renderer, const IntSize& offset, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0)
+ {
+ // We push LayoutState even if layoutState is disabled because it stores layoutDelta too.
+ if (!doingFullRepaint() || renderer->hasColumns() || m_layoutState->isPaginated()) {
+ m_layoutState = new (renderArena()) LayoutState(m_layoutState, renderer, offset, pageHeight, pageHeightChanged, colInfo);
+ return true;
+ }
+ return false;
+ }
+
+ void popLayoutState()
+ {
+ LayoutState* state = m_layoutState;
+ m_layoutState = state->m_next;
+ state->destroy(renderArena());
+ }
+
+ size_t getRetainedWidgets(Vector<RenderWidget*>&);
+ void releaseWidgets(Vector<RenderWidget*>&);
+
+ friend class LayoutStateMaintainer;
+
+protected:
+ FrameView* m_frameView;
+
+ RenderObject* m_selectionStart;
+ RenderObject* m_selectionEnd;
+ int m_selectionStartPos;
+ int m_selectionEndPos;
+
+ // FIXME: Only used by embedded WebViews inside AppKit NSViews. Find a way to remove.
+ struct LegacyPrinting {
+ LegacyPrinting()
+ : m_bestTruncatedAt(0)
+ , m_truncatedAt(0)
+ , m_truncatorWidth(0)
+ , m_forcedPageBreak(false)
+ { }
+
+ int m_bestTruncatedAt;
+ int m_truncatedAt;
+ int m_truncatorWidth;
+ IntRect m_printRect;
+ bool m_forcedPageBreak;
+ };
+ LegacyPrinting m_legacyPrinting;
+ // End deprecated members.
+
+ int m_maximalOutlineSize; // Used to apply a fudge factor to dirty-rect checks on blocks/tables.
+
+ typedef HashSet<RenderWidget*> RenderWidgetSet;
+ RenderWidgetSet m_widgets;
+
+private:
+ unsigned m_pageLogicalHeight;
+ bool m_pageLogicalHeightChanged;
+ LayoutState* m_layoutState;
+ unsigned m_layoutStateDisableCount;
+#if USE(ACCELERATED_COMPOSITING)
+ OwnPtr<RenderLayerCompositor> m_compositor;
+#endif
+};
+
+inline RenderView* toRenderView(RenderObject* object)
+{
+ ASSERT(!object || object->isRenderView());
+ return static_cast<RenderView*>(object);
+}
+
+inline const RenderView* toRenderView(const RenderObject* object)
+{
+ ASSERT(!object || object->isRenderView());
+ return static_cast<const RenderView*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderView(const RenderView*);
+
+
+// Stack-based class to assist with LayoutState push/pop
+class LayoutStateMaintainer : public Noncopyable {
+public:
+ // ctor to push now
+ LayoutStateMaintainer(RenderView* view, RenderBox* root, IntSize offset, bool disableState = false, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0)
+ : m_view(view)
+ , m_disabled(disableState)
+ , m_didStart(false)
+ , m_didEnd(false)
+ , m_didCreateLayoutState(false)
+ {
+ push(root, offset, pageHeight, pageHeightChanged, colInfo);
+ }
+
+ // ctor to maybe push later
+ LayoutStateMaintainer(RenderView* view)
+ : m_view(view)
+ , m_disabled(false)
+ , m_didStart(false)
+ , m_didEnd(false)
+ , m_didCreateLayoutState(false)
+ {
+ }
+
+ ~LayoutStateMaintainer()
+ {
+ ASSERT(m_didStart == m_didEnd); // if this fires, it means that someone did a push(), but forgot to pop().
+ }
+
+ void push(RenderBox* root, IntSize offset, int pageHeight = 0, bool pageHeightChanged = false, ColumnInfo* colInfo = 0)
+ {
+ ASSERT(!m_didStart);
+ // We push state even if disabled, because we still need to store layoutDelta
+ m_didCreateLayoutState = m_view->pushLayoutState(root, offset, pageHeight, pageHeightChanged, colInfo);
+ if (m_disabled && m_didCreateLayoutState)
+ m_view->disableLayoutState();
+ m_didStart = true;
+ }
+
+ void pop()
+ {
+ if (m_didStart) {
+ ASSERT(!m_didEnd);
+ if (m_didCreateLayoutState) {
+ m_view->popLayoutState();
+ if (m_disabled)
+ m_view->enableLayoutState();
+ }
+
+ m_didEnd = true;
+ }
+ }
+
+ bool didPush() const { return m_didStart; }
+
+private:
+ RenderView* m_view;
+ bool m_disabled : 1; // true if the offset and clip part of layoutState is disabled
+ bool m_didStart : 1; // true if we did a push or disable
+ bool m_didEnd : 1; // true if we popped or re-enabled
+ bool m_didCreateLayoutState : 1; // true if we actually made a layout state.
+};
+
+} // namespace WebCore
+
+#endif // RenderView_h
diff --git a/Source/WebCore/rendering/RenderWidget.cpp b/Source/WebCore/rendering/RenderWidget.cpp
new file mode 100644
index 0000000..152bb2f
--- /dev/null
+++ b/Source/WebCore/rendering/RenderWidget.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2006, 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 "RenderWidget.h"
+
+#include "AXObjectCache.h"
+#include "AnimationController.h"
+#include "GraphicsContext.h"
+#include "HitTestResult.h"
+#include "RenderCounter.h"
+#include "RenderLayer.h"
+#include "RenderView.h"
+#include "RenderWidgetProtector.h"
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "RenderLayerBacking.h"
+#endif
+
+using namespace std;
+
+namespace WebCore {
+
+static HashMap<const Widget*, RenderWidget*>& widgetRendererMap()
+{
+ static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>;
+ return *staticWidgetRendererMap;
+}
+
+static size_t widgetHierarchyUpdateSuspendCount;
+
+typedef HashMap<RefPtr<Widget>, FrameView*> WidgetToParentMap;
+
+static WidgetToParentMap& widgetNewParentMap()
+{
+ DEFINE_STATIC_LOCAL(WidgetToParentMap, map, ());
+ return map;
+}
+
+void RenderWidget::suspendWidgetHierarchyUpdates()
+{
+ widgetHierarchyUpdateSuspendCount++;
+}
+
+void RenderWidget::resumeWidgetHierarchyUpdates()
+{
+ ASSERT(widgetHierarchyUpdateSuspendCount);
+ if (widgetHierarchyUpdateSuspendCount == 1) {
+ WidgetToParentMap map = widgetNewParentMap();
+ widgetNewParentMap().clear();
+ WidgetToParentMap::iterator end = map.end();
+ for (WidgetToParentMap::iterator it = map.begin(); it != end; ++it) {
+ Widget* child = it->first.get();
+ ScrollView* currentParent = child->parent();
+ FrameView* newParent = it->second;
+ if (newParent != currentParent) {
+ if (currentParent)
+ currentParent->removeChild(child);
+ if (newParent)
+ newParent->addChild(child);
+ }
+ }
+ }
+ widgetHierarchyUpdateSuspendCount--;
+}
+
+static void moveWidgetToParentSoon(Widget* child, FrameView* parent)
+{
+ if (!widgetHierarchyUpdateSuspendCount) {
+ if (parent)
+ parent->addChild(child);
+ else
+ child->removeFromParent();
+ return;
+ }
+ widgetNewParentMap().set(child, parent);
+}
+
+RenderWidget::RenderWidget(Node* node)
+ : RenderReplaced(node)
+ , m_widget(0)
+ , m_frameView(node->document()->view())
+ // Reference counting is used to prevent the widget from being
+ // destroyed while inside the Widget code, which might not be
+ // able to handle that.
+ , m_refCount(1)
+{
+ view()->addWidget(this);
+}
+
+void RenderWidget::destroy()
+{
+ // We can't call the base class's destroy because we don't
+ // want to unconditionally delete ourselves (we're ref-counted).
+ // So the code below includes copied and pasted contents of
+ // both RenderBox::destroy() and RenderObject::destroy().
+ // Fix originally made for <rdar://problem/4228818>.
+
+ animation()->cancelAnimations(this);
+
+ if (RenderView* v = view())
+ v->removeWidget(this);
+
+ if (m_hasCounterNodeMap)
+ RenderCounter::destroyCounterNodes(this);
+
+ if (AXObjectCache::accessibilityEnabled()) {
+ document()->axObjectCache()->childrenChanged(this->parent());
+ document()->axObjectCache()->remove(this);
+ }
+ remove();
+
+ setWidget(0);
+
+ // removes from override size map
+ if (hasOverrideSize())
+ setOverrideSize(-1);
+
+ if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent()))
+ RenderBlock::removePercentHeightDescendant(this);
+
+ if (hasLayer()) {
+ layer()->clearClipRects();
+ setHasLayer(false);
+ destroyLayer();
+ }
+
+ // Grab the arena from node()->document()->renderArena() before clearing the node pointer.
+ // Clear the node before deref-ing, as this may be deleted when deref is called.
+ RenderArena* arena = renderArena();
+ setNode(0);
+ deref(arena);
+}
+
+RenderWidget::~RenderWidget()
+{
+ ASSERT(m_refCount <= 0);
+ clearWidget();
+}
+
+bool RenderWidget::setWidgetGeometry(const IntRect& frame)
+{
+ ASSERT(!widgetHierarchyUpdateSuspendCount);
+ if (!node())
+ return false;
+
+ IntRect clipRect = enclosingLayer()->childrenClipRect();
+ bool clipChanged = m_clipRect != clipRect;
+ bool boundsChanged = m_widget->frameRect() != frame;
+
+ if (!boundsChanged && !clipChanged)
+ return false;
+
+ m_clipRect = clipRect;
+
+ RenderWidgetProtector protector(this);
+ RefPtr<Node> protectedNode(node());
+ m_widget->setFrameRect(frame);
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (hasLayer() && layer()->isComposited())
+ layer()->backing()->updateAfterWidgetResize();
+#endif
+
+ return boundsChanged;
+}
+
+void RenderWidget::setWidget(PassRefPtr<Widget> widget)
+{
+ if (widget == m_widget)
+ return;
+
+ if (m_widget) {
+ moveWidgetToParentSoon(m_widget.get(), 0);
+ widgetRendererMap().remove(m_widget.get());
+ clearWidget();
+ }
+ m_widget = widget;
+ if (m_widget) {
+ widgetRendererMap().add(m_widget.get(), this);
+ // If we've already received a layout, apply the calculated space to the
+ // widget immediately, but we have to have really been fully constructed (with a non-null
+ // style pointer).
+ if (style()) {
+ if (!needsLayout())
+ setWidgetGeometry(absoluteContentBox());
+ if (style()->visibility() != VISIBLE)
+ m_widget->hide();
+ else
+ m_widget->show();
+ }
+ moveWidgetToParentSoon(m_widget.get(), m_frameView);
+ }
+}
+
+void RenderWidget::layout()
+{
+ ASSERT(needsLayout());
+
+ setNeedsLayout(false);
+}
+
+void RenderWidget::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderReplaced::styleDidChange(diff, oldStyle);
+ if (m_widget) {
+ if (style()->visibility() != VISIBLE)
+ m_widget->hide();
+ else
+ m_widget->show();
+ }
+}
+
+void RenderWidget::showSubstituteImage(PassRefPtr<Image> prpImage)
+{
+ m_substituteImage = prpImage;
+ repaint();
+}
+
+void RenderWidget::notifyWidget(WidgetNotification notification)
+{
+ if (m_widget)
+ m_widget->notifyWidget(notification);
+}
+
+void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ if (!shouldPaint(paintInfo, tx, ty))
+ return;
+
+ tx += x();
+ ty += y();
+
+ if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
+ paintBoxDecorations(paintInfo, tx, ty);
+
+ if (paintInfo.phase == PaintPhaseMask) {
+ paintMask(paintInfo, tx, ty);
+ return;
+ }
+
+ if (!m_frameView || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE)
+ return;
+
+#if PLATFORM(MAC)
+ if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
+ paintCustomHighlight(tx - x(), ty - y(), style()->highlight(), true);
+#endif
+
+ if (style()->hasBorderRadius()) {
+ IntRect borderRect = IntRect(tx, ty, width(), height());
+
+ if (borderRect.isEmpty())
+ return;
+
+ // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
+ paintInfo.context->save();
+
+ IntSize topLeft, topRight, bottomLeft, bottomRight;
+ style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
+
+ paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
+ }
+
+ if (m_widget) {
+ // Tell the widget to paint now. This is the only time the widget is allowed
+ // to paint itself. That way it will composite properly with z-indexed layers.
+ if (m_substituteImage)
+ paintInfo.context->drawImage(m_substituteImage.get(), style()->colorSpace(), m_widget->frameRect());
+ else {
+ IntPoint widgetLocation = m_widget->frameRect().location();
+ IntPoint paintLocation(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop());
+ IntRect paintRect = paintInfo.rect;
+
+ IntSize paintOffset = paintLocation - widgetLocation;
+ // When painting widgets into compositing layers, tx and ty are relative to the enclosing compositing layer,
+ // not the root. In this case, shift the CTM and adjust the paintRect to be root-relative to fix plug-in drawing.
+ if (!paintOffset.isZero()) {
+ paintInfo.context->translate(paintOffset);
+ paintRect.move(-paintOffset);
+ }
+ m_widget->paint(paintInfo.context, paintRect);
+
+ if (!paintOffset.isZero())
+ paintInfo.context->translate(-paintOffset);
+ }
+
+ if (m_widget->isFrameView()) {
+ FrameView* frameView = static_cast<FrameView*>(m_widget.get());
+ bool runOverlapTests = !frameView->useSlowRepaintsIfNotOverlapped() || frameView->hasCompositedContentIncludingDescendants();
+ if (paintInfo.overlapTestRequests && runOverlapTests) {
+ ASSERT(!paintInfo.overlapTestRequests->contains(this));
+ paintInfo.overlapTestRequests->set(this, m_widget->frameRect());
+ }
+ }
+ }
+
+ if (style()->hasBorderRadius())
+ paintInfo.context->restore();
+
+ // Paint a partially transparent wash over selected widgets.
+ if (isSelected() && !document()->printing()) {
+ // FIXME: selectionRect() is in absolute, not painting coordinates.
+ paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace());
+ }
+}
+
+void RenderWidget::setOverlapTestResult(bool isOverlapped)
+{
+ ASSERT(m_widget);
+ ASSERT(m_widget->isFrameView());
+ static_cast<FrameView*>(m_widget.get())->setIsOverlapped(isOverlapped);
+}
+
+void RenderWidget::deref(RenderArena *arena)
+{
+ if (--m_refCount <= 0)
+ arenaDelete(arena, this);
+}
+
+void RenderWidget::updateWidgetPosition()
+{
+ if (!m_widget || !node()) // Check the node in case destroy() has been called.
+ return;
+
+ // FIXME: This doesn't work correctly with transforms.
+ FloatPoint absPos = localToAbsolute();
+ absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop());
+
+ int w = width() - borderAndPaddingWidth();
+ int h = height() - borderAndPaddingHeight();
+
+ bool boundsChanged = setWidgetGeometry(IntRect(absPos.x(), absPos.y(), w, h));
+
+ // if the frame bounds got changed, or if view needs layout (possibly indicating
+ // content size is wrong) we have to do a layout to set the right widget size
+ if (m_widget && m_widget->isFrameView()) {
+ FrameView* frameView = static_cast<FrameView*>(m_widget.get());
+ // Check the frame's page to make sure that the frame isn't in the process of being destroyed.
+ if ((boundsChanged || frameView->needsLayout()) && frameView->frame()->page())
+ frameView->layout();
+ }
+}
+
+void RenderWidget::widgetPositionsUpdated()
+{
+ if (!m_widget)
+ return;
+ m_widget->widgetPositionsUpdated();
+}
+
+IntRect RenderWidget::windowClipRect() const
+{
+ if (!m_frameView)
+ return IntRect();
+
+ return intersection(m_frameView->contentsToWindow(m_clipRect), m_frameView->windowClipRect());
+}
+
+void RenderWidget::setSelectionState(SelectionState state)
+{
+ if (selectionState() != state) {
+ RenderReplaced::setSelectionState(state);
+ if (m_widget)
+ m_widget->setIsSelected(isSelected());
+ }
+}
+
+void RenderWidget::clearWidget()
+{
+ m_widget = 0;
+}
+
+RenderWidget* RenderWidget::find(const Widget* widget)
+{
+ return widgetRendererMap().get(widget);
+}
+
+bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action)
+{
+ bool hadResult = result.innerNode();
+ bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action);
+
+ // Check to see if we are really over the widget itself (and not just in the border/padding area).
+ if ((inside || result.isRectBasedTest()) && !hadResult && result.innerNode() == node())
+ result.setIsOverWidget(contentBoxRect().contains(result.localPoint()));
+ return inside;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RenderWidget.h b/Source/WebCore/rendering/RenderWidget.h
new file mode 100644
index 0000000..d2ec096
--- /dev/null
+++ b/Source/WebCore/rendering/RenderWidget.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 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 RenderWidget_h
+#define RenderWidget_h
+
+#include "OverlapTestRequestClient.h"
+#include "RenderReplaced.h"
+#include "Widget.h"
+
+namespace WebCore {
+
+class RenderWidget : public RenderReplaced, private OverlapTestRequestClient {
+public:
+ virtual ~RenderWidget();
+
+ Widget* widget() const { return m_widget.get(); }
+ virtual void setWidget(PassRefPtr<Widget>);
+
+ static RenderWidget* find(const Widget*);
+
+ void updateWidgetPosition();
+ void widgetPositionsUpdated();
+ IntRect windowClipRect() const;
+
+ void showSubstituteImage(PassRefPtr<Image>);
+
+ void notifyWidget(WidgetNotification);
+
+ static void suspendWidgetHierarchyUpdates();
+ static void resumeWidgetHierarchyUpdates();
+
+ RenderArena* ref() { ++m_refCount; return renderArena(); }
+ void deref(RenderArena*);
+
+protected:
+ RenderWidget(Node*);
+
+ FrameView* frameView() const { return m_frameView; }
+
+ void clearWidget();
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void layout();
+ virtual void paint(PaintInfo&, int x, int y);
+
+private:
+ virtual bool isWidget() const { return true; }
+
+ virtual void destroy();
+ virtual void setSelectionState(SelectionState);
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction);
+ virtual void setOverlapTestResult(bool);
+
+ bool setWidgetGeometry(const IntRect&);
+
+ RefPtr<Widget> m_widget;
+ RefPtr<Image> m_substituteImage;
+ FrameView* m_frameView;
+ IntRect m_clipRect; // The rectangle needs to remain correct after scrolling, so it is stored in content view coordinates, and not clipped to window.
+ int m_refCount;
+};
+
+inline RenderWidget* toRenderWidget(RenderObject* object)
+{
+ ASSERT(!object || object->isWidget());
+ return static_cast<RenderWidget*>(object);
+}
+
+inline const RenderWidget* toRenderWidget(const RenderObject* object)
+{
+ ASSERT(!object || object->isWidget());
+ return static_cast<const RenderWidget*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderWidget(const RenderWidget*);
+
+} // namespace WebCore
+
+#endif // RenderWidget_h
diff --git a/Source/WebCore/rendering/RenderWidgetProtector.h b/Source/WebCore/rendering/RenderWidgetProtector.h
new file mode 100644
index 0000000..788304c
--- /dev/null
+++ b/Source/WebCore/rendering/RenderWidgetProtector.h
@@ -0,0 +1,53 @@
+/*
+ * 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 RenderWidgetProtector_h
+#define RenderWidgetProtector_h
+
+#include "RenderWidget.h"
+
+namespace WebCore {
+
+class RenderWidgetProtector : private Noncopyable {
+public:
+ RenderWidgetProtector(RenderWidget* object)
+ : m_object(object)
+ , m_arena(object->ref())
+ {
+ }
+
+ ~RenderWidgetProtector()
+ {
+ m_object->deref(m_arena);
+ }
+
+private:
+ RenderWidget* m_object;
+ RenderArena* m_arena;
+};
+
+}
+
+#endif // RenderWidgetProtector_h
diff --git a/Source/WebCore/rendering/RenderWordBreak.cpp b/Source/WebCore/rendering/RenderWordBreak.cpp
new file mode 100644
index 0000000..a620560
--- /dev/null
+++ b/Source/WebCore/rendering/RenderWordBreak.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 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 "RenderWordBreak.h"
+
+#include "HTMLElement.h"
+
+namespace WebCore {
+
+RenderWordBreak::RenderWordBreak(HTMLElement* element)
+ : RenderText(element, StringImpl::empty())
+{
+}
+
+const char* RenderWordBreak::renderName() const
+{
+ return "RenderWordBreak";
+}
+
+bool RenderWordBreak::isWordBreak() const
+{
+ return true;
+}
+
+}
diff --git a/Source/WebCore/rendering/RenderWordBreak.h b/Source/WebCore/rendering/RenderWordBreak.h
new file mode 100644
index 0000000..db31eec
--- /dev/null
+++ b/Source/WebCore/rendering/RenderWordBreak.h
@@ -0,0 +1,46 @@
+/*
+ * 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 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 RenderWordBreak_h
+#define RenderWordBreak_h
+
+#include "RenderText.h"
+
+namespace WebCore {
+
+class HTMLElement;
+
+class RenderWordBreak : public RenderText {
+public:
+ explicit RenderWordBreak(HTMLElement*);
+
+ virtual const char* renderName() const;
+ virtual bool isWordBreak() const;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/RenderingAllInOne.cpp b/Source/WebCore/rendering/RenderingAllInOne.cpp
new file mode 100644
index 0000000..37ba704
--- /dev/null
+++ b/Source/WebCore/rendering/RenderingAllInOne.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+
+#include "AutoTableLayout.cpp"
+#include "BidiRun.cpp"
+#include "CounterNode.cpp"
+#include "EllipsisBox.cpp"
+#include "FixedTableLayout.cpp"
+#include "HitTestResult.cpp"
+#include "InlineBox.cpp"
+#include "InlineFlowBox.cpp"
+#include "InlineTextBox.cpp"
+#include "LayoutState.cpp"
+#include "MediaControlElements.cpp"
+#include "PointerEventsHitRules.cpp"
+#include "RenderApplet.cpp"
+#include "RenderArena.cpp"
+#include "RenderBR.cpp"
+#include "RenderBlock.cpp"
+#include "RenderBlockLineLayout.cpp"
+#include "RenderBox.cpp"
+#include "RenderBoxModelObject.cpp"
+#include "RenderButton.cpp"
+#include "RenderCounter.cpp"
+#include "RenderDataGrid.cpp"
+#include "RenderDetails.cpp"
+#include "RenderDetailsMarker.cpp"
+#include "RenderEmbeddedObject.cpp"
+#include "RenderFieldset.cpp"
+#include "RenderFileUploadControl.cpp"
+#include "RenderFlexibleBox.cpp"
+#include "RenderForeignObject.cpp"
+#include "RenderFrame.cpp"
+#include "RenderFrameBase.cpp"
+#include "RenderFrameSet.cpp"
+#include "RenderHTMLCanvas.cpp"
+#include "RenderIFrame.cpp"
+#include "RenderImage.cpp"
+#include "RenderImageResource.cpp"
+#include "RenderImageResourceStyleImage.cpp"
+#include "RenderIndicator.cpp"
+#include "RenderInline.cpp"
+#include "RenderLayer.cpp"
+#include "RenderLayerCompositor.cpp"
+#include "RenderLineBoxList.cpp"
+#include "RenderListBox.cpp"
+#include "RenderListItem.cpp"
+#include "RenderListMarker.cpp"
+#include "RenderMarquee.cpp"
+#include "RenderMedia.cpp"
+#include "RenderMediaControls.cpp"
+#include "RenderMenuList.cpp"
+#include "RenderMeter.cpp"
+#include "RenderObject.cpp"
+#include "RenderObjectChildList.cpp"
+#include "RenderPart.cpp"
+#include "RenderProgress.cpp"
+#include "RenderReplaced.cpp"
+#include "RenderReplica.cpp"
+#include "RenderRuby.cpp"
+#include "RenderRubyBase.cpp"
+#include "RenderRubyRun.cpp"
+#include "RenderRubyText.cpp"
+#include "RenderScrollbar.cpp"
+#include "RenderScrollbarPart.cpp"
+#include "RenderScrollbarTheme.cpp"
+#include "RenderSlider.cpp"
+#include "RenderSummary.cpp"
+#include "RenderTable.cpp"
+#include "RenderTableCell.cpp"
+#include "RenderTableCol.cpp"
+#include "RenderTableRow.cpp"
+#include "RenderTableSection.cpp"
+#include "RenderText.cpp"
+#include "RenderTextControl.cpp"
+#include "RenderTextControlMultiLine.cpp"
+#include "RenderTextControlSingleLine.cpp"
+#include "RenderTextFragment.cpp"
+#include "RenderTheme.cpp"
+#include "RenderThemeWin.cpp"
+#include "RenderTreeAsText.cpp"
+#include "RenderVideo.cpp"
+#include "RenderView.cpp"
+#include "RenderWidget.cpp"
+#include "RenderWordBreak.cpp"
+#include "RootInlineBox.cpp"
+#include "ScrollBehavior.cpp"
+#include "ShadowElement.cpp"
+#include "TextControlInnerElements.cpp"
+#include "TransformState.cpp"
+#include "break_lines.cpp"
diff --git a/Source/WebCore/rendering/RootInlineBox.cpp b/Source/WebCore/rendering/RootInlineBox.cpp
new file mode 100644
index 0000000..710224e
--- /dev/null
+++ b/Source/WebCore/rendering/RootInlineBox.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2003, 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 "RootInlineBox.h"
+
+#include "BidiResolver.h"
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Document.h"
+#include "EllipsisBox.h"
+#include "Frame.h"
+#include "GraphicsContext.h"
+#include "HitTestResult.h"
+#include "Page.h"
+#include "RenderArena.h"
+#include "RenderBlock.h"
+
+using namespace std;
+
+namespace WebCore {
+
+typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap;
+static EllipsisBoxMap* gEllipsisBoxMap = 0;
+
+RootInlineBox::RootInlineBox(RenderBlock* block)
+ : InlineFlowBox(block)
+ , m_lineBreakObj(0)
+ , m_lineBreakPos(0)
+ , m_lineTop(0)
+ , m_lineBottom(0)
+ , m_paginationStrut(0)
+ , m_blockLogicalHeight(0)
+ , m_baselineType(AlphabeticBaseline)
+ , m_hasAnnotationsBefore(false)
+ , m_hasAnnotationsAfter(false)
+{
+ setIsHorizontal(block->style()->isHorizontalWritingMode());
+}
+
+
+void RootInlineBox::destroy(RenderArena* arena)
+{
+ detachEllipsisBox(arena);
+ InlineFlowBox::destroy(arena);
+}
+
+void RootInlineBox::detachEllipsisBox(RenderArena* arena)
+{
+ if (hasEllipsisBox()) {
+ EllipsisBox* box = gEllipsisBoxMap->take(this);
+ box->setParent(0);
+ box->destroy(arena);
+ setHasEllipsisBox(false);
+ }
+}
+
+RenderLineBoxList* RootInlineBox::rendererLineBoxes() const
+{
+ return block()->lineBoxes();
+}
+
+void RootInlineBox::clearTruncation()
+{
+ if (hasEllipsisBox()) {
+ detachEllipsisBox(renderer()->renderArena());
+ InlineFlowBox::clearTruncation();
+ }
+}
+
+bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
+{
+ // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room.
+ int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
+ if (logicalWidth() - delta < ellipsisWidth)
+ return false;
+
+ // Next iterate over all the line boxes on the line. If we find a replaced element that intersects
+ // then we refuse to accommodate the ellipsis. Otherwise we're ok.
+ return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth);
+}
+
+void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth,
+ InlineBox* markupBox)
+{
+ // Create an ellipsis box.
+ EllipsisBox* ellipsisBox = new (renderer()->renderArena()) EllipsisBox(renderer(), ellipsisStr, this,
+ ellipsisWidth - (markupBox ? markupBox->logicalWidth() : 0), logicalHeight(),
+ y(), !prevRootBox(), isHorizontal(), markupBox);
+
+ if (!gEllipsisBoxMap)
+ gEllipsisBoxMap = new EllipsisBoxMap();
+ gEllipsisBoxMap->add(this, ellipsisBox);
+ setHasEllipsisBox(true);
+
+ // FIXME: Do we need an RTL version of this?
+ if (ltr && (x() + logicalWidth() + ellipsisWidth) <= blockRightEdge) {
+ ellipsisBox->m_x = x() + logicalWidth();
+ return;
+ }
+
+ // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL)
+ // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being
+ // truncated).
+ bool foundBox = false;
+ ellipsisBox->m_x = placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, foundBox);
+}
+
+int RootInlineBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox)
+{
+ int result = InlineFlowBox::placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, foundBox);
+ if (result == -1)
+ result = ltr ? blockRightEdge - ellipsisWidth : blockLeftEdge;
+ return result;
+}
+
+void RootInlineBox::paintEllipsisBox(PaintInfo& paintInfo, int tx, int ty) const
+{
+ if (hasEllipsisBox() && paintInfo.shouldPaintWithinRoot(renderer()) && renderer()->style()->visibility() == VISIBLE
+ && paintInfo.phase == PaintPhaseForeground)
+ ellipsisBox()->paint(paintInfo, tx, ty);
+}
+
+#if PLATFORM(MAC)
+
+void RootInlineBox::addHighlightOverflow()
+{
+ Frame* frame = renderer()->frame();
+ if (!frame)
+ return;
+ Page* page = frame->page();
+ if (!page)
+ return;
+
+ // Highlight acts as a selection inflation.
+ FloatRect rootRect(0, selectionTop(), logicalWidth(), selectionHeight());
+ IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(renderer()->node(), renderer()->style()->highlight(), rootRect));
+ setOverflowFromLogicalRects(inflatedRect, inflatedRect);
+}
+
+void RootInlineBox::paintCustomHighlight(PaintInfo& paintInfo, int tx, int ty, const AtomicString& highlightType)
+{
+ if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground)
+ return;
+
+ Frame* frame = renderer()->frame();
+ if (!frame)
+ return;
+ Page* page = frame->page();
+ if (!page)
+ return;
+
+ // Get the inflated rect so that we can properly hit test.
+ FloatRect rootRect(tx + x(), ty + selectionTop(), logicalWidth(), selectionHeight());
+ FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(renderer()->node(), highlightType, rootRect);
+ if (inflatedRect.intersects(paintInfo.rect))
+ page->chrome()->client()->paintCustomHighlight(renderer()->node(), highlightType, rootRect, rootRect, false, true);
+}
+
+#endif
+
+void RootInlineBox::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+ InlineFlowBox::paint(paintInfo, tx, ty);
+ paintEllipsisBox(paintInfo, tx, ty);
+#if PLATFORM(MAC)
+ RenderStyle* styleToUse = renderer()->style(m_firstLine);
+ if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
+ paintCustomHighlight(paintInfo, tx, ty, styleToUse->highlight());
+#endif
+}
+
+bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
+{
+ if (hasEllipsisBox() && visibleToHitTesting()) {
+ if (ellipsisBox()->nodeAtPoint(request, result, x, y, tx, ty)) {
+ renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
+ return true;
+ }
+ }
+ return InlineFlowBox::nodeAtPoint(request, result, x, y, tx, ty);
+}
+
+void RootInlineBox::adjustPosition(int dx, int dy)
+{
+ InlineFlowBox::adjustPosition(dx, dy);
+ int blockDirectionDelta = isHorizontal() ? dy : dx;
+ m_lineTop += blockDirectionDelta;
+ m_lineBottom += blockDirectionDelta;
+ m_blockLogicalHeight += blockDirectionDelta;
+}
+
+void RootInlineBox::childRemoved(InlineBox* box)
+{
+ if (box->renderer() == m_lineBreakObj)
+ setLineBreakInfo(0, 0, BidiStatus());
+
+ for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->renderer(); prev = prev->prevRootBox()) {
+ prev->setLineBreakInfo(0, 0, BidiStatus());
+ prev->markDirty();
+ }
+}
+
+int RootInlineBox::alignBoxesInBlockDirection(int heightOfBlock, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache)
+{
+#if ENABLE(SVG)
+ // SVG will handle vertical alignment on its own.
+ if (isSVGRootInlineBox())
+ return 0;
+#endif
+
+ int maxPositionTop = 0;
+ int maxPositionBottom = 0;
+ int maxAscent = 0;
+ int maxDescent = 0;
+ bool setMaxAscent = false;
+ bool setMaxDescent = false;
+
+ // Figure out if we're in no-quirks mode.
+ bool noQuirksMode = renderer()->document()->inNoQuirksMode();
+
+ m_baselineType = requiresIdeographicBaseline(textBoxDataMap) ? IdeographicBaseline : AlphabeticBaseline;
+
+ computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, setMaxAscent, setMaxDescent, noQuirksMode,
+ textBoxDataMap, baselineType(), verticalPositionCache);
+
+ if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom))
+ adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
+
+ int maxHeight = maxAscent + maxDescent;
+ int lineTop = heightOfBlock;
+ int lineBottom = heightOfBlock;
+ int lineTopIncludingMargins = heightOfBlock;
+ int lineBottomIncludingMargins = heightOfBlock;
+ bool setLineTop = false;
+ bool hasAnnotationsBefore = false;
+ bool hasAnnotationsAfter = false;
+ placeBoxesInBlockDirection(heightOfBlock, maxHeight, maxAscent, noQuirksMode, lineTop, lineBottom, setLineTop,
+ lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType());
+ m_hasAnnotationsBefore = hasAnnotationsBefore;
+ m_hasAnnotationsAfter = hasAnnotationsAfter;
+ setLineTopBottomPositions(lineTop, lineBottom);
+
+ int annotationsAdjustment = beforeAnnotationsAdjustment();
+ if (annotationsAdjustment) {
+ // FIXME: Need to handle pagination here. We might have to move to the next page/column as a result of the
+ // ruby expansion.
+ adjustBlockDirectionPosition(annotationsAdjustment);
+ heightOfBlock += annotationsAdjustment;
+ }
+
+ // Detect integer overflow.
+ if (heightOfBlock > numeric_limits<int>::max() - maxHeight)
+ return numeric_limits<int>::max();
+
+ return heightOfBlock + maxHeight;
+}
+
+int RootInlineBox::beforeAnnotationsAdjustment() const
+{
+ int result = 0;
+
+ if (!renderer()->style()->isFlippedLinesWritingMode()) {
+ // Annotations under the previous line may push us down.
+ if (prevRootBox() && prevRootBox()->hasAnnotationsAfter())
+ result = prevRootBox()->computeUnderAnnotationAdjustment(lineTop());
+
+ if (!hasAnnotationsBefore())
+ return result;
+
+ // Annotations over this line may push us further down.
+ int highestAllowedPosition = prevRootBox() ? min(prevRootBox()->lineBottom(), lineTop()) + result : block()->borderBefore();
+ result = computeOverAnnotationAdjustment(highestAllowedPosition);
+ } else {
+ // Annotations under this line may push us up.
+ if (hasAnnotationsBefore())
+ result = computeUnderAnnotationAdjustment(prevRootBox() ? prevRootBox()->lineBottom() : block()->borderBefore());
+
+ if (!prevRootBox() || !prevRootBox()->hasAnnotationsAfter())
+ return result;
+
+ // We have to compute the expansion for annotations over the previous line to see how much we should move.
+ int lowestAllowedPosition = max(prevRootBox()->lineBottom(), lineTop()) - result;
+ result = prevRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
+ }
+
+ return result;
+}
+
+GapRects RootInlineBox::lineSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock,
+ int selTop, int selHeight, const PaintInfo* paintInfo)
+{
+ RenderObject::SelectionState lineState = selectionState();
+
+ bool leftGap, rightGap;
+ block()->getSelectionGapInfo(lineState, leftGap, rightGap);
+
+ GapRects result;
+
+ InlineBox* firstBox = firstSelectedBox();
+ InlineBox* lastBox = lastSelectedBox();
+ if (leftGap)
+ result.uniteLeft(block()->logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
+ firstBox->parent()->renderer(), firstBox->logicalLeft(), selTop, selHeight, paintInfo));
+ if (rightGap)
+ result.uniteRight(block()->logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock,
+ lastBox->parent()->renderer(), lastBox->logicalRight(), selTop, selHeight, paintInfo));
+
+ // When dealing with bidi text, a non-contiguous selection region is possible.
+ // e.g. The logical text aaaAAAbbb (capitals denote RTL text and non-capitals LTR) is layed out
+ // visually as 3 text runs |aaa|bbb|AAA| if we select 4 characters from the start of the text the
+ // selection will look like (underline denotes selection):
+ // |aaa|bbb|AAA|
+ // ___ _
+ // We can see that the |bbb| run is not part of the selection while the runs around it are.
+ if (firstBox && firstBox != lastBox) {
+ // Now fill in any gaps on the line that occurred between two selected elements.
+ int lastLogicalLeft = firstBox->logicalRight();
+ bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone;
+ for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
+ if (box->selectionState() != RenderObject::SelectionNone) {
+ IntRect logicalRect(lastLogicalLeft, selTop, box->logicalLeft() - lastLogicalLeft, selHeight);
+ logicalRect.move(renderer()->style()->isHorizontalWritingMode() ? offsetFromRootBlock : IntSize(offsetFromRootBlock.height(), offsetFromRootBlock.width()));
+ IntRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, logicalRect);
+ if (isPreviousBoxSelected && gapRect.width() > 0 && gapRect.height() > 0) {
+ if (paintInfo && box->parent()->renderer()->style()->visibility() == VISIBLE)
+ paintInfo->context->fillRect(gapRect, box->parent()->renderer()->selectionBackgroundColor(), box->parent()->renderer()->style()->colorSpace());
+ // VisibleSelection may be non-contiguous, see comment above.
+ result.uniteCenter(gapRect);
+ }
+ lastLogicalLeft = box->logicalRight();
+ }
+ if (box == lastBox)
+ break;
+ isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone;
+ }
+ }
+
+ return result;
+}
+
+void RootInlineBox::setHasSelectedChildren(bool b)
+{
+ if (m_hasSelectedChildren == b)
+ return;
+ m_hasSelectedChildren = b;
+}
+
+RenderObject::SelectionState RootInlineBox::selectionState()
+{
+ // Walk over all of the selected boxes.
+ RenderObject::SelectionState state = RenderObject::SelectionNone;
+ for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
+ RenderObject::SelectionState boxState = box->selectionState();
+ if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
+ (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
+ state = RenderObject::SelectionBoth;
+ else if (state == RenderObject::SelectionNone ||
+ ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
+ (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
+ state = boxState;
+ if (state == RenderObject::SelectionBoth)
+ break;
+ }
+
+ return state;
+}
+
+InlineBox* RootInlineBox::firstSelectedBox()
+{
+ for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
+ if (box->selectionState() != RenderObject::SelectionNone)
+ return box;
+ }
+
+ return 0;
+}
+
+InlineBox* RootInlineBox::lastSelectedBox()
+{
+ for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) {
+ if (box->selectionState() != RenderObject::SelectionNone)
+ return box;
+ }
+
+ return 0;
+}
+
+int RootInlineBox::selectionTop() const
+{
+ int selectionTop = m_lineTop;
+
+ if (m_hasAnnotationsBefore)
+ selectionTop -= !renderer()->style()->isFlippedLinesWritingMode() ? computeOverAnnotationAdjustment(m_lineTop) : computeUnderAnnotationAdjustment(m_lineTop);
+
+ if (renderer()->style()->isFlippedLinesWritingMode())
+ return selectionTop;
+
+ int prevBottom = prevRootBox() ? prevRootBox()->selectionBottom() : block()->borderBefore() + block()->paddingBefore();
+ if (prevBottom < selectionTop && block()->containsFloats()) {
+ // This line has actually been moved further down, probably from a large line-height, but possibly because the
+ // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous
+ // line's bottom if the offsets are greater on both sides.
+ int prevLeft = block()->logicalLeftOffsetForLine(prevBottom, false);
+ int prevRight = block()->logicalRightOffsetForLine(prevBottom, false);
+ int newLeft = block()->logicalLeftOffsetForLine(selectionTop, false);
+ int newRight = block()->logicalRightOffsetForLine(selectionTop, false);
+ if (prevLeft > newLeft || prevRight < newRight)
+ return selectionTop;
+ }
+
+ return prevBottom;
+}
+
+int RootInlineBox::selectionBottom() const
+{
+ int selectionBottom = m_lineBottom;
+
+ if (m_hasAnnotationsAfter)
+ selectionBottom += !renderer()->style()->isFlippedLinesWritingMode() ? computeUnderAnnotationAdjustment(m_lineBottom) : computeOverAnnotationAdjustment(m_lineBottom);
+
+ if (!renderer()->style()->isFlippedLinesWritingMode() || !nextRootBox())
+ return selectionBottom;
+
+ int nextTop = nextRootBox()->selectionTop();
+ if (nextTop > selectionBottom && block()->containsFloats()) {
+ // The next line has actually been moved further over, probably from a large line-height, but possibly because the
+ // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the next
+ // line's top if the offsets are greater on both sides.
+ int nextLeft = block()->logicalLeftOffsetForLine(nextTop, false);
+ int nextRight = block()->logicalRightOffsetForLine(nextTop, false);
+ int newLeft = block()->logicalLeftOffsetForLine(selectionBottom, false);
+ int newRight = block()->logicalRightOffsetForLine(selectionBottom, false);
+ if (nextLeft > newLeft || nextRight < newRight)
+ return selectionBottom;
+ }
+
+ return nextTop;
+}
+
+RenderBlock* RootInlineBox::block() const
+{
+ return toRenderBlock(renderer());
+}
+
+static bool isEditableLeaf(InlineBox* leaf)
+{
+ return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->isContentEditable();
+}
+
+InlineBox* RootInlineBox::closestLeafChildForLogicalLeftPosition(int leftPosition, bool onlyEditableLeaves)
+{
+ InlineBox* firstLeaf = firstLeafChild();
+ InlineBox* lastLeaf = lastLeafChild();
+ if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
+ return firstLeaf;
+
+ // Avoid returning a list marker when possible.
+ if (leftPosition <= firstLeaf->logicalLeft() && !firstLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
+ // The leftPosition coordinate is less or equal to left edge of the firstLeaf.
+ // Return it.
+ return firstLeaf;
+
+ if (leftPosition >= lastLeaf->logicalRight() && !lastLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf)))
+ // The leftPosition coordinate is greater or equal to right edge of the lastLeaf.
+ // Return it.
+ return lastLeaf;
+
+ InlineBox* closestLeaf = 0;
+ for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
+ if (!leaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) {
+ closestLeaf = leaf;
+ if (leftPosition < leaf->logicalRight())
+ // The x coordinate is less than the right edge of the box.
+ // Return it.
+ return leaf;
+ }
+ }
+
+ return closestLeaf ? closestLeaf : lastLeaf;
+}
+
+BidiStatus RootInlineBox::lineBreakBidiStatus() const
+{
+ return BidiStatus(m_lineBreakBidiStatusEor, m_lineBreakBidiStatusLastStrong, m_lineBreakBidiStatusLast, m_lineBreakContext);
+}
+
+void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status)
+{
+ m_lineBreakObj = obj;
+ m_lineBreakPos = breakPos;
+ m_lineBreakBidiStatusEor = status.eor;
+ m_lineBreakBidiStatusLastStrong = status.lastStrong;
+ m_lineBreakBidiStatusLast = status.last;
+ m_lineBreakContext = status.context;
+}
+
+EllipsisBox* RootInlineBox::ellipsisBox() const
+{
+ if (!hasEllipsisBox())
+ return 0;
+ return gEllipsisBoxMap->get(this);
+}
+
+void RootInlineBox::removeLineBoxFromRenderObject()
+{
+ block()->lineBoxes()->removeLineBox(this);
+}
+
+void RootInlineBox::extractLineBoxFromRenderObject()
+{
+ block()->lineBoxes()->extractLineBox(this);
+}
+
+void RootInlineBox::attachLineBoxToRenderObject()
+{
+ block()->lineBoxes()->attachLineBox(this);
+}
+
+IntRect RootInlineBox::paddedLayoutOverflowRect(int endPadding) const
+{
+ IntRect lineLayoutOverflow = layoutOverflowRect();
+ if (!endPadding)
+ return lineLayoutOverflow;
+
+ if (isHorizontal()) {
+ if (isLeftToRightDirection())
+ lineLayoutOverflow.shiftRightEdgeTo(max(lineLayoutOverflow.right(), logicalRight() + endPadding));
+ else
+ lineLayoutOverflow.shiftLeftEdgeTo(min(lineLayoutOverflow.x(), logicalLeft() - endPadding));
+ } else {
+ if (isLeftToRightDirection())
+ lineLayoutOverflow.shiftBottomEdgeTo(max(lineLayoutOverflow.bottom(), logicalRight() + endPadding));
+ else
+ lineLayoutOverflow.shiftTopEdgeTo(min(lineLayoutOverflow.y(), logicalRight() - endPadding));
+ }
+
+ return lineLayoutOverflow;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/RootInlineBox.h b/Source/WebCore/rendering/RootInlineBox.h
new file mode 100644
index 0000000..7c4b15c
--- /dev/null
+++ b/Source/WebCore/rendering/RootInlineBox.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2003, 2006, 2007, 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 RootInlineBox_h
+#define RootInlineBox_h
+
+#include "BidiContext.h"
+#include "InlineFlowBox.h"
+
+namespace WebCore {
+
+class EllipsisBox;
+class HitTestResult;
+
+struct BidiStatus;
+struct GapRects;
+
+class RootInlineBox : public InlineFlowBox {
+public:
+ RootInlineBox(RenderBlock* block);
+
+ virtual void destroy(RenderArena*);
+
+ virtual bool isRootInlineBox() const { return true; }
+
+ void detachEllipsisBox(RenderArena*);
+
+ RootInlineBox* nextRootBox() const { return static_cast<RootInlineBox*>(m_nextLineBox); }
+ RootInlineBox* prevRootBox() const { return static_cast<RootInlineBox*>(m_prevLineBox); }
+
+ virtual void adjustPosition(int dx, int dy);
+
+ int lineTop() const { return m_lineTop; }
+ int lineBottom() const { return m_lineBottom; }
+
+ int paginationStrut() const { return m_paginationStrut; }
+ void setPaginationStrut(int s) { m_paginationStrut = s; }
+
+ int selectionTop() const;
+ int selectionBottom() const;
+ int selectionHeight() const { return max(0, selectionBottom() - selectionTop()); }
+
+ int alignBoxesInBlockDirection(int heightOfBlock, GlyphOverflowAndFallbackFontsMap&, VerticalPositionCache&);
+ void setLineTopBottomPositions(int top, int bottom);
+
+ virtual RenderLineBoxList* rendererLineBoxes() const;
+
+ RenderObject* lineBreakObj() const { return m_lineBreakObj; }
+ BidiStatus lineBreakBidiStatus() const;
+ void setLineBreakInfo(RenderObject*, unsigned breakPos, const BidiStatus&);
+
+ unsigned lineBreakPos() const { return m_lineBreakPos; }
+ void setLineBreakPos(unsigned p) { m_lineBreakPos = p; }
+
+ int blockLogicalHeight() const { return m_blockLogicalHeight; }
+ void setBlockLogicalHeight(int h) { m_blockLogicalHeight = h; }
+
+ bool endsWithBreak() const { return m_endsWithBreak; }
+ void setEndsWithBreak(bool b) { m_endsWithBreak = b; }
+
+ void childRemoved(InlineBox* box);
+
+ bool canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth);
+ void placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, InlineBox* markupBox = 0);
+ virtual int placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox);
+
+ EllipsisBox* ellipsisBox() const;
+
+ void paintEllipsisBox(PaintInfo&, int tx, int ty) const;
+ bool hitTestEllipsisBox(HitTestResult&, int x, int y, int tx, int ty, HitTestAction, bool);
+
+ virtual void clearTruncation();
+
+ virtual int baselinePosition(FontBaseline baselineType) const { return boxModelObject()->baselinePosition(baselineType, m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); }
+ virtual int lineHeight() const { return boxModelObject()->lineHeight(m_firstLine, isHorizontal() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); }
+
+#if PLATFORM(MAC)
+ void addHighlightOverflow();
+ void paintCustomHighlight(PaintInfo&, int tx, int ty, const AtomicString& highlightType);
+#endif
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int);
+
+ bool hasSelectedChildren() const { return m_hasSelectedChildren; }
+ void setHasSelectedChildren(bool);
+
+ virtual RenderObject::SelectionState selectionState();
+ InlineBox* firstSelectedBox();
+ InlineBox* lastSelectedBox();
+
+ GapRects lineSelectionGap(RenderBlock* rootBlock, const IntPoint& rootBlockPhysicalPosition, const IntSize& offsetFromRootBlock, int selTop, int selHeight, const PaintInfo*);
+
+ RenderBlock* block() const;
+
+ InlineBox* closestLeafChildForLogicalLeftPosition(int, bool onlyEditableLeaves = false);
+
+ Vector<RenderBox*>& floats()
+ {
+ ASSERT(!isDirty());
+ if (!m_floats)
+ m_floats= adoptPtr(new Vector<RenderBox*>);
+ return *m_floats;
+ }
+
+ Vector<RenderBox*>* floatsPtr() { ASSERT(!isDirty()); return m_floats.get(); }
+
+ virtual void extractLineBoxFromRenderObject();
+ virtual void attachLineBoxToRenderObject();
+ virtual void removeLineBoxFromRenderObject();
+
+ FontBaseline baselineType() const { return static_cast<FontBaseline>(m_baselineType); }
+
+ bool hasAnnotationsBefore() const { return m_hasAnnotationsBefore; }
+ bool hasAnnotationsAfter() const { return m_hasAnnotationsAfter; }
+
+ IntRect paddedLayoutOverflowRect(int endPadding) const;
+
+private:
+ bool hasEllipsisBox() const { return m_hasEllipsisBoxOrHyphen; }
+ void setHasEllipsisBox(bool hasEllipsisBox) { m_hasEllipsisBoxOrHyphen = hasEllipsisBox; }
+
+ int beforeAnnotationsAdjustment() const;
+
+ // Where this line ended. The exact object and the position within that object are stored so that
+ // we can create an InlineIterator beginning just after the end of this line.
+ RenderObject* m_lineBreakObj;
+ unsigned m_lineBreakPos;
+ RefPtr<BidiContext> m_lineBreakContext;
+
+ int m_lineTop;
+ int m_lineBottom;
+
+ int m_paginationStrut;
+
+ // Floats hanging off the line are pushed into this vector during layout. It is only
+ // good for as long as the line has not been marked dirty.
+ OwnPtr<Vector<RenderBox*> > m_floats;
+
+ // The logical height of the block at the end of this line. This is where the next line starts.
+ int m_blockLogicalHeight;
+
+ // Whether or not this line uses alphabetic or ideographic baselines by default.
+ unsigned m_baselineType : 1; // FontBaseline
+
+ // If the line contains any ruby runs, then this will be true.
+ bool m_hasAnnotationsBefore : 1;
+ bool m_hasAnnotationsAfter : 1;
+
+ WTF::Unicode::Direction m_lineBreakBidiStatusEor : 5;
+ WTF::Unicode::Direction m_lineBreakBidiStatusLastStrong : 5;
+ WTF::Unicode::Direction m_lineBreakBidiStatusLast : 5;
+};
+
+inline void RootInlineBox::setLineTopBottomPositions(int top, int bottom)
+{
+ m_lineTop = top;
+ m_lineBottom = bottom;
+}
+
+} // namespace WebCore
+
+#endif // RootInlineBox_h
diff --git a/Source/WebCore/rendering/SVGImageBufferTools.cpp b/Source/WebCore/rendering/SVGImageBufferTools.cpp
new file mode 100644
index 0000000..2dc0a6f
--- /dev/null
+++ b/Source/WebCore/rendering/SVGImageBufferTools.cpp
@@ -0,0 +1,128 @@
+/*
+ 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)
+#include "SVGImageBufferTools.h"
+
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "RenderObject.h"
+#include "RenderSVGContainer.h"
+#include "RenderSVGRoot.h"
+
+namespace WebCore {
+
+static AffineTransform& currentContentTransformation()
+{
+ DEFINE_STATIC_LOCAL(AffineTransform, s_currentContentTransformation, ());
+ return s_currentContentTransformation;
+}
+
+void SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(const RenderObject* renderer, AffineTransform& absoluteTransform)
+{
+ const RenderObject* current = renderer;
+ ASSERT(current);
+
+ absoluteTransform = currentContentTransformation();
+ while (current) {
+ absoluteTransform.multiply(current->localToParentTransform());
+ if (current->isSVGRoot())
+ break;
+ current = current->parent();
+ }
+}
+
+bool SVGImageBufferTools::createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>& imageBuffer, ColorSpace colorSpace)
+{
+ IntSize imageSize(roundedImageBufferSize(clampedAbsoluteTargetRect.size()));
+ IntSize unclampedImageSize(SVGImageBufferTools::roundedImageBufferSize(absoluteTargetRect.size()));
+
+ // Don't create empty ImageBuffers.
+ if (imageSize.isEmpty())
+ return false;
+
+ OwnPtr<ImageBuffer> image = ImageBuffer::create(imageSize, colorSpace);
+ if (!image)
+ return false;
+
+ GraphicsContext* imageContext = image->context();
+ ASSERT(imageContext);
+
+ // Compensate rounding effects, as the absolute target rect is using floating-point numbers and the image buffer size is integer.
+ imageContext->scale(FloatSize(unclampedImageSize.width() / absoluteTargetRect.width(), unclampedImageSize.height() / absoluteTargetRect.height()));
+
+ imageBuffer = image.release();
+ return true;
+}
+
+void SVGImageBufferTools::renderSubtreeToImageBuffer(ImageBuffer* image, RenderObject* item, const AffineTransform& subtreeContentTransformation)
+{
+ ASSERT(item);
+ ASSERT(image);
+ ASSERT(image->context());
+
+ PaintInfo info(image->context(), PaintInfo::infiniteRect(), PaintPhaseForeground, 0, 0, 0);
+
+ AffineTransform& contentTransformation = currentContentTransformation();
+ AffineTransform savedContentTransformation = contentTransformation;
+ contentTransformation.multiply(subtreeContentTransformation);
+
+ item->layoutIfNeeded();
+ item->paint(info, 0, 0);
+
+ contentTransformation = savedContentTransformation;
+}
+
+void SVGImageBufferTools::clipToImageBuffer(GraphicsContext* context, const AffineTransform& absoluteTransform, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>& imageBuffer)
+{
+ ASSERT(context);
+ ASSERT(imageBuffer);
+
+ // The mask image has been created in the absolute coordinate space, as the image should not be scaled.
+ // So the actual masking process has to be done in the absolute coordinate space as well.
+ context->concatCTM(absoluteTransform.inverse());
+ context->clipToImageBuffer(imageBuffer.get(), clampedAbsoluteTargetRect);
+ context->concatCTM(absoluteTransform);
+
+ // When nesting resources, with objectBoundingBox as content unit types, there's no use in caching the
+ // resulting image buffer as the parent resource already caches the result.
+ if (!currentContentTransformation().isIdentity())
+ imageBuffer.clear();
+}
+
+IntSize SVGImageBufferTools::roundedImageBufferSize(const FloatSize& size)
+{
+ return IntSize(static_cast<int>(lroundf(size.width())), static_cast<int>(lroundf(size.height())));
+}
+
+FloatRect SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(const RenderObject* renderer, const FloatRect& absoluteTargetRect)
+{
+ ASSERT(renderer);
+
+ const RenderSVGRoot* svgRoot = SVGRenderSupport::findTreeRootObject(renderer);
+ FloatRect clampedAbsoluteTargetRect = absoluteTargetRect;
+ clampedAbsoluteTargetRect.intersect(svgRoot->contentBoxRect());
+ return clampedAbsoluteTargetRect;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/SVGImageBufferTools.h b/Source/WebCore/rendering/SVGImageBufferTools.h
new file mode 100644
index 0000000..9bcc7a4
--- /dev/null
+++ b/Source/WebCore/rendering/SVGImageBufferTools.h
@@ -0,0 +1,53 @@
+/*
+ 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
+ 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 SVGImageBufferTools_h
+#define SVGImageBufferTools_h
+
+#if ENABLE(SVG)
+#include "ImageBuffer.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class AffineTransform;
+class FloatRect;
+class FloatSize;
+class GraphicsContext;
+class RenderObject;
+
+class SVGImageBufferTools : public Noncopyable {
+public:
+ static bool createImageBuffer(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>&, ColorSpace);
+ static void renderSubtreeToImageBuffer(ImageBuffer*, RenderObject*, const AffineTransform&);
+ static void clipToImageBuffer(GraphicsContext*, const AffineTransform& absoluteTransform, const FloatRect& clampedAbsoluteTargetRect, OwnPtr<ImageBuffer>&);
+
+ static void calculateTransformationToOutermostSVGCoordinateSystem(const RenderObject*, AffineTransform& absoluteTransform);
+ static FloatRect clampedAbsoluteTargetRectForRenderer(const RenderObject*, const FloatRect& absoluteTargetRect);
+ static IntSize roundedImageBufferSize(const FloatSize&);
+
+private:
+ SVGImageBufferTools() { }
+ ~SVGImageBufferTools() { }
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/SVGMarkerData.h b/Source/WebCore/rendering/SVGMarkerData.h
new file mode 100644
index 0000000..ba11e7e
--- /dev/null
+++ b/Source/WebCore/rendering/SVGMarkerData.h
@@ -0,0 +1,134 @@
+/*
+ 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
+ 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 SVGMarkerData_h
+#define SVGMarkerData_h
+
+#if ENABLE(SVG)
+#include "FloatConversion.h"
+#include "Path.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+class RenderSVGResourceMarker;
+
+class SVGMarkerData {
+public:
+ enum Type {
+ Unknown = 0,
+ Start,
+ Mid,
+ End
+ };
+
+ SVGMarkerData(const Type& type = Unknown, RenderSVGResourceMarker* marker = 0)
+ : m_type(type)
+ , m_marker(marker)
+ {
+ }
+
+ FloatPoint origin() const { return m_origin; }
+ RenderSVGResourceMarker* marker() const { return m_marker; }
+
+ float currentAngle() const
+ {
+ FloatSize inslopeChange = m_inslopePoints[1] - m_inslopePoints[0];
+ FloatSize outslopeChange = m_outslopePoints[1] - m_outslopePoints[0];
+
+ double inslope = rad2deg(atan2(inslopeChange.height(), inslopeChange.width()));
+ double outslope = rad2deg(atan2(outslopeChange.height(), outslopeChange.width()));
+
+ double angle = 0;
+ switch (m_type) {
+ case Start:
+ angle = outslope;
+ break;
+ case Mid:
+ angle = (inslope + outslope) / 2;
+ break;
+ case End:
+ angle = inslope;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ return narrowPrecisionToFloat(angle);
+ }
+
+ void updateTypeAndMarker(const Type& type, RenderSVGResourceMarker* marker)
+ {
+ m_type = type;
+ m_marker = marker;
+ }
+
+ void updateOutslope(const FloatPoint& point)
+ {
+ m_outslopePoints[0] = m_origin;
+ m_outslopePoints[1] = point;
+ }
+
+ void updateMarkerDataForPathElement(const PathElement* element)
+ {
+ FloatPoint* points = element->points;
+
+ switch (element->type) {
+ case PathElementAddQuadCurveToPoint:
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=33115 (PathElementAddQuadCurveToPoint not handled for <marker>)
+ m_origin = points[1];
+ break;
+ case PathElementAddCurveToPoint:
+ m_inslopePoints[0] = points[1];
+ m_inslopePoints[1] = points[2];
+ m_origin = points[2];
+ break;
+ case PathElementMoveToPoint:
+ m_subpathStart = points[0];
+ case PathElementAddLineToPoint:
+ updateInslope(points[0]);
+ m_origin = points[0];
+ break;
+ case PathElementCloseSubpath:
+ updateInslope(points[0]);
+ m_origin = m_subpathStart;
+ m_subpathStart = FloatPoint();
+ }
+ }
+
+private:
+ void updateInslope(const FloatPoint& point)
+ {
+ m_inslopePoints[0] = m_origin;
+ m_inslopePoints[1] = point;
+ }
+
+ Type m_type;
+ RenderSVGResourceMarker* m_marker;
+ FloatPoint m_origin;
+ FloatPoint m_subpathStart;
+ FloatPoint m_inslopePoints[2];
+ FloatPoint m_outslopePoints[2];
+};
+
+}
+
+#endif // ENABLE(SVG)
+#endif // SVGMarkerData_h
diff --git a/Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp b/Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp
new file mode 100644
index 0000000..aed756a
--- /dev/null
+++ b/Source/WebCore/rendering/SVGMarkerLayoutInfo.cpp
@@ -0,0 +1,123 @@
+/*
+ Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2008 Rob Buis <buis@kde.org>
+ 2005, 2007 Eric Seidel <eric@webkit.org>
+ 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
+ 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"
+
+#if ENABLE(SVG)
+#include "SVGMarkerLayoutInfo.h"
+
+#include "RenderSVGResourceMarker.h"
+
+namespace WebCore {
+
+SVGMarkerLayoutInfo::SVGMarkerLayoutInfo()
+ : m_midMarker(0)
+ , m_elementIndex(0)
+ , m_strokeWidth(0)
+{
+}
+
+SVGMarkerLayoutInfo::~SVGMarkerLayoutInfo()
+{
+}
+
+static inline void processStartAndMidMarkers(void* infoPtr, const PathElement* element)
+{
+ SVGMarkerLayoutInfo& info = *reinterpret_cast<SVGMarkerLayoutInfo*>(infoPtr);
+ SVGMarkerData& markerData = info.markerData();
+ int& elementIndex = info.elementIndex();
+
+ // First update the outslope for the previous element
+ markerData.updateOutslope(element->points[0]);
+
+ // Draw the marker for the previous element
+ RenderSVGResourceMarker* marker = markerData.marker();
+ if (elementIndex > 0 && marker)
+ info.addLayoutedMarker(marker, markerData.origin(), markerData.currentAngle());
+
+ // Update our marker data for this element
+ markerData.updateMarkerDataForPathElement(element);
+
+ // After drawing the start marker, switch to drawing mid markers
+ if (elementIndex == 1)
+ markerData.updateTypeAndMarker(SVGMarkerData::Mid, info.midMarker());
+
+ ++elementIndex;
+}
+
+FloatRect SVGMarkerLayoutInfo::calculateBoundaries(RenderSVGResourceMarker* startMarker, RenderSVGResourceMarker* midMarker, RenderSVGResourceMarker* endMarker, float strokeWidth, const Path& path)
+{
+ m_layout.clear();
+ m_midMarker = midMarker;
+ m_strokeWidth = strokeWidth;
+ m_elementIndex = 0;
+ m_markerData = SVGMarkerData(SVGMarkerData::Start, startMarker);
+ path.apply(this, processStartAndMidMarkers);
+
+ if (endMarker) {
+ m_markerData.updateTypeAndMarker(SVGMarkerData::End, endMarker);
+ addLayoutedMarker(endMarker, m_markerData.origin(), m_markerData.currentAngle());
+ }
+
+ if (m_layout.isEmpty())
+ return FloatRect();
+
+ Vector<MarkerLayout>::iterator it = m_layout.begin();
+ Vector<MarkerLayout>::iterator end = m_layout.end();
+
+ FloatRect bounds;
+ for (; it != end; ++it) {
+ MarkerLayout& layout = *it;
+
+ RenderSVGResourceMarker* markerContent = layout.marker;
+ ASSERT(markerContent);
+
+ bounds.unite(markerContent->markerBoundaries(layout.matrix));
+ }
+
+ return bounds;
+}
+
+void SVGMarkerLayoutInfo::drawMarkers(PaintInfo& paintInfo)
+{
+ if (m_layout.isEmpty())
+ return;
+
+ Vector<MarkerLayout>::iterator it = m_layout.begin();
+ Vector<MarkerLayout>::iterator end = m_layout.end();
+
+ for (; it != end; ++it) {
+ MarkerLayout& layout = *it;
+ layout.marker->draw(paintInfo, layout.matrix);
+ }
+}
+
+void SVGMarkerLayoutInfo::addLayoutedMarker(RenderSVGResourceMarker* marker, const FloatPoint& origin, float angle)
+{
+ ASSERT(marker);
+ m_layout.append(MarkerLayout(marker, marker->markerTransformation(origin, angle, m_strokeWidth)));
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/SVGMarkerLayoutInfo.h b/Source/WebCore/rendering/SVGMarkerLayoutInfo.h
new file mode 100644
index 0000000..8f59703
--- /dev/null
+++ b/Source/WebCore/rendering/SVGMarkerLayoutInfo.h
@@ -0,0 +1,74 @@
+/*
+ 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
+ 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 SVGMarkerLayoutInfo_h
+#define SVGMarkerLayoutInfo_h
+
+#if ENABLE(SVG)
+#include "RenderObject.h"
+#include "SVGMarkerData.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class Path;
+class RenderSVGResourceMarker;
+
+struct MarkerLayout {
+ MarkerLayout(RenderSVGResourceMarker* markerObj = 0, AffineTransform matrixObj = AffineTransform())
+ : marker(markerObj)
+ , matrix(matrixObj)
+ {
+ ASSERT(marker);
+ }
+
+ RenderSVGResourceMarker* marker;
+ AffineTransform matrix;
+};
+
+class SVGMarkerLayoutInfo : public Noncopyable {
+public:
+ SVGMarkerLayoutInfo();
+ ~SVGMarkerLayoutInfo();
+
+ FloatRect calculateBoundaries(RenderSVGResourceMarker* startMarker, RenderSVGResourceMarker* midMarker, RenderSVGResourceMarker* endMarker, float strokeWidth, const Path&);
+ void drawMarkers(PaintInfo&);
+
+ // Used by static inline helper functions in SVGMarkerLayoutInfo.cpp
+ SVGMarkerData& markerData() { return m_markerData; }
+ RenderSVGResourceMarker* midMarker() const { return m_midMarker; }
+ int& elementIndex() { return m_elementIndex; }
+ void addLayoutedMarker(RenderSVGResourceMarker*, const FloatPoint& origin, float angle);
+
+private:
+ RenderSVGResourceMarker* m_midMarker;
+
+ // Used while layouting markers
+ int m_elementIndex;
+ SVGMarkerData m_markerData;
+ float m_strokeWidth;
+
+ // Holds the final computed result
+ Vector<MarkerLayout> m_layout;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/SVGRenderSupport.cpp b/Source/WebCore/rendering/SVGRenderSupport.cpp
new file mode 100644
index 0000000..cbde49b
--- /dev/null
+++ b/Source/WebCore/rendering/SVGRenderSupport.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org>
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * (C) 2007 Eric Seidel <eric@webkit.org>
+ * (C) 2009 Google, Inc. All rights reserved.
+ * (C) 2009 Dirk Schulze <krit@webkit.org>
+ * Copyright (C) Research In Motion Limited 2009-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)
+#include "SVGRenderSupport.h"
+
+#include "FrameView.h"
+#include "ImageBuffer.h"
+#include "NodeRenderStyle.h"
+#include "RenderLayer.h"
+#include "RenderSVGPath.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "RenderSVGRoot.h"
+#include "SVGResources.h"
+#include "SVGStyledElement.h"
+#include "TransformState.h"
+#include <wtf/UnusedParam.h>
+
+namespace WebCore {
+
+IntRect SVGRenderSupport::clippedOverflowRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer)
+{
+ // Return early for any cases where we don't actually paint
+ if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
+ return IntRect();
+
+ // Pass our local paint rect to computeRectForRepaint() which will
+ // map to parent coords and recurse up the parent chain.
+ IntRect repaintRect = enclosingIntRect(object->repaintRectInLocalCoordinates());
+ object->computeRectForRepaint(repaintContainer, repaintRect);
+ return repaintRect;
+}
+
+void SVGRenderSupport::computeRectForRepaint(RenderObject* object, RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+ const SVGRenderStyle* svgStyle = object->style()->svgStyle();
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+
+ // Translate to coords in our parent renderer, and then call computeRectForRepaint on our parent
+ repaintRect = object->localToParentTransform().mapRect(repaintRect);
+ object->parent()->computeRectForRepaint(repaintContainer, repaintRect, fixed);
+}
+
+void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, RenderBoxModelObject* repaintContainer, bool fixed , bool useTransforms, TransformState& transformState)
+{
+ ASSERT(!fixed); // We should have no fixed content in the SVG rendering tree.
+ ASSERT(useTransforms); // Mapping a point through SVG w/o respecting transforms is useless.
+ transformState.applyTransform(object->localToParentTransform());
+ object->parent()->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
+}
+
+bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo)
+{
+ ASSERT(object);
+
+ RenderStyle* style = object->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ // Setup transparency layers before setting up SVG resources!
+ float opacity = style->opacity();
+ const ShadowData* shadow = svgStyle->shadow();
+ if (opacity < 1 || shadow) {
+ FloatRect repaintRect = object->repaintRectInLocalCoordinates();
+
+ if (opacity < 1) {
+ paintInfo.context->clip(repaintRect);
+ paintInfo.context->beginTransparencyLayer(opacity);
+ }
+
+ if (shadow) {
+ paintInfo.context->clip(repaintRect);
+ paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace());
+ paintInfo.context->beginTransparencyLayer(1);
+ }
+ }
+
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources)
+ return true;
+
+ if (RenderSVGResourceMasker* masker = resources->masker()) {
+ if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper()) {
+ if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+
+#if ENABLE(FILTERS)
+ if (RenderSVGResourceFilter* filter = resources->filter()) {
+ if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode))
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void SVGRenderSupport::finishRenderSVGContent(RenderObject* object, PaintInfo& paintInfo, GraphicsContext* savedContext)
+{
+#if !ENABLE(FILTERS)
+ UNUSED_PARAM(savedContext);
+#endif
+
+ ASSERT(object);
+
+ const RenderStyle* style = object->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+#if ENABLE(FILTERS)
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (resources) {
+ if (RenderSVGResourceFilter* filter = resources->filter()) {
+ filter->postApplyResource(object, paintInfo.context, ApplyToDefaultMode, /* path */0);
+ paintInfo.context = savedContext;
+ }
+ }
+#endif
+
+ if (style->opacity() < 1)
+ paintInfo.context->endTransparencyLayer();
+
+ if (svgStyle->shadow())
+ paintInfo.context->endTransparencyLayer();
+}
+
+void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
+{
+ for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
+ if (current->isSVGHiddenContainer())
+ continue;
+
+ const AffineTransform& transform = current->localToParentTransform();
+ if (transform.isIdentity()) {
+ objectBoundingBox.unite(current->objectBoundingBox());
+ strokeBoundingBox.unite(current->strokeBoundingBox());
+ repaintBoundingBox.unite(current->repaintRectInLocalCoordinates());
+ } else {
+ objectBoundingBox.unite(transform.mapRect(current->objectBoundingBox()));
+ strokeBoundingBox.unite(transform.mapRect(current->strokeBoundingBox()));
+ repaintBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
+ }
+ }
+}
+
+bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
+{
+ if (localTransform.isIdentity())
+ return localRepaintRect.intersects(paintInfo.rect);
+
+ return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
+}
+
+const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
+{
+ while (start && !start->isSVGRoot())
+ start = start->parent();
+
+ ASSERT(start);
+ ASSERT(start->isSVGRoot());
+ return toRenderSVGRoot(start);
+}
+
+static inline void invalidateResourcesOfChildren(RenderObject* start)
+{
+ ASSERT(!start->needsLayout());
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
+ resources->removeClientFromCache(start, false);
+
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
+ invalidateResourcesOfChildren(child);
+}
+
+void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
+{
+ bool layoutSizeChanged = findTreeRootObject(start)->isLayoutSizeChanged();
+ HashSet<RenderObject*> notlayoutedObjects;
+
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ bool needsLayout = selfNeedsLayout;
+
+ if (layoutSizeChanged) {
+ // When selfNeedsLayout is false and the layout size changed, we have to check whether this child uses relative lengths
+ if (SVGElement* element = child->node()->isSVGElement() ? static_cast<SVGElement*>(child->node()) : 0) {
+ if (element->isStyled() && static_cast<SVGStyledElement*>(element)->hasRelativeLengths()) {
+ // When the layout size changed and when using relative values tell the RenderSVGPath to update its Path object
+ if (child->isSVGPath())
+ toRenderSVGPath(child)->setNeedsPathUpdate();
+
+ needsLayout = true;
+ }
+ }
+ }
+
+ if (needsLayout) {
+ child->setNeedsLayout(true, false);
+ child->layout();
+ } else {
+ if (child->needsLayout())
+ child->layout();
+ else if (layoutSizeChanged)
+ notlayoutedObjects.add(child);
+ }
+
+ ASSERT(!child->needsLayout());
+ }
+
+ if (!layoutSizeChanged) {
+ ASSERT(notlayoutedObjects.isEmpty());
+ return;
+ }
+
+ // If the layout size changed, invalidate all resources of all children that didn't go through the layout() code path.
+ HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
+ for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
+ invalidateResourcesOfChildren(*it);
+}
+
+bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
+{
+ // SVG doesn't support independent x/y overflow
+ ASSERT(object->style()->overflowX() == object->style()->overflowY());
+
+ // OSCROLL is never set for SVG - see CSSStyleSelector::adjustRenderStyle
+ ASSERT(object->style()->overflowX() != OSCROLL);
+
+ // RenderSVGRoot should never query for overflow state - it should always clip itself to the initial viewport size.
+ ASSERT(!object->isRoot());
+
+ return object->style()->overflowX() == OHIDDEN;
+}
+
+void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect)
+{
+ ASSERT(object);
+
+ RenderStyle* style = object->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ RenderObject* renderer = const_cast<RenderObject*>(object);
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
+ if (!resources) {
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+ return;
+ }
+
+#if ENABLE(FILTERS)
+ if (RenderSVGResourceFilter* filter = resources->filter())
+ repaintRect = filter->resourceBoundingBox(renderer);
+#endif
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper())
+ repaintRect.intersect(clipper->resourceBoundingBox(renderer));
+
+ if (RenderSVGResourceMasker* masker = resources->masker())
+ repaintRect.intersect(masker->resourceBoundingBox(renderer));
+
+ if (const ShadowData* shadow = svgStyle->shadow())
+ shadow->adjustRectForShadow(repaintRect);
+}
+
+bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
+{
+ ASSERT(object);
+
+ // We just take clippers into account to determine if a point is on the node. The Specification may
+ // change later and we also need to check maskers.
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources)
+ return true;
+
+ if (RenderSVGResourceClipper* clipper = resources->clipper())
+ return clipper->hitTestClipContent(object->objectBoundingBox(), point);
+
+ return true;
+}
+
+void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
+{
+ ASSERT(context);
+ ASSERT(style);
+ ASSERT(object);
+ ASSERT(object->node());
+ ASSERT(object->node()->isSVGElement());
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ SVGElement* lengthContext = static_cast<SVGElement*>(object->node());
+ context->setStrokeThickness(svgStyle->strokeWidth().value(lengthContext));
+ context->setLineCap(svgStyle->capStyle());
+ context->setLineJoin(svgStyle->joinStyle());
+ if (svgStyle->joinStyle() == MiterJoin)
+ context->setMiterLimit(svgStyle->strokeMiterLimit());
+
+ const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
+ if (dashes.isEmpty())
+ context->setStrokeStyle(SolidStroke);
+ else {
+ DashArray dashArray;
+ const Vector<SVGLength>::const_iterator end = dashes.end();
+ for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
+ dashArray.append((*it).value(lengthContext));
+
+ context->setLineDash(dashArray, svgStyle->strokeDashOffset().value(lengthContext));
+ }
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/SVGRenderSupport.h b/Source/WebCore/rendering/SVGRenderSupport.h
new file mode 100644
index 0000000..7814863
--- /dev/null
+++ b/Source/WebCore/rendering/SVGRenderSupport.h
@@ -0,0 +1,84 @@
+/**
+ * Copyright (C) 2007 Rob Buis <buis@kde.org>
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * (C) 2007 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2009 Google, 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.
+ *
+ */
+
+#ifndef SVGRenderSupport_h
+#define SVGRenderSupport_h
+
+#if ENABLE(SVG)
+#include "PaintInfo.h"
+
+namespace WebCore {
+
+class FloatPoint;
+class FloatRect;
+class ImageBuffer;
+class RenderBoxModelObject;
+class RenderObject;
+class RenderStyle;
+class RenderSVGRoot;
+class TransformState;
+
+// SVGRendererSupport is a helper class sharing code between all SVG renderers.
+class SVGRenderSupport {
+public:
+ // Used by all SVG renderers who apply clip/filter/etc. resources to the renderer content
+ static bool prepareToRenderSVGContent(RenderObject*, PaintInfo&);
+ static void finishRenderSVGContent(RenderObject*, PaintInfo&, GraphicsContext* savedContext);
+
+ // Shares child layouting code between RenderSVGRoot/RenderSVG(Hidden)Container
+ static void layoutChildren(RenderObject*, bool selfNeedsLayout);
+
+ // Helper function determining wheter overflow is hidden
+ static bool isOverflowHidden(const RenderObject*);
+
+ // Calculates the repaintRect in combination with filter, clipper and masker in local coordinates.
+ static void intersectRepaintRectWithResources(const RenderObject*, FloatRect&);
+
+ // Determines whether the passed point lies in a clipping area
+ static bool pointInClippingArea(RenderObject*, const FloatPoint&);
+
+ static void computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox);
+ static bool paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo);
+
+ // Important functions used by nearly all SVG renderers centralizing coordinate transformations / repaint rect calculations
+ static IntRect clippedOverflowRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer);
+ static void computeRectForRepaint(RenderObject*, RenderBoxModelObject* repaintContainer, IntRect&, bool fixed);
+ static void mapLocalToContainer(const RenderObject*, RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&);
+
+ // Shared between SVG renderers and resources.
+ static void applyStrokeStyleToContext(GraphicsContext*, const RenderStyle*, const RenderObject*);
+
+ // FIXME: These methods do not belong here.
+ static const RenderSVGRoot* findTreeRootObject(const RenderObject* start);
+
+private:
+ // This class is not constructable.
+ SVGRenderSupport();
+ ~SVGRenderSupport();
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // SVGRenderSupport_h
diff --git a/Source/WebCore/rendering/SVGRenderTreeAsText.cpp b/Source/WebCore/rendering/SVGRenderTreeAsText.cpp
new file mode 100644
index 0000000..ea843c2
--- /dev/null
+++ b/Source/WebCore/rendering/SVGRenderTreeAsText.cpp
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
+ * (C) 2005 Rob Buis <buis@kde.org>
+ * (C) 2006 Alexander Kellett <lypanov@kde.org>
+ * Copyright (C) Research In Motion Limited 2010. 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"
+
+#if ENABLE(SVG)
+#include "SVGRenderTreeAsText.h"
+
+#include "GraphicsTypes.h"
+#include "HTMLNames.h"
+#include "InlineTextBox.h"
+#include "LinearGradientAttributes.h"
+#include "NodeRenderStyle.h"
+#include "Path.h"
+#include "PatternAttributes.h"
+#include "RadialGradientAttributes.h"
+#include "RenderImage.h"
+#include "RenderSVGContainer.h"
+#include "RenderSVGGradientStop.h"
+#include "RenderSVGImage.h"
+#include "RenderSVGInlineText.h"
+#include "RenderSVGPath.h"
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceGradient.h"
+#include "RenderSVGResourceLinearGradient.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "RenderSVGResourcePattern.h"
+#include "RenderSVGResourceRadialGradient.h"
+#include "RenderSVGResourceSolidColor.h"
+#include "RenderSVGRoot.h"
+#include "RenderSVGText.h"
+#include "RenderTreeAsText.h"
+#include "SVGCircleElement.h"
+#include "SVGEllipseElement.h"
+#include "SVGInlineTextBox.h"
+#include "SVGLineElement.h"
+#include "SVGLinearGradientElement.h"
+#include "SVGNames.h"
+#include "SVGPathElement.h"
+#include "SVGPathParserFactory.h"
+#include "SVGPatternElement.h"
+#include "SVGPointList.h"
+#include "SVGPolyElement.h"
+#include "SVGRadialGradientElement.h"
+#include "SVGRectElement.h"
+#include "SVGRootInlineBox.h"
+#include "SVGStopElement.h"
+#include "SVGStyledElement.h"
+
+#include <math.h>
+
+namespace WebCore {
+
+/** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
+ * Can be used in cases where you don't know which item in the list is the first
+ * one to be printed, but still want to avoid strings like ", b, c".
+ */
+class TextStreamSeparator {
+public:
+ TextStreamSeparator(const String& s)
+ : m_separator(s)
+ , m_needToSeparate(false)
+ {
+ }
+
+private:
+ friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
+
+ String m_separator;
+ bool m_needToSeparate;
+};
+
+TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
+{
+ if (sep.m_needToSeparate)
+ ts << sep.m_separator;
+ else
+ sep.m_needToSeparate = true;
+ return ts;
+}
+
+template<typename ValueType>
+static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
+{
+ ts << " [" << name << "=" << value << "]";
+}
+
+template<typename ValueType>
+static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
+{
+ ts << " [" << name << "=\"" << value << "\"]";
+}
+
+static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value)
+{
+ if (!value.isEmpty())
+ writeNameValuePair(ts, name, value);
+}
+
+template<typename ValueType>
+static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue)
+{
+ if (value != defaultValue)
+ writeNameValuePair(ts, name, value);
+}
+
+TextStream& operator<<(TextStream& ts, const FloatRect &r)
+{
+ ts << "at (";
+ if (hasFractions(r.x()))
+ ts << r.x();
+ else
+ ts << int(r.x());
+ ts << ",";
+ if (hasFractions(r.y()))
+ ts << r.y();
+ else
+ ts << int(r.y());
+ ts << ") size ";
+ if (hasFractions(r.width()))
+ ts << r.width();
+ else
+ ts << int(r.width());
+ ts << "x";
+ if (hasFractions(r.height()))
+ ts << r.height();
+ else
+ ts << int(r.height());
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
+{
+ if (transform.isIdentity())
+ ts << "identity";
+ else
+ ts << "{m=(("
+ << transform.a() << "," << transform.b()
+ << ")("
+ << transform.c() << "," << transform.d()
+ << ")) t=("
+ << transform.e() << "," << transform.f()
+ << ")}";
+
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const WindRule rule)
+{
+ switch (rule) {
+ case RULE_NONZERO:
+ ts << "NON-ZERO";
+ break;
+ case RULE_EVENODD:
+ ts << "EVEN-ODD";
+ break;
+ }
+
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType)
+{
+ switch (unitType) {
+ case SVGUnitTypes::SVG_UNIT_TYPE_UNKNOWN:
+ ts << "unknown";
+ break;
+ case SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE:
+ ts << "userSpaceOnUse";
+ break;
+ case SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX:
+ ts << "objectBoundingBox";
+ break;
+ }
+
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const SVGMarkerElement::SVGMarkerUnitsType& markerUnit)
+{
+ switch (markerUnit) {
+ case SVGMarkerElement::SVG_MARKERUNITS_UNKNOWN:
+ ts << "unknown";
+ break;
+ case SVGMarkerElement::SVG_MARKERUNITS_USERSPACEONUSE:
+ ts << "userSpaceOnUse";
+ break;
+ case SVGMarkerElement::SVG_MARKERUNITS_STROKEWIDTH:
+ ts << "strokeWidth";
+ break;
+ }
+
+ return ts;
+}
+
+TextStream& operator<<(TextStream& ts, const Color& c)
+{
+ return ts << c.name();
+}
+
+// FIXME: Maybe this should be in KCanvasRenderingStyle.cpp
+static TextStream& operator<<(TextStream& ts, const DashArray& a)
+{
+ ts << "{";
+ DashArray::const_iterator end = a.end();
+ for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
+ if (it != a.begin())
+ ts << ", ";
+ ts << *it;
+ }
+ ts << "}";
+ return ts;
+}
+
+// FIXME: Maybe this should be in GraphicsTypes.cpp
+static TextStream& operator<<(TextStream& ts, LineCap style)
+{
+ switch (style) {
+ case ButtCap:
+ ts << "BUTT";
+ break;
+ case RoundCap:
+ ts << "ROUND";
+ break;
+ case SquareCap:
+ ts << "SQUARE";
+ break;
+ }
+ return ts;
+}
+
+// FIXME: Maybe this should be in GraphicsTypes.cpp
+static TextStream& operator<<(TextStream& ts, LineJoin style)
+{
+ switch (style) {
+ case MiterJoin:
+ ts << "MITER";
+ break;
+ case RoundJoin:
+ ts << "ROUND";
+ break;
+ case BevelJoin:
+ ts << "BEVEL";
+ break;
+ }
+ return ts;
+}
+
+// FIXME: Maybe this should be in Gradient.cpp
+static TextStream& operator<<(TextStream& ts, GradientSpreadMethod mode)
+{
+ switch (mode) {
+ case SpreadMethodPad:
+ ts << "PAD";
+ break;
+ case SpreadMethodRepeat:
+ ts << "REPEAT";
+ break;
+ case SpreadMethodReflect:
+ ts << "REFLECT";
+ break;
+ }
+
+ return ts;
+}
+
+static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource)
+{
+ if (resource->resourceType() == SolidColorResourceType) {
+ ts << "[type=SOLID] [color=" << static_cast<RenderSVGResourceSolidColor*>(resource)->color() << "]";
+ return;
+ }
+
+ // All other resources derive from RenderSVGResourceContainer
+ RenderSVGResourceContainer* container = static_cast<RenderSVGResourceContainer*>(resource);
+ Node* node = container->node();
+ ASSERT(node);
+ ASSERT(node->isSVGElement());
+
+ if (resource->resourceType() == PatternResourceType)
+ ts << "[type=PATTERN]";
+ else if (resource->resourceType() == LinearGradientResourceType)
+ ts << "[type=LINEAR-GRADIENT]";
+ else if (resource->resourceType() == RadialGradientResourceType)
+ ts << "[type=RADIAL-GRADIENT]";
+
+ ts << " [id=\"" << static_cast<SVGElement*>(node)->getIdAttribute() << "\"]";
+}
+
+static void writeStyle(TextStream& ts, const RenderObject& object)
+{
+ const RenderStyle* style = object.style();
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+
+ if (!object.localTransform().isIdentity())
+ writeNameValuePair(ts, "transform", object.localTransform());
+ writeIfNotDefault(ts, "image rendering", svgStyle->imageRendering(), SVGRenderStyle::initialImageRendering());
+ writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity());
+ if (object.isSVGPath()) {
+ const RenderSVGPath& path = static_cast<const RenderSVGPath&>(object);
+ ASSERT(path.node());
+ ASSERT(path.node()->isSVGElement());
+
+ Color fallbackColor;
+ if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) {
+ TextStreamSeparator s(" ");
+ ts << " [stroke={" << s;
+ writeSVGPaintingResource(ts, strokePaintingResource);
+
+ SVGElement* element = static_cast<SVGElement*>(path.node());
+ double dashOffset = svgStyle->strokeDashOffset().value(element);
+ double strokeWidth = svgStyle->strokeWidth().value(element);
+ const Vector<SVGLength>& dashes = svgStyle->strokeDashArray();
+
+ DashArray dashArray;
+ const Vector<SVGLength>::const_iterator end = dashes.end();
+ for (Vector<SVGLength>::const_iterator it = dashes.begin(); it != end; ++it)
+ dashArray.append((*it).value(element));
+
+ writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f);
+ writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0);
+ writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f);
+ writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap);
+ writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin);
+ writeIfNotDefault(ts, "dash offset", dashOffset, 0.0);
+ if (!dashArray.isEmpty())
+ writeNameValuePair(ts, "dash array", dashArray);
+
+ ts << "}]";
+ }
+
+ if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGPath*>(&path), path.style(), fallbackColor)) {
+ TextStreamSeparator s(" ");
+ ts << " [fill={" << s;
+ writeSVGPaintingResource(ts, fillPaintingResource);
+
+ writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f);
+ writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO);
+ ts << "}]";
+ }
+ writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO);
+ }
+
+ writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource());
+ writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource());
+ writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource());
+}
+
+static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
+{
+ ts << " " << const_cast<RenderObject&>(object).absoluteClippedOverflowRect();
+ writeStyle(ts, object);
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const RenderSVGPath& path)
+{
+ writePositionAndStyle(ts, path);
+
+ ASSERT(path.node()->isSVGElement());
+ SVGElement* svgElement = static_cast<SVGElement*>(path.node());
+
+ if (svgElement->hasTagName(SVGNames::rectTag)) {
+ SVGRectElement* element = static_cast<SVGRectElement*>(svgElement);
+ writeNameValuePair(ts, "x", element->x().value(element));
+ writeNameValuePair(ts, "y", element->y().value(element));
+ writeNameValuePair(ts, "width", element->width().value(element));
+ writeNameValuePair(ts, "height", element->height().value(element));
+ } else if (svgElement->hasTagName(SVGNames::lineTag)) {
+ SVGLineElement* element = static_cast<SVGLineElement*>(svgElement);
+ writeNameValuePair(ts, "x1", element->x1().value(element));
+ writeNameValuePair(ts, "y1", element->y1().value(element));
+ writeNameValuePair(ts, "x2", element->x2().value(element));
+ writeNameValuePair(ts, "y2", element->y2().value(element));
+ } else if (svgElement->hasTagName(SVGNames::ellipseTag)) {
+ SVGEllipseElement* element = static_cast<SVGEllipseElement*>(svgElement);
+ writeNameValuePair(ts, "cx", element->cx().value(element));
+ writeNameValuePair(ts, "cy", element->cy().value(element));
+ writeNameValuePair(ts, "rx", element->rx().value(element));
+ writeNameValuePair(ts, "ry", element->ry().value(element));
+ } else if (svgElement->hasTagName(SVGNames::circleTag)) {
+ SVGCircleElement* element = static_cast<SVGCircleElement*>(svgElement);
+ writeNameValuePair(ts, "cx", element->cx().value(element));
+ writeNameValuePair(ts, "cy", element->cy().value(element));
+ writeNameValuePair(ts, "r", element->r().value(element));
+ } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) {
+ SVGPolyElement* element = static_cast<SVGPolyElement*>(svgElement);
+ writeNameAndQuotedValue(ts, "points", element->pointList().valueAsString());
+ } else if (svgElement->hasTagName(SVGNames::pathTag)) {
+ SVGPathElement* element = static_cast<SVGPathElement*>(svgElement);
+ String pathString;
+ // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
+ SVGPathParserFactory::self()->buildStringFromByteStream(element->pathByteStream(), pathString, NormalizedParsing);
+ writeNameAndQuotedValue(ts, "data", pathString);
+ } else
+ ASSERT_NOT_REACHED();
+ return ts;
+}
+
+static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
+{
+ return writePositionAndStyle(ts, root);
+}
+
+static void writeRenderSVGTextBox(TextStream& ts, const RenderBlock& text)
+{
+ SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox());
+ if (!box)
+ return;
+
+ ts << " at (" << text.x() << "," << text.y() << ") size " << box->logicalWidth() << "x" << box->logicalHeight();
+
+ // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
+ ts << " contains 1 chunk(s)";
+
+ if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor)))
+ writeNameValuePair(ts, "color", text.style()->visitedDependentColor(CSSPropertyColor).name());
+}
+
+static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
+{
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ if (fragments.isEmpty())
+ return;
+
+ RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
+ ASSERT(textRenderer);
+
+ const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle();
+ String text = textBox->textRenderer()->text();
+
+ unsigned fragmentsSize = fragments.size();
+ for (unsigned i = 0; i < fragmentsSize; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+ writeIndent(ts, indent + 1);
+
+ unsigned startOffset = fragment.positionListOffset;
+ unsigned endOffset = fragment.positionListOffset + fragment.length;
+
+ // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
+ ts << "chunk 1 ";
+ ETextAnchor anchor = svgStyle->textAnchor();
+ bool isVerticalText = svgStyle->isVerticalWritingMode();
+ if (anchor == TA_MIDDLE) {
+ ts << "(middle anchor";
+ if (isVerticalText)
+ ts << ", vertical";
+ ts << ") ";
+ } else if (anchor == TA_END) {
+ ts << "(end anchor";
+ if (isVerticalText)
+ ts << ", vertical";
+ ts << ") ";
+ } else if (isVerticalText)
+ ts << "(vertical) ";
+ startOffset -= textBox->start();
+ endOffset -= textBox->start();
+ // </hack>
+
+ ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")";
+ ts << " startOffset " << startOffset << " endOffset " << endOffset;
+ if (isVerticalText)
+ ts << " height " << fragment.height;
+ else
+ ts << " width " << fragment.width;
+
+ if (!textBox->isLeftToRightDirection() || textBox->m_dirOverride) {
+ ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL");
+ if (textBox->m_dirOverride)
+ ts << " override";
+ }
+
+ ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.positionListOffset, fragment.length)) << "\n";
+ }
+}
+
+static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent)
+{
+ for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox())
+ writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent);
+}
+
+static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent)
+{
+ writeIndent(ts, indent);
+ ts << object.renderName();
+
+ if (object.node())
+ ts << " {" << object.node()->nodeName() << "}";
+}
+
+static void writeChildren(TextStream& ts, const RenderObject& object, int indent)
+{
+ for (RenderObject* child = object.firstChild(); child; child = child->nextSibling())
+ write(ts, *child, indent + 1);
+}
+
+static inline String boundingBoxModeString(bool boundingBoxMode)
+{
+ return boundingBoxMode ? "objectBoundingBox" : "userSpaceOnUse";
+}
+
+static inline void writeCommonGradientProperties(TextStream& ts, GradientSpreadMethod spreadMethod, const AffineTransform& gradientTransform, bool boundingBoxMode)
+{
+ writeNameValuePair(ts, "gradientUnits", boundingBoxModeString(boundingBoxMode));
+
+ if (spreadMethod != SpreadMethodPad)
+ ts << " [spreadMethod=" << spreadMethod << "]";
+
+ if (!gradientTransform.isIdentity())
+ ts << " [gradientTransform=" << gradientTransform << "]";
+}
+
+void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent)
+{
+ writeStandardPrefix(ts, object, indent);
+
+ Element* element = static_cast<Element*>(object.node());
+ const AtomicString& id = element->getIdAttribute();
+ writeNameAndQuotedValue(ts, "id", id);
+
+ RenderSVGResourceContainer* resource = const_cast<RenderObject&>(object).toRenderSVGResourceContainer();
+ ASSERT(resource);
+
+ if (resource->resourceType() == MaskerResourceType) {
+ RenderSVGResourceMasker* masker = static_cast<RenderSVGResourceMasker*>(resource);
+ writeNameValuePair(ts, "maskUnits", masker->maskUnits());
+ writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits());
+ ts << "\n";
+#if ENABLE(FILTERS)
+ } else if (resource->resourceType() == FilterResourceType) {
+ RenderSVGResourceFilter* filter = static_cast<RenderSVGResourceFilter*>(resource);
+ writeNameValuePair(ts, "filterUnits", filter->filterUnits());
+ writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits());
+ ts << "\n";
+ // Creating a placeholder filter which is passed to the builder.
+ FloatRect dummyRect;
+ RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyRect, dummyRect, dummyRect, true);
+ if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) {
+ if (FilterEffect* lastEffect = builder->lastEffect())
+ lastEffect->externalRepresentation(ts, indent + 1);
+ }
+#endif
+ } else if (resource->resourceType() == ClipperResourceType) {
+ RenderSVGResourceClipper* clipper = static_cast<RenderSVGResourceClipper*>(resource);
+ writeNameValuePair(ts, "clipPathUnits", clipper->clipPathUnits());
+ ts << "\n";
+ } else if (resource->resourceType() == MarkerResourceType) {
+ RenderSVGResourceMarker* marker = static_cast<RenderSVGResourceMarker*>(resource);
+ writeNameValuePair(ts, "markerUnits", marker->markerUnits());
+ ts << " [ref at " << marker->referencePoint() << "]";
+ ts << " [angle=";
+ if (marker->angle() == -1)
+ ts << "auto" << "]\n";
+ else
+ ts << marker->angle() << "]\n";
+ } else if (resource->resourceType() == PatternResourceType) {
+ RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource);
+
+ // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may
+ // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties()
+ PatternAttributes attributes;
+ static_cast<SVGPatternElement*>(pattern->node())->collectPatternAttributes(attributes);
+
+ writeNameValuePair(ts, "patternUnits", boundingBoxModeString(attributes.boundingBoxMode()));
+ writeNameValuePair(ts, "patternContentUnits", boundingBoxModeString(attributes.boundingBoxModeContent()));
+
+ AffineTransform transform = attributes.patternTransform();
+ if (!transform.isIdentity())
+ ts << " [patternTransform=" << transform << "]";
+ ts << "\n";
+ } else if (resource->resourceType() == LinearGradientResourceType) {
+ RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource);
+
+ // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
+ // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
+ SVGLinearGradientElement* linearGradientElement = static_cast<SVGLinearGradientElement*>(gradient->node());
+
+ LinearGradientAttributes attributes;
+ linearGradientElement->collectGradientAttributes(attributes);
+ writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode());
+
+ FloatPoint startPoint;
+ FloatPoint endPoint;
+ linearGradientElement->calculateStartEndPoints(attributes, startPoint, endPoint);
+
+ ts << " [start=" << startPoint << "] [end=" << endPoint << "]\n";
+ } else if (resource->resourceType() == RadialGradientResourceType) {
+ RenderSVGResourceRadialGradient* gradient = static_cast<RenderSVGResourceRadialGradient*>(resource);
+
+ // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
+ // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
+ SVGRadialGradientElement* radialGradientElement = static_cast<SVGRadialGradientElement*>(gradient->node());
+
+ RadialGradientAttributes attributes;
+ radialGradientElement->collectGradientAttributes(attributes);
+ writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.boundingBoxMode());
+
+ FloatPoint focalPoint;
+ FloatPoint centerPoint;
+ float radius;
+ radialGradientElement->calculateFocalCenterPointsAndRadius(attributes, focalPoint, centerPoint, radius);
+
+ ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "]\n";
+ } else
+ ts << "\n";
+ writeChildren(ts, object, indent);
+}
+
+void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent)
+{
+ // Currently RenderSVGResourceFilterPrimitive has no meaningful output.
+ if (container.isSVGResourceFilterPrimitive())
+ return;
+ writeStandardPrefix(ts, container, indent);
+ writePositionAndStyle(ts, container);
+ ts << "\n";
+ writeResources(ts, container, indent);
+ writeChildren(ts, container, indent);
+}
+
+void write(TextStream& ts, const RenderSVGRoot& root, int indent)
+{
+ writeStandardPrefix(ts, root, indent);
+ ts << root << "\n";
+ writeChildren(ts, root, indent);
+}
+
+void writeSVGText(TextStream& ts, const RenderBlock& text, int indent)
+{
+ writeStandardPrefix(ts, text, indent);
+ writeRenderSVGTextBox(ts, text);
+ ts << "\n";
+ writeResources(ts, text, indent);
+ writeChildren(ts, text, indent);
+}
+
+void writeSVGInlineText(TextStream& ts, const RenderText& text, int indent)
+{
+ writeStandardPrefix(ts, text, indent);
+
+ // Why not just linesBoundingBox()?
+ ts << " " << FloatRect(text.firstRunOrigin(), text.linesBoundingBox().size()) << "\n";
+ writeResources(ts, text, indent);
+ writeSVGInlineTextBoxes(ts, text, indent);
+}
+
+void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent)
+{
+ writeStandardPrefix(ts, image, indent);
+ writePositionAndStyle(ts, image);
+ ts << "\n";
+ writeResources(ts, image, indent);
+}
+
+void write(TextStream& ts, const RenderSVGPath& path, int indent)
+{
+ writeStandardPrefix(ts, path, indent);
+ ts << path << "\n";
+ writeResources(ts, path, indent);
+}
+
+void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent)
+{
+ writeStandardPrefix(ts, stop, indent);
+
+ SVGStopElement* stopElement = static_cast<SVGStopElement*>(stop.node());
+ ASSERT(stopElement);
+
+ RenderStyle* style = stop.style();
+ if (!style)
+ return;
+
+ ts << " [offset=" << stopElement->offset() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n";
+}
+
+void writeResources(TextStream& ts, const RenderObject& object, int indent)
+{
+ const RenderStyle* style = object.style();
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+
+ // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache.
+ // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output.
+ RenderObject& renderer = const_cast<RenderObject&>(object);
+ if (!svgStyle->maskerResource().isEmpty()) {
+ if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle->maskerResource())) {
+ writeIndent(ts, indent);
+ ts << " ";
+ writeNameAndQuotedValue(ts, "masker", svgStyle->maskerResource());
+ ts << " ";
+ writeStandardPrefix(ts, *masker, 0);
+ ts << " " << masker->resourceBoundingBox(&renderer) << "\n";
+ }
+ }
+ if (!svgStyle->clipperResource().isEmpty()) {
+ if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle->clipperResource())) {
+ writeIndent(ts, indent);
+ ts << " ";
+ writeNameAndQuotedValue(ts, "clipPath", svgStyle->clipperResource());
+ ts << " ";
+ writeStandardPrefix(ts, *clipper, 0);
+ ts << " " << clipper->resourceBoundingBox(&renderer) << "\n";
+ }
+ }
+#if ENABLE(FILTERS)
+ if (!svgStyle->filterResource().isEmpty()) {
+ if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle->filterResource())) {
+ writeIndent(ts, indent);
+ ts << " ";
+ writeNameAndQuotedValue(ts, "filter", svgStyle->filterResource());
+ ts << " ";
+ writeStandardPrefix(ts, *filter, 0);
+ ts << " " << filter->resourceBoundingBox(&renderer) << "\n";
+ }
+ }
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/SVGRenderTreeAsText.h b/Source/WebCore/rendering/SVGRenderTreeAsText.h
new file mode 100644
index 0000000..4e9ba5d
--- /dev/null
+++ b/Source/WebCore/rendering/SVGRenderTreeAsText.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2004, 2005, 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 SVGRenderTreeAsText_h
+#define SVGRenderTreeAsText_h
+
+#if ENABLE(SVG)
+
+#include "TextStream.h"
+
+namespace WebCore {
+
+ class Color;
+ class FloatRect;
+ class FloatSize;
+ class Node;
+ class RenderBlock;
+ class RenderImage;
+ class RenderObject;
+ class RenderSVGGradientStop;
+ class RenderSVGImage;
+ class RenderSVGPath;
+ class RenderSVGRoot;
+ class RenderText;
+ class AffineTransform;
+ class SVGUnitTypes;
+
+// functions used by the main RenderTreeAsText code
+void write(TextStream&, const RenderSVGPath&, int indent);
+void write(TextStream&, const RenderSVGRoot&, int indent);
+void writeSVGGradientStop(TextStream&, const RenderSVGGradientStop&, int indent);
+void writeSVGResourceContainer(TextStream&, const RenderObject&, int indent);
+void writeSVGContainer(TextStream&, const RenderObject&, int indent);
+void writeSVGImage(TextStream&, const RenderSVGImage&, int indent);
+void writeSVGInlineText(TextStream&, const RenderText&, int indent);
+void writeSVGText(TextStream&, const RenderBlock&, int indent);
+void writeResources(TextStream&, const RenderObject&, int indent);
+
+// helper operators defined used in various classes to dump the render tree.
+TextStream& operator<<(TextStream&, const AffineTransform&);
+TextStream& operator<<(TextStream&, const Color&);
+TextStream& operator<<(TextStream&, const FloatRect&);
+
+// helper operators specific to dumping the render tree. these are used in various classes to dump the render tree
+// these could be defined in separate namespace to avoid matching these generic signatures unintentionally.
+
+template<typename Item>
+TextStream& operator<<(TextStream& ts, const Vector<Item*>& v)
+{
+ ts << "[";
+
+ for (unsigned i = 0; i < v.size(); i++) {
+ ts << *v[i];
+ if (i < v.size() - 1)
+ ts << ", ";
+ }
+
+ ts << "]";
+ return ts;
+}
+
+template<typename Pointer>
+TextStream& operator<<(TextStream& ts, Pointer* t)
+{
+ ts << reinterpret_cast<intptr_t>(t);
+ return ts;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+
+#endif // SVGRenderTreeAsText_h
diff --git a/Source/WebCore/rendering/SVGResources.cpp b/Source/WebCore/rendering/SVGResources.cpp
new file mode 100644
index 0000000..e162f83
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResources.cpp
@@ -0,0 +1,684 @@
+/*
+ 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
+ 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 "SVGResources.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "SVGFilterElement.h"
+#include "SVGGradientElement.h"
+#include "SVGNames.h"
+#include "SVGPaint.h"
+#include "SVGPatternElement.h"
+#include "SVGRenderStyle.h"
+#include "SVGURIReference.h"
+
+namespace WebCore {
+
+SVGResources::SVGResources()
+ : m_clipperFilterMaskerData(0)
+ , m_markerData(0)
+ , m_fillStrokeData(0)
+ , m_linkedResource(0)
+{
+}
+
+static HashSet<AtomicStringImpl*>& clipperFilterMaskerTags()
+{
+ DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, s_tagList, ());
+ if (s_tagList.isEmpty()) {
+ // "container elements": http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
+ // "graphics elements" : http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
+ s_tagList.add(SVGNames::aTag.localName().impl());
+ s_tagList.add(SVGNames::circleTag.localName().impl());
+ s_tagList.add(SVGNames::ellipseTag.localName().impl());
+ s_tagList.add(SVGNames::glyphTag.localName().impl());
+ s_tagList.add(SVGNames::gTag.localName().impl());
+ s_tagList.add(SVGNames::imageTag.localName().impl());
+ s_tagList.add(SVGNames::lineTag.localName().impl());
+ s_tagList.add(SVGNames::markerTag.localName().impl());
+ s_tagList.add(SVGNames::maskTag.localName().impl());
+ s_tagList.add(SVGNames::missing_glyphTag.localName().impl());
+ s_tagList.add(SVGNames::pathTag.localName().impl());
+ s_tagList.add(SVGNames::polygonTag.localName().impl());
+ s_tagList.add(SVGNames::polylineTag.localName().impl());
+ s_tagList.add(SVGNames::rectTag.localName().impl());
+ s_tagList.add(SVGNames::svgTag.localName().impl());
+ s_tagList.add(SVGNames::textTag.localName().impl());
+ s_tagList.add(SVGNames::useTag.localName().impl());
+
+ // Not listed in the definitions is the clipPath element, the SVG spec says though:
+ // The "clipPath" element or any of its children can specify property "clip-path".
+ // So we have to add clipPathTag here, otherwhise clip-path on clipPath will fail.
+ // (Already mailed SVG WG, waiting for a solution)
+ s_tagList.add(SVGNames::clipPathTag.localName().impl());
+
+ // Not listed in the definitions are the text content elements, though filter/clipper/masker on tspan/text/.. is allowed.
+ // (Already mailed SVG WG, waiting for a solution)
+ s_tagList.add(SVGNames::altGlyphTag.localName().impl());
+ s_tagList.add(SVGNames::textPathTag.localName().impl());
+ s_tagList.add(SVGNames::trefTag.localName().impl());
+ s_tagList.add(SVGNames::tspanTag.localName().impl());
+
+ // Elements that we ignore, as it doesn't make any sense.
+ // defs, pattern, switch (FIXME: Mail SVG WG about these)
+ // symbol (is converted to a svg element, when referenced by use, we can safely ignore it.)
+ }
+
+ return s_tagList;
+}
+
+static HashSet<AtomicStringImpl*>& markerTags()
+{
+ DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, s_tagList, ());
+ if (s_tagList.isEmpty()) {
+ s_tagList.add(SVGNames::lineTag.localName().impl());
+ s_tagList.add(SVGNames::pathTag.localName().impl());
+ s_tagList.add(SVGNames::polygonTag.localName().impl());
+ s_tagList.add(SVGNames::polylineTag.localName().impl());
+ }
+
+ return s_tagList;
+}
+
+static HashSet<AtomicStringImpl*>& fillAndStrokeTags()
+{
+ DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, s_tagList, ());
+ if (s_tagList.isEmpty()) {
+ s_tagList.add(SVGNames::altGlyphTag.localName().impl());
+ s_tagList.add(SVGNames::circleTag.localName().impl());
+ s_tagList.add(SVGNames::ellipseTag.localName().impl());
+ s_tagList.add(SVGNames::lineTag.localName().impl());
+ s_tagList.add(SVGNames::pathTag.localName().impl());
+ s_tagList.add(SVGNames::polygonTag.localName().impl());
+ s_tagList.add(SVGNames::polylineTag.localName().impl());
+ s_tagList.add(SVGNames::rectTag.localName().impl());
+ s_tagList.add(SVGNames::textTag.localName().impl());
+ s_tagList.add(SVGNames::textPathTag.localName().impl());
+ s_tagList.add(SVGNames::trefTag.localName().impl());
+ s_tagList.add(SVGNames::tspanTag.localName().impl());
+ }
+
+ return s_tagList;
+}
+
+static HashSet<AtomicStringImpl*>& chainableResourceTags()
+{
+ DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, s_tagList, ());
+ if (s_tagList.isEmpty()) {
+ s_tagList.add(SVGNames::linearGradientTag.localName().impl());
+ s_tagList.add(SVGNames::filterTag.localName().impl());
+ s_tagList.add(SVGNames::patternTag.localName().impl());
+ s_tagList.add(SVGNames::radialGradientTag.localName().impl());
+ }
+
+ return s_tagList;
+}
+
+static inline String targetReferenceFromResource(SVGElement* element)
+{
+ String target;
+ if (element->hasTagName(SVGNames::patternTag))
+ target = static_cast<SVGPatternElement*>(element)->href();
+ else if (element->hasTagName(SVGNames::linearGradientTag) || element->hasTagName(SVGNames::radialGradientTag))
+ target = static_cast<SVGGradientElement*>(element)->href();
+#if ENABLE(FILTERS)
+ else if (element->hasTagName(SVGNames::filterTag))
+ target = static_cast<SVGFilterElement*>(element)->href();
+#endif
+ else
+ ASSERT_NOT_REACHED();
+
+ return SVGURIReference::getTarget(target);
+}
+
+static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document* document, SVGPaint* paint, AtomicString& id, bool& hasPendingResource)
+{
+ ASSERT(paint);
+
+ SVGPaint::SVGPaintType paintType = paint->paintType();
+ if (paintType != SVGPaint::SVG_PAINTTYPE_URI && paintType != SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)
+ return 0;
+
+ id = SVGURIReference::getTarget(paint->uri());
+ RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id);
+ if (!container) {
+ hasPendingResource = true;
+ return 0;
+ }
+
+ RenderSVGResourceType resourceType = container->resourceType();
+ if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
+ return 0;
+
+ return container;
+}
+
+static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement* element)
+{
+ ASSERT(element);
+ if (!element->isStyled())
+ return;
+
+ extensions->addPendingResource(id, static_cast<SVGStyledElement*>(element));
+}
+
+bool SVGResources::buildCachedResources(const RenderObject* object, const SVGRenderStyle* style)
+{
+ ASSERT(object);
+ ASSERT(style);
+
+ Node* node = object->node();
+ ASSERT(node);
+ ASSERT(node->isSVGElement());
+
+ SVGElement* element = static_cast<SVGElement*>(node);
+ if (!element)
+ return false;
+
+ Document* document = object->document();
+ ASSERT(document);
+
+ SVGDocumentExtensions* extensions = document->accessSVGExtensions();
+ ASSERT(extensions);
+
+ AtomicStringImpl* tagNameImpl = element->tagQName().localName().impl();
+ if (!tagNameImpl)
+ return false;
+
+ bool foundResources = false;
+ if (clipperFilterMaskerTags().contains(tagNameImpl)) {
+ if (style->hasClipper()) {
+ AtomicString id(style->clipperResource());
+ if (setClipper(getRenderSVGResourceById<RenderSVGResourceClipper>(document, id)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, id, element);
+ }
+
+#if ENABLE(FILTERS)
+ if (style->hasFilter()) {
+ AtomicString id(style->filterResource());
+ if (setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(document, id)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, id, element);
+ }
+#endif
+
+ if (style->hasMasker()) {
+ AtomicString id(style->maskerResource());
+ if (setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(document, id)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, id, element);
+ }
+ }
+
+ if (markerTags().contains(tagNameImpl) && style->hasMarkers()) {
+ AtomicString markerStartId(style->markerStartResource());
+ if (setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerStartId)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, markerStartId, element);
+
+ AtomicString markerMidId(style->markerMidResource());
+ if (setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerMidId)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, markerMidId, element);
+
+ AtomicString markerEndId(style->markerEndResource());
+ if (setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerEndId)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, markerEndId, element);
+ }
+
+ if (fillAndStrokeTags().contains(tagNameImpl)) {
+ if (style->hasFill()) {
+ bool hasPendingResource = false;
+ AtomicString id;
+ if (setFill(paintingResourceFromSVGPaint(document, style->fillPaint(), id, hasPendingResource)))
+ foundResources = true;
+ else if (hasPendingResource)
+ registerPendingResource(extensions, id, element);
+ }
+
+ if (style->hasStroke()) {
+ bool hasPendingResource = false;
+ AtomicString id;
+ if (setStroke(paintingResourceFromSVGPaint(document, style->strokePaint(), id, hasPendingResource)))
+ foundResources = true;
+ else if (hasPendingResource)
+ registerPendingResource(extensions, id, element);
+ }
+ }
+
+ if (chainableResourceTags().contains(tagNameImpl)) {
+ AtomicString id(targetReferenceFromResource(element));
+ if (setLinkedResource(getRenderSVGResourceContainerById(document, id)))
+ foundResources = true;
+ else
+ registerPendingResource(extensions, id, element);
+ }
+
+ return foundResources;
+}
+
+void SVGResources::removeClientFromCache(RenderObject* object, bool markForInvalidation) const
+{
+ if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
+ return;
+
+ if (m_linkedResource) {
+ ASSERT(!m_clipperFilterMaskerData);
+ ASSERT(!m_markerData);
+ ASSERT(!m_fillStrokeData);
+ m_linkedResource->removeClientFromCache(object, markForInvalidation);
+ return;
+ }
+
+ if (m_clipperFilterMaskerData) {
+ if (m_clipperFilterMaskerData->clipper)
+ m_clipperFilterMaskerData->clipper->removeClientFromCache(object, markForInvalidation);
+#if ENABLE(FILTERS)
+ if (m_clipperFilterMaskerData->filter)
+ m_clipperFilterMaskerData->filter->removeClientFromCache(object, markForInvalidation);
+#endif
+ if (m_clipperFilterMaskerData->masker)
+ m_clipperFilterMaskerData->masker->removeClientFromCache(object, markForInvalidation);
+ }
+
+ if (m_markerData) {
+ if (m_markerData->markerStart)
+ m_markerData->markerStart->removeClientFromCache(object, markForInvalidation);
+ if (m_markerData->markerMid)
+ m_markerData->markerMid->removeClientFromCache(object, markForInvalidation);
+ if (m_markerData->markerEnd)
+ m_markerData->markerEnd->removeClientFromCache(object, markForInvalidation);
+ }
+
+ if (m_fillStrokeData) {
+ if (m_fillStrokeData->fill)
+ m_fillStrokeData->fill->removeClientFromCache(object, markForInvalidation);
+ if (m_fillStrokeData->stroke)
+ m_fillStrokeData->stroke->removeClientFromCache(object, markForInvalidation);
+ }
+}
+
+void SVGResources::resourceDestroyed(RenderSVGResourceContainer* resource)
+{
+ ASSERT(resource);
+ if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
+ return;
+
+ if (m_linkedResource == resource) {
+ ASSERT(!m_clipperFilterMaskerData);
+ ASSERT(!m_markerData);
+ ASSERT(!m_fillStrokeData);
+ m_linkedResource->removeAllClientsFromCache();
+ m_linkedResource = 0;
+ return;
+ }
+
+ switch (resource->resourceType()) {
+ case MaskerResourceType:
+ if (!m_clipperFilterMaskerData)
+ break;
+ if (m_clipperFilterMaskerData->masker == resource) {
+ m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
+ m_clipperFilterMaskerData->masker = 0;
+ }
+ break;
+ case MarkerResourceType:
+ if (!m_markerData)
+ break;
+ if (m_markerData->markerStart == resource) {
+ m_markerData->markerStart->removeAllClientsFromCache();
+ m_markerData->markerStart = 0;
+ }
+ if (m_markerData->markerMid == resource) {
+ m_markerData->markerMid->removeAllClientsFromCache();
+ m_markerData->markerMid = 0;
+ }
+ if (m_markerData->markerEnd == resource) {
+ m_markerData->markerEnd->removeAllClientsFromCache();
+ m_markerData->markerEnd = 0;
+ }
+ break;
+ case PatternResourceType:
+ case LinearGradientResourceType:
+ case RadialGradientResourceType:
+ if (!m_fillStrokeData)
+ break;
+ if (m_fillStrokeData->fill == resource) {
+ m_fillStrokeData->fill->removeAllClientsFromCache();
+ m_fillStrokeData->fill = 0;
+ }
+ if (m_fillStrokeData->stroke == resource) {
+ m_fillStrokeData->stroke->removeAllClientsFromCache();
+ m_fillStrokeData->stroke = 0;
+ }
+ break;
+ case FilterResourceType:
+#if ENABLE(FILTERS)
+ if (!m_clipperFilterMaskerData)
+ break;
+ if (m_clipperFilterMaskerData->filter == resource) {
+ m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
+ m_clipperFilterMaskerData->filter = 0;
+ }
+#else
+ ASSERT_NOT_REACHED();
+#endif
+ break;
+ case ClipperResourceType:
+ if (!m_clipperFilterMaskerData)
+ break;
+ if (m_clipperFilterMaskerData->clipper == resource) {
+ m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
+ m_clipperFilterMaskerData->clipper = 0;
+ }
+ break;
+ case SolidColorResourceType:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
+{
+ if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
+ return;
+
+ if (m_linkedResource) {
+ ASSERT(!m_clipperFilterMaskerData);
+ ASSERT(!m_markerData);
+ ASSERT(!m_fillStrokeData);
+ set.add(m_linkedResource);
+ return;
+ }
+
+ if (m_clipperFilterMaskerData) {
+ if (m_clipperFilterMaskerData->clipper)
+ set.add(m_clipperFilterMaskerData->clipper);
+#if ENABLE(FILTERS)
+ if (m_clipperFilterMaskerData->filter)
+ set.add(m_clipperFilterMaskerData->filter);
+#endif
+ if (m_clipperFilterMaskerData->masker)
+ set.add(m_clipperFilterMaskerData->masker);
+ }
+
+ if (m_markerData) {
+ if (m_markerData->markerStart)
+ set.add(m_markerData->markerStart);
+ if (m_markerData->markerMid)
+ set.add(m_markerData->markerMid);
+ if (m_markerData->markerEnd)
+ set.add(m_markerData->markerEnd);
+ }
+
+ if (m_fillStrokeData) {
+ if (m_fillStrokeData->fill)
+ set.add(m_fillStrokeData->fill);
+ if (m_fillStrokeData->stroke)
+ set.add(m_fillStrokeData->stroke);
+ }
+}
+
+bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
+{
+ if (!clipper)
+ return false;
+
+ ASSERT(clipper->resourceType() == ClipperResourceType);
+
+ if (!m_clipperFilterMaskerData)
+ m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
+
+ m_clipperFilterMaskerData->clipper = clipper;
+ return true;
+}
+
+void SVGResources::resetClipper()
+{
+ ASSERT(m_clipperFilterMaskerData);
+ ASSERT(m_clipperFilterMaskerData->clipper);
+ m_clipperFilterMaskerData->clipper = 0;
+}
+
+#if ENABLE(FILTERS)
+bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
+{
+ if (!filter)
+ return false;
+
+ ASSERT(filter->resourceType() == FilterResourceType);
+
+ if (!m_clipperFilterMaskerData)
+ m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
+
+ m_clipperFilterMaskerData->filter = filter;
+ return true;
+}
+
+void SVGResources::resetFilter()
+{
+ ASSERT(m_clipperFilterMaskerData);
+ ASSERT(m_clipperFilterMaskerData->filter);
+ m_clipperFilterMaskerData->filter = 0;
+}
+#endif
+
+bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
+{
+ if (!markerStart)
+ return false;
+
+ ASSERT(markerStart->resourceType() == MarkerResourceType);
+
+ if (!m_markerData)
+ m_markerData = MarkerData::create();
+
+ m_markerData->markerStart = markerStart;
+ return true;
+}
+
+void SVGResources::resetMarkerStart()
+{
+ ASSERT(m_markerData);
+ ASSERT(m_markerData->markerStart);
+ m_markerData->markerStart = 0;
+}
+
+bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
+{
+ if (!markerMid)
+ return false;
+
+ ASSERT(markerMid->resourceType() == MarkerResourceType);
+
+ if (!m_markerData)
+ m_markerData = MarkerData::create();
+
+ m_markerData->markerMid = markerMid;
+ return true;
+}
+
+void SVGResources::resetMarkerMid()
+{
+ ASSERT(m_markerData);
+ ASSERT(m_markerData->markerMid);
+ m_markerData->markerMid = 0;
+}
+
+bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
+{
+ if (!markerEnd)
+ return false;
+
+ ASSERT(markerEnd->resourceType() == MarkerResourceType);
+
+ if (!m_markerData)
+ m_markerData = MarkerData::create();
+
+ m_markerData->markerEnd = markerEnd;
+ return true;
+}
+
+void SVGResources::resetMarkerEnd()
+{
+ ASSERT(m_markerData);
+ ASSERT(m_markerData->markerEnd);
+ m_markerData->markerEnd = 0;
+}
+
+bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
+{
+ if (!masker)
+ return false;
+
+ ASSERT(masker->resourceType() == MaskerResourceType);
+
+ if (!m_clipperFilterMaskerData)
+ m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
+
+ m_clipperFilterMaskerData->masker = masker;
+ return true;
+}
+
+void SVGResources::resetMasker()
+{
+ ASSERT(m_clipperFilterMaskerData);
+ ASSERT(m_clipperFilterMaskerData->masker);
+ m_clipperFilterMaskerData->masker = 0;
+}
+
+bool SVGResources::setFill(RenderSVGResourceContainer* fill)
+{
+ if (!fill)
+ return false;
+
+ ASSERT(fill->resourceType() == PatternResourceType
+ || fill->resourceType() == LinearGradientResourceType
+ || fill->resourceType() == RadialGradientResourceType);
+
+ if (!m_fillStrokeData)
+ m_fillStrokeData = FillStrokeData::create();
+
+ m_fillStrokeData->fill = fill;
+ return true;
+}
+
+void SVGResources::resetFill()
+{
+ ASSERT(m_fillStrokeData);
+ ASSERT(m_fillStrokeData->fill);
+ m_fillStrokeData->fill = 0;
+}
+
+bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
+{
+ if (!stroke)
+ return false;
+
+ ASSERT(stroke->resourceType() == PatternResourceType
+ || stroke->resourceType() == LinearGradientResourceType
+ || stroke->resourceType() == RadialGradientResourceType);
+
+ if (!m_fillStrokeData)
+ m_fillStrokeData = FillStrokeData::create();
+
+ m_fillStrokeData->stroke = stroke;
+ return true;
+}
+
+void SVGResources::resetStroke()
+{
+ ASSERT(m_fillStrokeData);
+ ASSERT(m_fillStrokeData->stroke);
+ m_fillStrokeData->stroke = 0;
+}
+
+bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
+{
+ if (!linkedResource)
+ return false;
+
+ m_linkedResource = linkedResource;
+ return true;
+}
+
+void SVGResources::resetLinkedResource()
+{
+ ASSERT(m_linkedResource);
+ m_linkedResource = 0;
+}
+
+#ifndef NDEBUG
+void SVGResources::dump(const RenderObject* object)
+{
+ ASSERT(object);
+ ASSERT(object->node());
+
+ fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
+ fprintf(stderr, " | DOM Tree:\n");
+ object->node()->showTreeForThis();
+
+ fprintf(stderr, "\n | List of resources:\n");
+ if (m_clipperFilterMaskerData) {
+ if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
+ fprintf(stderr, " |-> Clipper : %p (node=%p)\n", clipper, clipper->node());
+#if ENABLE(FILTERS)
+ if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
+ fprintf(stderr, " |-> Filter : %p (node=%p)\n", filter, filter->node());
+#endif
+ if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
+ fprintf(stderr, " |-> Masker : %p (node=%p)\n", masker, masker->node());
+ }
+
+ if (m_markerData) {
+ if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
+ fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->node());
+ if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
+ fprintf(stderr, " |-> MarkerMid : %p (node=%p)\n", markerMid, markerMid->node());
+ if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
+ fprintf(stderr, " |-> MarkerEnd : %p (node=%p)\n", markerEnd, markerEnd->node());
+ }
+
+ if (m_fillStrokeData) {
+ if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
+ fprintf(stderr, " |-> Fill : %p (node=%p)\n", fill, fill->node());
+ if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
+ fprintf(stderr, " |-> Stroke : %p (node=%p)\n", stroke, stroke->node());
+ }
+
+ if (m_linkedResource)
+ fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->node());
+}
+#endif
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/SVGResources.h b/Source/WebCore/rendering/SVGResources.h
new file mode 100644
index 0000000..49591cf
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResources.h
@@ -0,0 +1,179 @@
+/*
+ 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
+ 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 SVGResources_h
+#define SVGResources_h
+
+#if ENABLE(SVG)
+#include <wtf/HashSet.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class Document;
+class RenderObject;
+class RenderSVGResourceClipper;
+class RenderSVGResourceContainer;
+class RenderSVGResourceFilter;
+class RenderSVGResourceMarker;
+class RenderSVGResourceMasker;
+class SVGRenderStyle;
+
+// Holds a set of resources associated with a RenderObject
+class SVGResources : public Noncopyable {
+public:
+ SVGResources();
+
+ bool buildCachedResources(const RenderObject*, const SVGRenderStyle*);
+
+ // Ordinary resources
+ RenderSVGResourceClipper* clipper() const { return m_clipperFilterMaskerData ? m_clipperFilterMaskerData->clipper : 0; }
+#if ENABLE(FILTERS)
+ RenderSVGResourceFilter* filter() const { return m_clipperFilterMaskerData ? m_clipperFilterMaskerData->filter : 0; }
+#endif
+ RenderSVGResourceMarker* markerStart() const { return m_markerData ? m_markerData->markerStart : 0; }
+ RenderSVGResourceMarker* markerMid() const { return m_markerData ? m_markerData->markerMid : 0; }
+ RenderSVGResourceMarker* markerEnd() const { return m_markerData ? m_markerData->markerEnd : 0; }
+ RenderSVGResourceMasker* masker() const { return m_clipperFilterMaskerData ? m_clipperFilterMaskerData->masker : 0; }
+
+ // Paint servers
+ RenderSVGResourceContainer* fill() const { return m_fillStrokeData ? m_fillStrokeData->fill : 0; }
+ RenderSVGResourceContainer* stroke() const { return m_fillStrokeData ? m_fillStrokeData->stroke : 0; }
+
+ // Chainable resources - linked through xlink:href
+ RenderSVGResourceContainer* linkedResource() const { return m_linkedResource; }
+
+ void buildSetOfResources(HashSet<RenderSVGResourceContainer*>&);
+
+ // Methods operating on all cached resources
+ void removeClientFromCache(RenderObject*, bool markForInvalidation = true) const;
+ void resourceDestroyed(RenderSVGResourceContainer*);
+
+#ifndef NDEBUG
+ void dump(const RenderObject*);
+#endif
+
+private:
+ friend class SVGResourcesCycleSolver;
+
+ // Only used by SVGResourcesCache cycle detection logic
+ void resetClipper();
+#if ENABLE(FILTERS)
+ void resetFilter();
+#endif
+ void resetMarkerStart();
+ void resetMarkerMid();
+ void resetMarkerEnd();
+ void resetMasker();
+ void resetFill();
+ void resetStroke();
+ void resetLinkedResource();
+
+private:
+ bool setClipper(RenderSVGResourceClipper*);
+#if ENABLE(FILTERS)
+ bool setFilter(RenderSVGResourceFilter*);
+#endif
+ bool setMarkerStart(RenderSVGResourceMarker*);
+ bool setMarkerMid(RenderSVGResourceMarker*);
+ bool setMarkerEnd(RenderSVGResourceMarker*);
+ bool setMasker(RenderSVGResourceMasker*);
+ bool setFill(RenderSVGResourceContainer*);
+ bool setStroke(RenderSVGResourceContainer*);
+ bool setLinkedResource(RenderSVGResourceContainer*);
+
+ // From SVG 1.1 2nd Edition
+ // clipper: 'container elements' and 'graphics elements'
+ // filter: 'container elements' and 'graphics elements'
+ // masker: 'container elements' and 'graphics elements'
+ // -> a, circle, defs, ellipse, glyph, g, image, line, marker, mask, missing-glyph, path, pattern, polygon, polyline, rect, svg, switch, symbol, text, use
+ struct ClipperFilterMaskerData {
+ ClipperFilterMaskerData()
+ : clipper(0)
+#if ENABLE(FILTERS)
+ , filter(0)
+#endif
+ , masker(0)
+ {
+ }
+
+ static PassOwnPtr<ClipperFilterMaskerData> create()
+ {
+ return new ClipperFilterMaskerData;
+ }
+
+ RenderSVGResourceClipper* clipper;
+#if ENABLE(FILTERS)
+ RenderSVGResourceFilter* filter;
+#endif
+ RenderSVGResourceMasker* masker;
+ };
+
+ // From SVG 1.1 2nd Edition
+ // marker: line, path, polygon, polyline
+ struct MarkerData {
+ MarkerData()
+ : markerStart(0)
+ , markerMid(0)
+ , markerEnd(0)
+ {
+ }
+
+ static PassOwnPtr<MarkerData> create()
+ {
+ return new MarkerData;
+ }
+
+ RenderSVGResourceMarker* markerStart;
+ RenderSVGResourceMarker* markerMid;
+ RenderSVGResourceMarker* markerEnd;
+ };
+
+ // From SVG 1.1 2nd Edition
+ // fill: 'shapes' and 'text content elements'
+ // stroke: 'shapes' and 'text content elements'
+ // -> altGlyph, circle, ellipse, line, path, polygon, polyline, rect, text, textPath, tref, tspan
+ struct FillStrokeData {
+ FillStrokeData()
+ : fill(0)
+ , stroke(0)
+ {
+ }
+
+ static PassOwnPtr<FillStrokeData> create()
+ {
+ return new FillStrokeData;
+ }
+
+ RenderSVGResourceContainer* fill;
+ RenderSVGResourceContainer* stroke;
+ };
+
+ OwnPtr<ClipperFilterMaskerData> m_clipperFilterMaskerData;
+ OwnPtr<MarkerData> m_markerData;
+ OwnPtr<FillStrokeData> m_fillStrokeData;
+ RenderSVGResourceContainer* m_linkedResource;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/SVGResourcesCache.cpp b/Source/WebCore/rendering/SVGResourcesCache.cpp
new file mode 100644
index 0000000..88cbb3a
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResourcesCache.cpp
@@ -0,0 +1,165 @@
+/*
+ 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
+ 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 "SVGResourcesCache.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGResourceContainer.h"
+#include "SVGDocumentExtensions.h"
+#include "SVGResources.h"
+#include "SVGResourcesCycleSolver.h"
+
+namespace WebCore {
+
+SVGResourcesCache::SVGResourcesCache()
+{
+}
+
+SVGResourcesCache::~SVGResourcesCache()
+{
+ deleteAllValues(m_cache);
+}
+
+void SVGResourcesCache::addResourcesFromRenderObject(RenderObject* object, const RenderStyle* style)
+{
+ ASSERT(object);
+ ASSERT(style);
+ ASSERT(!m_cache.contains(object));
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ // Build a list of all resources associated with the passed RenderObject
+ SVGResources* resources = new SVGResources;
+ if (!resources->buildCachedResources(object, svgStyle)) {
+ delete resources;
+ return;
+ }
+
+ // Put object in cache.
+ m_cache.set(object, resources);
+
+ // Run cycle-detection _afterwards_, so self-references can be caught as well.
+ SVGResourcesCycleSolver solver(object, resources);
+ solver.resolveCycles();
+
+ // Walk resources and register the render object at each resources.
+ HashSet<RenderSVGResourceContainer*> resourceSet;
+ resources->buildSetOfResources(resourceSet);
+
+ HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
+ (*it)->addClient(object);
+}
+
+void SVGResourcesCache::removeResourcesFromRenderObject(RenderObject* object)
+{
+ if (!m_cache.contains(object))
+ return;
+
+ SVGResources* resources = m_cache.get(object);
+
+ // Walk resources and register the render object at each resources.
+ HashSet<RenderSVGResourceContainer*> resourceSet;
+ resources->buildSetOfResources(resourceSet);
+
+ HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it)
+ (*it)->removeClient(object);
+
+ delete m_cache.take(object);
+}
+
+static inline SVGResourcesCache* resourcesCacheFromRenderObject(RenderObject* renderer)
+{
+ Document* document = renderer->document();
+ ASSERT(document);
+
+ SVGDocumentExtensions* extensions = document->accessSVGExtensions();
+ ASSERT(extensions);
+
+ SVGResourcesCache* cache = extensions->resourcesCache();
+ ASSERT(cache);
+
+ return cache;
+}
+
+SVGResources* SVGResourcesCache::cachedResourcesForRenderObject(RenderObject* renderer)
+{
+ ASSERT(renderer);
+ SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
+ if (!cache->m_cache.contains(renderer))
+ return 0;
+
+ return cache->m_cache.get(renderer);
+}
+
+void SVGResourcesCache::clientLayoutChanged(RenderObject* object)
+{
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
+ if (!resources)
+ return;
+
+ resources->removeClientFromCache(object);
+}
+
+void SVGResourcesCache::clientStyleChanged(RenderObject* renderer, StyleDifference diff, const RenderStyle* newStyle)
+{
+ ASSERT(renderer);
+ if (diff == StyleDifferenceEqual)
+ return;
+
+ clientUpdatedFromElement(renderer, newStyle);
+ RenderSVGResource::markForLayoutAndParentResourceInvalidation(renderer, false);
+}
+
+void SVGResourcesCache::clientUpdatedFromElement(RenderObject* renderer, const RenderStyle* newStyle)
+{
+ ASSERT(renderer);
+ ASSERT(renderer->parent());
+
+ SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
+ cache->removeResourcesFromRenderObject(renderer);
+ cache->addResourcesFromRenderObject(renderer, newStyle);
+}
+
+void SVGResourcesCache::clientDestroyed(RenderObject* renderer)
+{
+ ASSERT(renderer);
+ SVGResourcesCache* cache = resourcesCacheFromRenderObject(renderer);
+ cache->removeResourcesFromRenderObject(renderer);
+}
+
+void SVGResourcesCache::resourceDestroyed(RenderSVGResourceContainer* resource)
+{
+ ASSERT(resource);
+ SVGResourcesCache* cache = resourcesCacheFromRenderObject(resource);
+
+ // The resource itself may have clients, that need to be notified.
+ cache->removeResourcesFromRenderObject(resource);
+
+ HashMap<RenderObject*, SVGResources*>::iterator end = cache->m_cache.end();
+ for (HashMap<RenderObject*, SVGResources*>::iterator it = cache->m_cache.begin(); it != end; ++it)
+ it->second->resourceDestroyed(resource);
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/SVGResourcesCache.h b/Source/WebCore/rendering/SVGResourcesCache.h
new file mode 100644
index 0000000..4a61570
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResourcesCache.h
@@ -0,0 +1,65 @@
+/*
+ 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
+ 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 SVGResourcesCache_h
+#define SVGResourcesCache_h
+
+#if ENABLE(SVG)
+#include "RenderStyleConstants.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class RenderObject;
+class RenderStyle;
+class RenderSVGResourceContainer;
+class SVGResources;
+
+class SVGResourcesCache : public Noncopyable {
+public:
+ SVGResourcesCache();
+ ~SVGResourcesCache();
+
+ void addResourcesFromRenderObject(RenderObject*, const RenderStyle*);
+ void removeResourcesFromRenderObject(RenderObject*);
+ static SVGResources* cachedResourcesForRenderObject(RenderObject*);
+
+ // Called from all SVG renderers destroy() methods - except for RenderSVGResourceContainer.
+ static void clientDestroyed(RenderObject*);
+
+ // Called from all SVG renderers layout() methods.
+ static void clientLayoutChanged(RenderObject*);
+
+ // Called from all SVG renderers styleDidChange() methods.
+ static void clientStyleChanged(RenderObject*, StyleDifference, const RenderStyle* newStyle);
+
+ // Called from all SVG renderers updateFromElement() methods.
+ static void clientUpdatedFromElement(RenderObject*, const RenderStyle* newStyle);
+
+ // Called from RenderSVGResourceContainer::destroy().
+ static void resourceDestroyed(RenderSVGResourceContainer*);
+
+private:
+ HashMap<RenderObject*, SVGResources*> m_cache;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/SVGResourcesCycleSolver.cpp b/Source/WebCore/rendering/SVGResourcesCycleSolver.cpp
new file mode 100644
index 0000000..8cd2e80
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResourcesCycleSolver.cpp
@@ -0,0 +1,213 @@
+/*
+ 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
+ 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 "SVGResourcesCycleSolver.h"
+
+// Set to a value > 0, to debug the resource cache.
+#define DEBUG_CYCLE_DETECTION 0
+
+#if ENABLE(SVG)
+#include "RenderSVGResourceClipper.h"
+#include "RenderSVGResourceFilter.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceMasker.h"
+#include "SVGFilterElement.h"
+#include "SVGGradientElement.h"
+#include "SVGPatternElement.h"
+#include "SVGResources.h"
+#include "SVGResourcesCache.h"
+
+namespace WebCore {
+
+SVGResourcesCycleSolver::SVGResourcesCycleSolver(RenderObject* renderer, SVGResources* resources)
+ : m_renderer(renderer)
+ , m_resources(resources)
+{
+ ASSERT(m_renderer);
+ ASSERT(m_resources);
+}
+
+SVGResourcesCycleSolver::~SVGResourcesCycleSolver()
+{
+}
+
+bool SVGResourcesCycleSolver::resourceContainsCycles(RenderObject* renderer) const
+{
+ ASSERT(renderer);
+
+ // First operate on the resources of the given renderer.
+ // <marker id="a"> <path marker-start="url(#b)"/> ...
+ // <marker id="b" marker-start="url(#a)"/>
+ if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer)) {
+ HashSet<RenderSVGResourceContainer*> resourceSet;
+ resources->buildSetOfResources(resourceSet);
+
+ // Walk all resources and check wheter they reference any resource contained in the resources set.
+ HashSet<RenderSVGResourceContainer*>::iterator end = resourceSet.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = resourceSet.begin(); it != end; ++it) {
+ if (m_allResources.contains(*it))
+ return true;
+ }
+ }
+
+ // Then operate on the child resources of the given renderer.
+ // <marker id="a"> <path marker-start="url(#b)"/> ...
+ // <marker id="b"> <path marker-start="url(#a)"/> ...
+ for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) {
+ SVGResources* childResources = SVGResourcesCache::cachedResourcesForRenderObject(child);
+ if (!childResources)
+ continue;
+
+ // A child of the given 'resource' contains resources.
+ HashSet<RenderSVGResourceContainer*> childSet;
+ childResources->buildSetOfResources(childSet);
+
+ // Walk all child resources and check wheter they reference any resource contained in the resources set.
+ HashSet<RenderSVGResourceContainer*>::iterator end = childSet.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = childSet.begin(); it != end; ++it) {
+ if (m_allResources.contains(*it))
+ return true;
+ }
+
+ // Walk children recursively, stop immediately if we found a cycle
+ if (resourceContainsCycles(child))
+ return true;
+ }
+
+ return false;
+}
+
+void SVGResourcesCycleSolver::resolveCycles()
+{
+ ASSERT(m_allResources.isEmpty());
+
+#if DEBUG_CYCLE_DETECTION > 0
+ fprintf(stderr, "\nBefore cycle detection:\n");
+ m_resources->dump(m_renderer);
+#endif
+
+ // Stash all resources into a HashSet for the ease of traversing.
+ HashSet<RenderSVGResourceContainer*> localResources;
+ m_resources->buildSetOfResources(localResources);
+ ASSERT(!localResources.isEmpty());
+
+ // Add all parent resource containers to the HashSet.
+ HashSet<RenderSVGResourceContainer*> parentResources;
+ RenderObject* parent = m_renderer->parent();
+ while (parent) {
+ if (parent->isSVGResourceContainer())
+ parentResources.add(parent->toRenderSVGResourceContainer());
+ parent = parent->parent();
+ }
+
+#if DEBUG_CYCLE_DETECTION > 0
+ fprintf(stderr, "\nDetecting wheter any resources references any of following objects:\n");
+ {
+ fprintf(stderr, "Local resources:\n");
+ HashSet<RenderSVGResourceContainer*>::iterator end = localResources.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = localResources.begin(); it != end; ++it)
+ fprintf(stderr, "|> %s: object=%p (node=%p)\n", (*it)->renderName(), *it, (*it)->node());
+
+ fprintf(stderr, "Parent resources:\n");
+ end = parentResources.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = parentResources.begin(); it != end; ++it)
+ fprintf(stderr, "|> %s: object=%p (node=%p)\n", (*it)->renderName(), *it, (*it)->node());
+ }
+#endif
+
+ // Build combined set of local and parent resources.
+ m_allResources = localResources;
+ HashSet<RenderSVGResourceContainer*>::iterator end = parentResources.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = parentResources.begin(); it != end; ++it)
+ m_allResources.add(*it);
+
+ // If we're a resource, add ourselves to the HashSet.
+ if (m_renderer->isSVGResourceContainer())
+ m_allResources.add(m_renderer->toRenderSVGResourceContainer());
+
+ ASSERT(!m_allResources.isEmpty());
+
+ // The job of this function is to determine wheter any of the 'resources' associated with the given 'renderer'
+ // references us (or wheter any of its kids references us) -> that's a cycle, we need to find and break it.
+ end = localResources.end();
+ for (HashSet<RenderSVGResourceContainer*>::iterator it = localResources.begin(); it != end; ++it) {
+ RenderSVGResourceContainer* resource = *it;
+ if (parentResources.contains(resource) || resourceContainsCycles(resource))
+ breakCycle(resource);
+ }
+
+#if DEBUG_CYCLE_DETECTION > 0
+ fprintf(stderr, "\nAfter cycle detection:\n");
+ m_resources->dump(m_renderer);
+#endif
+
+ m_allResources.clear();
+}
+
+void SVGResourcesCycleSolver::breakCycle(RenderSVGResourceContainer* resourceLeadingToCycle)
+{
+ ASSERT(resourceLeadingToCycle);
+ if (resourceLeadingToCycle == m_resources->linkedResource()) {
+ m_resources->resetLinkedResource();
+ return;
+ }
+
+ switch (resourceLeadingToCycle->resourceType()) {
+ case MaskerResourceType:
+ ASSERT(resourceLeadingToCycle == m_resources->masker());
+ m_resources->resetMasker();
+ break;
+ case MarkerResourceType:
+ ASSERT(resourceLeadingToCycle == m_resources->markerStart() || resourceLeadingToCycle == m_resources->markerMid() || resourceLeadingToCycle == m_resources->markerEnd());
+ if (m_resources->markerStart() == resourceLeadingToCycle)
+ m_resources->resetMarkerStart();
+ if (m_resources->markerMid() == resourceLeadingToCycle)
+ m_resources->resetMarkerMid();
+ if (m_resources->markerEnd() == resourceLeadingToCycle)
+ m_resources->resetMarkerEnd();
+ break;
+ case PatternResourceType:
+ case LinearGradientResourceType:
+ case RadialGradientResourceType:
+ ASSERT(resourceLeadingToCycle == m_resources->fill() || resourceLeadingToCycle == m_resources->stroke());
+ if (m_resources->fill() == resourceLeadingToCycle)
+ m_resources->resetFill();
+ if (m_resources->stroke() == resourceLeadingToCycle)
+ m_resources->resetStroke();
+ break;
+ case FilterResourceType:
+#if ENABLE(FILTERS)
+ ASSERT(resourceLeadingToCycle == m_resources->filter());
+ m_resources->resetFilter();
+#endif
+ break;
+ case ClipperResourceType:
+ ASSERT(resourceLeadingToCycle == m_resources->clipper());
+ m_resources->resetClipper();
+ break;
+ case SolidColorResourceType:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/SVGResourcesCycleSolver.h b/Source/WebCore/rendering/SVGResourcesCycleSolver.h
new file mode 100644
index 0000000..e63ee63
--- /dev/null
+++ b/Source/WebCore/rendering/SVGResourcesCycleSolver.h
@@ -0,0 +1,51 @@
+/*
+ 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
+ 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 SVGResourcesCycleSolver_h
+#define SVGResourcesCycleSolver_h
+
+#if ENABLE(SVG)
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+class RenderObject;
+class RenderSVGResourceContainer;
+class SVGResources;
+
+class SVGResourcesCycleSolver : public Noncopyable {
+public:
+ SVGResourcesCycleSolver(RenderObject*, SVGResources*);
+ ~SVGResourcesCycleSolver();
+
+ void resolveCycles();
+
+private:
+ bool resourceContainsCycles(RenderObject*) const;
+ void breakCycle(RenderSVGResourceContainer*);
+
+ RenderObject* m_renderer;
+ SVGResources* m_resources;
+ HashSet<RenderSVGResourceContainer*> m_allResources;
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/SVGShadowTreeElements.cpp b/Source/WebCore/rendering/SVGShadowTreeElements.cpp
new file mode 100644
index 0000000..7c5e1a9
--- /dev/null
+++ b/Source/WebCore/rendering/SVGShadowTreeElements.cpp
@@ -0,0 +1,90 @@
+/*
+ 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
+ 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"
+
+#if ENABLE(SVG)
+#include "SVGShadowTreeElements.h"
+
+#include "Document.h"
+#include "FloatSize.h"
+#include "RenderObject.h"
+#include "SVGNames.h"
+#include "SVGUseElement.h"
+
+namespace WebCore {
+
+// SVGShadowTreeContainerElement
+
+SVGShadowTreeContainerElement::SVGShadowTreeContainerElement(Document* document)
+ : SVGGElement(SVGNames::gTag, document)
+{
+}
+
+PassRefPtr<SVGShadowTreeContainerElement> SVGShadowTreeContainerElement::create(Document* document)
+{
+ return adoptRef(new SVGShadowTreeContainerElement(document));
+}
+
+FloatSize SVGShadowTreeContainerElement::containerTranslation() const
+{
+ return FloatSize(m_xOffset.value(this), m_yOffset.value(this));
+}
+
+// SVGShadowTreeRootElement
+
+inline SVGShadowTreeRootElement::SVGShadowTreeRootElement(Document* document, SVGUseElement* shadowParent)
+ : SVGShadowTreeContainerElement(document)
+{
+ setShadowHost(shadowParent);
+ setInDocument();
+}
+
+PassRefPtr<SVGShadowTreeRootElement> SVGShadowTreeRootElement::create(Document* document, SVGUseElement* shadowParent)
+{
+ return adoptRef(new SVGShadowTreeRootElement(document, shadowParent));
+}
+
+void SVGShadowTreeRootElement::attachElement(PassRefPtr<RenderStyle> style, RenderArena* arena)
+{
+ ASSERT(shadowHost());
+
+ // Create the renderer with the specified style
+ RenderObject* renderer = createRenderer(arena, style.get());
+ if (renderer) {
+ setRenderer(renderer);
+ renderer->setStyle(style);
+ }
+
+ // Set these explicitly since this normally happens during an attach()
+ setAttached();
+
+ // Add the renderer to the render tree
+ if (renderer)
+ shadowHost()->renderer()->addChild(renderer);
+}
+
+void SVGShadowTreeRootElement::clearShadowHost()
+{
+ setShadowHost(0);
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/SVGShadowTreeElements.h b/Source/WebCore/rendering/SVGShadowTreeElements.h
new file mode 100644
index 0000000..fe25678
--- /dev/null
+++ b/Source/WebCore/rendering/SVGShadowTreeElements.h
@@ -0,0 +1,67 @@
+/*
+ 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
+ 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 SVGShadowTreeElements_h
+#define SVGShadowTreeElements_h
+
+#if ENABLE(SVG)
+#include "SVGGElement.h"
+#include "SVGLength.h"
+
+namespace WebCore {
+
+class FloatSize;
+class SVGUseElement;
+
+class SVGShadowTreeContainerElement : public SVGGElement {
+public:
+ static PassRefPtr<SVGShadowTreeContainerElement> create(Document*);
+
+ FloatSize containerTranslation() const;
+ void setContainerOffset(const SVGLength& x, const SVGLength& y)
+ {
+ m_xOffset = x;
+ m_yOffset = y;
+ }
+
+protected:
+ SVGShadowTreeContainerElement(Document*);
+
+private:
+ virtual bool isShadowTreeContainerElement() const { return true; }
+
+ SVGLength m_xOffset;
+ SVGLength m_yOffset;
+};
+
+class SVGShadowTreeRootElement : public SVGShadowTreeContainerElement {
+public:
+ static PassRefPtr<SVGShadowTreeRootElement> create(Document*, SVGUseElement* shadowParent);
+
+ void attachElement(PassRefPtr<RenderStyle>, RenderArena*);
+ void clearShadowHost();
+
+private:
+ SVGShadowTreeRootElement(Document*, SVGUseElement* shadowParent);
+};
+
+}
+
+#endif
+#endif
diff --git a/Source/WebCore/rendering/ScrollBehavior.cpp b/Source/WebCore/rendering/ScrollBehavior.cpp
new file mode 100644
index 0000000..232ea19
--- /dev/null
+++ b/Source/WebCore/rendering/ScrollBehavior.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 "ScrollBehavior.h"
+
+namespace WebCore {
+
+const ScrollAlignment ScrollAlignment::alignCenterIfNeeded = { noScroll, alignCenter, alignToClosestEdge };
+const ScrollAlignment ScrollAlignment::alignToEdgeIfNeeded = { noScroll, alignToClosestEdge, alignToClosestEdge };
+const ScrollAlignment ScrollAlignment::alignCenterAlways = { alignCenter, alignCenter, alignCenter };
+const ScrollAlignment ScrollAlignment::alignTopAlways = { alignTop, alignTop, alignTop };
+const ScrollAlignment ScrollAlignment::alignBottomAlways = { alignBottom, alignBottom, alignBottom };
+
+}; // namespace WebCore
diff --git a/Source/WebCore/rendering/ScrollBehavior.h b/Source/WebCore/rendering/ScrollBehavior.h
new file mode 100644
index 0000000..390c68a
--- /dev/null
+++ b/Source/WebCore/rendering/ScrollBehavior.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2003, 2009 Apple Inc. All rights reserved.
+ *
+ * Portions are Copyright (C) 1998 Netscape Communications Corporation.
+ *
+ * Other contributors:
+ * Robert O'Callahan <roc+@cs.cmu.edu>
+ * David Baron <dbaron@fas.harvard.edu>
+ * Christian Biesinger <cbiesinger@web.de>
+ * Randall Jesup <rjesup@wgate.com>
+ * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
+ * Josh Soref <timeless@mac.com>
+ * Boris Zbarsky <bzbarsky@mit.edu>
+ *
+ * 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 ScrollBehavior_h
+#define ScrollBehavior_h
+
+namespace WebCore {
+
+enum ScrollBehavior {
+ noScroll,
+ alignCenter,
+ alignTop,
+ alignBottom,
+ alignLeft,
+ alignRight,
+ alignToClosestEdge
+};
+
+struct ScrollAlignment {
+ static ScrollBehavior getVisibleBehavior(const ScrollAlignment& s) { return s.m_rectVisible; }
+ static ScrollBehavior getPartialBehavior(const ScrollAlignment& s) { return s.m_rectPartial; }
+ static ScrollBehavior getHiddenBehavior(const ScrollAlignment& s) { return s.m_rectHidden; }
+
+ static const ScrollAlignment alignCenterIfNeeded;
+ static const ScrollAlignment alignToEdgeIfNeeded;
+ static const ScrollAlignment alignCenterAlways;
+ static const ScrollAlignment alignTopAlways;
+ static const ScrollAlignment alignBottomAlways;
+
+ ScrollBehavior m_rectVisible;
+ ScrollBehavior m_rectHidden;
+ ScrollBehavior m_rectPartial;
+};
+
+
+}; // namespace WebCore
+
+#endif // ScrollBehavior_h
diff --git a/Source/WebCore/rendering/ShadowElement.cpp b/Source/WebCore/rendering/ShadowElement.cpp
new file mode 100644
index 0000000..e1b247c
--- /dev/null
+++ b/Source/WebCore/rendering/ShadowElement.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 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 "ShadowElement.h"
+
+#include "HTMLNames.h"
+#include "RenderTheme.h"
+#include "RenderView.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+PassRefPtr<ShadowBlockElement> ShadowBlockElement::create(HTMLElement* shadowParent)
+{
+ return adoptRef(new ShadowBlockElement(shadowParent));
+}
+
+ShadowBlockElement::ShadowBlockElement(HTMLElement* shadowParent)
+ : ShadowElement<HTMLDivElement>(divTag, shadowParent)
+{
+}
+
+void ShadowBlockElement::layoutAsPart(const IntRect& partRect)
+{
+ RenderBox* parentRenderer = toRenderBox(renderer()->parent());
+ RenderBox* selfRenderer = toRenderBox(renderer());
+ IntRect oldRect = selfRenderer->frameRect();
+
+ LayoutStateMaintainer statePusher(parentRenderer->view(), parentRenderer, parentRenderer->size(), parentRenderer->style()->isFlippedBlocksWritingMode());
+
+ if (oldRect.size() != partRect.size())
+ selfRenderer->setChildNeedsLayout(true, false);
+
+ selfRenderer->layoutIfNeeded();
+ selfRenderer->setFrameRect(partRect);
+
+ if (selfRenderer->checkForRepaintDuringLayout())
+ selfRenderer->repaintDuringLayoutIfMoved(oldRect);
+
+ statePusher.pop();
+ parentRenderer->addOverflowFromChild(selfRenderer);
+}
+
+void ShadowBlockElement::updateStyleForPart(PseudoId pseudoId)
+{
+ if (renderer()->style()->styleType() != pseudoId)
+ renderer()->setStyle(createStyleForPart(renderer()->parent(), pseudoId));
+}
+
+PassRefPtr<ShadowBlockElement> ShadowBlockElement::createForPart(HTMLElement* shadowParent, PseudoId pseudoId)
+{
+ RefPtr<ShadowBlockElement> part = create(shadowParent);
+ part->initAsPart(pseudoId);
+ return part.release();
+}
+
+void ShadowBlockElement::initAsPart(PseudoId pseudoId)
+{
+ RenderObject* parentRenderer = shadowHost()->renderer();
+ RefPtr<RenderStyle> styleForPart = createStyleForPart(parentRenderer, pseudoId);
+ setRenderer(createRenderer(parentRenderer->renderArena(), styleForPart.get()));
+ renderer()->setStyle(styleForPart.release());
+ setAttached();
+ setInDocument();
+}
+
+PassRefPtr<RenderStyle> ShadowBlockElement::createStyleForPart(RenderObject* parentRenderer, PseudoId pseudoId)
+{
+ RefPtr<RenderStyle> styleForPart;
+ RenderStyle* pseudoStyle = parentRenderer->getCachedPseudoStyle(pseudoId);
+ if (pseudoStyle)
+ styleForPart = RenderStyle::clone(pseudoStyle);
+ else
+ styleForPart = RenderStyle::create();
+
+ styleForPart->inheritFrom(parentRenderer->style());
+ styleForPart->setDisplay(BLOCK);
+ styleForPart->setAppearance(NoControlPart);
+ return styleForPart.release();
+}
+
+bool ShadowBlockElement::partShouldHaveStyle(const RenderObject* parentRenderer, PseudoId pseudoId)
+{
+ // We have some -webkit-appearance values for default styles of parts and
+ // that appearance get turned off during RenderStyle creation
+ // if they have background styles specified.
+ // So !hasAppearance() implies that there are something to be styled.
+ RenderStyle* pseudoStyle = parentRenderer->getCachedPseudoStyle(pseudoId);
+ return !(pseudoStyle && pseudoStyle->hasAppearance());
+}
+
+PassRefPtr<ShadowInputElement> ShadowInputElement::create(HTMLElement* shadowParent)
+{
+ return adoptRef(new ShadowInputElement(shadowParent));
+}
+
+ShadowInputElement::ShadowInputElement(HTMLElement* shadowParent)
+ : ShadowElement<HTMLInputElement>(inputTag, shadowParent)
+{
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/ShadowElement.h b/Source/WebCore/rendering/ShadowElement.h
new file mode 100644
index 0000000..2c1a69e
--- /dev/null
+++ b/Source/WebCore/rendering/ShadowElement.h
@@ -0,0 +1,86 @@
+/*
+ * 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 ShadowElement_h
+#define ShadowElement_h
+
+#include "HTMLDivElement.h"
+#include "HTMLInputElement.h"
+
+namespace WebCore {
+
+template<class BaseElement>
+class ShadowElement : public BaseElement {
+protected:
+ ShadowElement(const QualifiedName& name, HTMLElement* shadowParent)
+ : BaseElement(name, shadowParent->document())
+ , m_shadowParent(shadowParent)
+ {
+ BaseElement::setShadowHost(shadowParent);
+ }
+
+public:
+ virtual void detach();
+
+private:
+ RefPtr<HTMLElement> m_shadowParent;
+};
+
+template<class BaseElement>
+void ShadowElement<BaseElement>::detach()
+{
+ BaseElement::detach();
+ // FIXME: Remove once shadow DOM uses Element::setShadowRoot().
+ BaseElement::setShadowHost(0);
+}
+
+class ShadowBlockElement : public ShadowElement<HTMLDivElement> {
+public:
+ static PassRefPtr<ShadowBlockElement> create(HTMLElement*);
+ static PassRefPtr<ShadowBlockElement> createForPart(HTMLElement*, PseudoId);
+ static bool partShouldHaveStyle(const RenderObject* parentRenderer, PseudoId pseudoId);
+ void layoutAsPart(const IntRect& partRect);
+ virtual void updateStyleForPart(PseudoId);
+
+protected:
+ ShadowBlockElement(HTMLElement*);
+ void initAsPart(PseudoId pasuedId);
+private:
+ static PassRefPtr<RenderStyle> createStyleForPart(RenderObject*, PseudoId);
+};
+
+class ShadowInputElement : public ShadowElement<HTMLInputElement> {
+public:
+ static PassRefPtr<ShadowInputElement> create(HTMLElement*);
+protected:
+ ShadowInputElement(HTMLElement*);
+};
+
+} // namespace WebCore
+
+#endif // ShadowElement_h
diff --git a/Source/WebCore/rendering/TableLayout.h b/Source/WebCore/rendering/TableLayout.h
new file mode 100644
index 0000000..e0fa8ee
--- /dev/null
+++ b/Source/WebCore/rendering/TableLayout.h
@@ -0,0 +1,48 @@
+/*
+ * 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 TableLayout_h
+#define TableLayout_h
+
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class RenderTable;
+
+class TableLayout : public Noncopyable {
+public:
+ TableLayout(RenderTable* table)
+ : m_table(table)
+ {
+ }
+
+ virtual ~TableLayout() { }
+
+ virtual void computePreferredLogicalWidths(int& minWidth, int& maxWidth) = 0;
+ virtual void layout() = 0;
+
+protected:
+ RenderTable* m_table;
+};
+
+} // namespace WebCore
+
+#endif // TableLayout_h
diff --git a/Source/WebCore/rendering/TextControlInnerElements.cpp b/Source/WebCore/rendering/TextControlInnerElements.cpp
new file mode 100644
index 0000000..d6fc7aa
--- /dev/null
+++ b/Source/WebCore/rendering/TextControlInnerElements.cpp
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * 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:
+ * 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 "TextControlInnerElements.h"
+
+#include "BeforeTextInsertedEvent.h"
+#include "Document.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLTextAreaElement.h"
+#include "MouseEvent.h"
+#include "Page.h"
+#include "RenderLayer.h"
+#include "RenderTextControlSingleLine.h"
+#include "ScrollbarTheme.h"
+#include "SpeechInput.h"
+#include "SpeechInputEvent.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+class RenderTextControlInnerBlock : public RenderBlock {
+public:
+ RenderTextControlInnerBlock(Node* node, bool isMultiLine) : RenderBlock(node), m_multiLine(isMultiLine) { }
+
+private:
+ virtual bool hasLineIfEmpty() const { return true; }
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+
+ bool m_multiLine;
+};
+
+VisiblePosition RenderTextControlInnerBlock::positionForPoint(const IntPoint& point)
+{
+ IntPoint contentsPoint(point);
+
+ // Multiline text controls have the scroll on shadowAncestorNode, so we need to take that
+ // into account here.
+ if (m_multiLine) {
+ RenderTextControl* renderer = toRenderTextControl(node()->shadowAncestorNode()->renderer());
+ if (renderer->hasOverflowClip())
+ contentsPoint += renderer->layer()->scrolledContentOffset();
+ }
+
+ return RenderBlock::positionForPoint(contentsPoint);
+}
+
+// ----------------------------
+
+TextControlInnerElement::TextControlInnerElement(Document* document, HTMLElement* shadowParent)
+ : HTMLDivElement(divTag, document)
+ , m_shadowParent(shadowParent)
+{
+ setShadowHost(shadowParent);
+}
+
+PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(HTMLElement* shadowParent)
+{
+ return adoptRef(new TextControlInnerElement(shadowParent->document(), shadowParent));
+}
+
+void TextControlInnerElement::attachInnerElement(Node* parent, PassRefPtr<RenderStyle> style, RenderArena* arena)
+{
+ // When adding these elements, create the renderer & style first before adding to the DOM.
+ // Otherwise, the render tree will create some anonymous blocks that will mess up our layout.
+
+ // Create the renderer with the specified style
+ RenderObject* renderer = createRenderer(arena, style.get());
+ if (renderer) {
+ setRenderer(renderer);
+ renderer->setStyle(style);
+ }
+
+ // Set these explicitly since this normally happens during an attach()
+ setAttached();
+ setInDocument();
+
+ // For elements not yet in shadow DOM, add the node to the DOM normally.
+ if (!isShadowRoot()) {
+ // 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->deprecatedParserAddChild(this);
+ }
+
+ // Add the renderer to the render tree
+ if (renderer)
+ parent->renderer()->addChild(renderer);
+}
+
+void TextControlInnerElement::detach()
+{
+ HTMLDivElement::detach();
+ // FIXME: Remove once shadow DOM uses Element::setShadowRoot().
+ setShadowHost(0);
+}
+
+// ----------------------------
+
+inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document, HTMLElement* shadowParent)
+ : TextControlInnerElement(document, shadowParent)
+{
+}
+
+PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document, HTMLElement* shadowParent)
+{
+ return adoptRef(new TextControlInnerTextElement(document, shadowParent));
+}
+
+void TextControlInnerTextElement::defaultEventHandler(Event* event)
+{
+ // FIXME: In the future, we should add a way to have default event listeners.
+ // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
+ // Or possibly we could just use a normal event listener.
+ if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
+ Node* shadowAncestor = shadowAncestorNode();
+ // A TextControlInnerTextElement can be its own shadow ancestor if its been detached, but kept alive by an EditCommand.
+ // In this case, an undo/redo can cause events to be sent to the TextControlInnerTextElement.
+ // To prevent an infinite loop, we must check for this case before sending the event up the chain.
+ if (shadowAncestor && shadowAncestor != this)
+ shadowAncestor->defaultEventHandler(event);
+ }
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ bool multiLine = false;
+ Node* shadowAncestor = shadowAncestorNode();
+ if (shadowAncestor && shadowAncestor->renderer()) {
+ ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea());
+ multiLine = shadowAncestor->renderer()->isTextArea();
+ }
+ return new (arena) RenderTextControlInnerBlock(this, multiLine);
+}
+
+// ----------------------------
+
+inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
+ : TextControlInnerElement(document)
+{
+}
+
+PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
+{
+ return adoptRef(new SearchFieldResultsButtonElement(document));
+}
+
+void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
+{
+ // On mousedown, bring up a menu, if needed
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ input->focus();
+ input->select();
+ RenderTextControlSingleLine* renderer = toRenderTextControlSingleLine(input->renderer());
+ if (renderer->popupIsVisible())
+ renderer->hidePopup();
+ else if (input->maxResults() > 0)
+ renderer->showPopup();
+ event->setDefaultHandled();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+// ----------------------------
+
+inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
+ : TextControlInnerElement(document)
+ , m_capturing(false)
+{
+}
+
+PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
+{
+ return adoptRef(new SearchFieldCancelButtonElement(document));
+}
+
+void SearchFieldCancelButtonElement::detach()
+{
+ if (m_capturing) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+ TextControlInnerElement::detach();
+}
+
+
+void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
+{
+ // If the element is visible, on mouseup, clear the value, and set selection
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ input->focus();
+ input->select();
+ event->setDefaultHandled();
+ }
+ if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ if (hovered()) {
+ RefPtr<HTMLInputElement> protector(input);
+ String oldValue = input->value();
+ input->setValue("");
+ if (!oldValue.isEmpty()) {
+ toRenderTextControl(input->renderer())->setChangedSinceLastChangeEvent(true);
+ input->dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+ }
+ input->onSearch();
+ event->setDefaultHandled();
+ }
+ }
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+// ----------------------------
+
+inline SpinButtonElement::SpinButtonElement(HTMLElement* shadowParent)
+ : TextControlInnerElement(shadowParent->document(), shadowParent)
+ , m_capturing(false)
+ , m_upDownState(Indeterminate)
+ , m_pressStartingState(Indeterminate)
+ , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired)
+{
+}
+
+PassRefPtr<SpinButtonElement> SpinButtonElement::create(HTMLElement* shadowParent)
+{
+ return adoptRef(new SpinButtonElement(shadowParent));
+}
+
+void SpinButtonElement::defaultEventHandler(Event* event)
+{
+ if (!event->isMouseEvent()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ RenderBox* box = renderBox();
+ if (!box) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ if (input->disabled() || input->isReadOnlyFormControl()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
+ if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
+ if (box->borderBoxRect().contains(local)) {
+ RefPtr<Node> protector(input);
+ input->focus();
+ input->select();
+ input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1);
+ event->setDefaultHandled();
+ startRepeatingTimer();
+ }
+ } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton)
+ stopRepeatingTimer();
+ else if (event->type() == eventNames().mousemoveEvent) {
+ if (box->borderBoxRect().contains(local)) {
+ if (!m_capturing) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ UpDownState oldUpDownState = m_upDownState;
+ m_upDownState = local.y() < box->height() / 2 ? Up : Down;
+ if (m_upDownState != oldUpDownState)
+ renderer()->repaint();
+ } else {
+ if (m_capturing) {
+ stopRepeatingTimer();
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ }
+ }
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+void SpinButtonElement::startRepeatingTimer()
+{
+ m_pressStartingState = m_upDownState;
+ ScrollbarTheme* theme = ScrollbarTheme::nativeTheme();
+ m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay());
+}
+
+void SpinButtonElement::stopRepeatingTimer()
+{
+ m_repeatingTimer.stop();
+}
+
+void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*)
+{
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ if (input->disabled() || input->isReadOnlyFormControl())
+ return;
+ // On Mac OS, NSStepper updates the value for the button under the mouse
+ // cursor regardless of the button pressed at the beginning. So the
+ // following check is not needed for Mac OS.
+#if !OS(MAC_OS_X)
+ if (m_upDownState != m_pressStartingState)
+ return;
+#endif
+ input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1);
+}
+
+void SpinButtonElement::setHovered(bool flag)
+{
+ if (!hovered() && flag)
+ m_upDownState = Indeterminate;
+ TextControlInnerElement::setHovered(flag);
+}
+
+
+// ----------------------------
+
+#if ENABLE(INPUT_SPEECH)
+
+inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(HTMLElement* shadowParent)
+ : TextControlInnerElement(shadowParent->document(), shadowParent)
+ , m_capturing(false)
+ , m_state(Idle)
+ , m_listenerId(document()->page()->speechInput()->registerListener(this))
+{
+}
+
+InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
+{
+ SpeechInput* speech = speechInput();
+ if (speech) { // Could be null when page is unloading.
+ if (m_state != Idle)
+ speech->cancelRecognition(m_listenerId);
+ speech->unregisterListener(m_listenerId);
+ }
+}
+
+PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(HTMLElement* shadowParent)
+{
+ return adoptRef(new InputFieldSpeechButtonElement(shadowParent));
+}
+
+void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
+{
+ // For privacy reasons, only allow clicks directly coming from the user.
+ if (!event->fromUserGesture()) {
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ // On mouse down, select the text and set focus.
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ // The call to focus() below dispatches a focus event, and an event handler in the page might
+ // remove the input element from DOM. To make sure it remains valid until we finish our work
+ // here, we take a temporary reference.
+ RefPtr<HTMLInputElement> holdRef(input);
+ input->focus();
+ input->select();
+ event->setDefaultHandled();
+ }
+ // On mouse up, release capture cleanly.
+ if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ }
+ }
+
+ if (event->type() == eventNames().clickEvent) {
+ switch (m_state) {
+ case Idle:
+ if (speechInput()->startRecognition(m_listenerId, input->renderer()->absoluteBoundingBoxRect(), input->computeInheritedLanguage(), input->getAttribute(webkitgrammarAttr)))
+ setState(Recording);
+ break;
+ case Recording:
+ speechInput()->stopRecording(m_listenerId);
+ break;
+ case Recognizing:
+ // Nothing to do here, we will continue to wait for results.
+ break;
+ }
+ event->setDefaultHandled();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+void InputFieldSpeechButtonElement::setState(SpeechInputState state)
+{
+ if (m_state != state) {
+ m_state = state;
+ shadowAncestorNode()->renderer()->repaint();
+ }
+}
+
+SpeechInput* InputFieldSpeechButtonElement::speechInput()
+{
+ return document()->page() ? document()->page()->speechInput() : 0;
+}
+
+void InputFieldSpeechButtonElement::didCompleteRecording(int)
+{
+ setState(Recognizing);
+}
+
+void InputFieldSpeechButtonElement::didCompleteRecognition(int)
+{
+ setState(Idle);
+}
+
+void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
+{
+ m_results = results;
+
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
+ // The call to setValue() below dispatches an event, and an event handler in the page might
+ // remove the input element from DOM. To make sure it remains valid until we finish our work
+ // here, we take a temporary reference.
+ RefPtr<HTMLInputElement> holdRef(input);
+ input->setValue(results.isEmpty() ? "" : results[0]->utterance());
+ input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
+ renderer()->repaint();
+}
+
+void InputFieldSpeechButtonElement::detach()
+{
+ if (m_capturing) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+
+ if (m_state != Idle)
+ speechInput()->cancelRecognition(m_listenerId);
+
+ TextControlInnerElement::detach();
+}
+
+#endif // ENABLE(INPUT_SPEECH)
+
+}
diff --git a/Source/WebCore/rendering/TextControlInnerElements.h b/Source/WebCore/rendering/TextControlInnerElements.h
new file mode 100644
index 0000000..bb77dcd
--- /dev/null
+++ b/Source/WebCore/rendering/TextControlInnerElements.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * 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:
+ * 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 TextControlInnerElements_h
+#define TextControlInnerElements_h
+
+#include "HTMLDivElement.h"
+#include "SpeechInputListener.h"
+#include "Timer.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class SpeechInput;
+
+class TextControlInnerElement : public HTMLDivElement {
+public:
+ static PassRefPtr<TextControlInnerElement> create(HTMLElement* shadowParent);
+ virtual void detach();
+
+ void attachInnerElement(Node*, PassRefPtr<RenderStyle>, RenderArena*);
+
+protected:
+ TextControlInnerElement(Document*, HTMLElement* shadowParent = 0);
+
+private:
+ virtual bool isMouseFocusable() const { return false; }
+
+ RefPtr<HTMLElement> m_shadowParent;
+};
+
+class TextControlInnerTextElement : public TextControlInnerElement {
+public:
+ static PassRefPtr<TextControlInnerTextElement> create(Document*, HTMLElement* shadowParent);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ TextControlInnerTextElement(Document*, HTMLElement* shadowParent);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+};
+
+class SearchFieldResultsButtonElement : public TextControlInnerElement {
+public:
+ static PassRefPtr<SearchFieldResultsButtonElement> create(Document*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ SearchFieldResultsButtonElement(Document*);
+};
+
+class SearchFieldCancelButtonElement : public TextControlInnerElement {
+public:
+ static PassRefPtr<SearchFieldCancelButtonElement> create(Document*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ SearchFieldCancelButtonElement(Document*);
+
+ virtual void detach();
+
+ bool m_capturing;
+};
+
+class SpinButtonElement : public TextControlInnerElement {
+public:
+ enum UpDownState {
+ Indeterminate, // Hovered, but the event is not handled.
+ Down,
+ Up,
+ };
+
+ static PassRefPtr<SpinButtonElement> create(HTMLElement*);
+ UpDownState upDownState() const { return m_upDownState; }
+
+private:
+ SpinButtonElement(HTMLElement*);
+
+ virtual bool isSpinButtonElement() const { return true; }
+ // FIXME: shadowAncestorNode() should be const.
+ virtual bool isEnabledFormControl() const { return static_cast<Element*>(const_cast<SpinButtonElement*>(this)->shadowAncestorNode())->isEnabledFormControl(); }
+ virtual bool isReadOnlyFormControl() const { return static_cast<Element*>(const_cast<SpinButtonElement*>(this)->shadowAncestorNode())->isReadOnlyFormControl(); }
+ virtual void defaultEventHandler(Event*);
+ void startRepeatingTimer();
+ void stopRepeatingTimer();
+ void repeatingTimerFired(Timer<SpinButtonElement>*);
+ virtual void setHovered(bool = true);
+
+ bool m_capturing;
+ UpDownState m_upDownState;
+ UpDownState m_pressStartingState;
+ Timer<SpinButtonElement> m_repeatingTimer;
+};
+
+#if ENABLE(INPUT_SPEECH)
+
+class InputFieldSpeechButtonElement
+ : public TextControlInnerElement,
+ public SpeechInputListener {
+public:
+ enum SpeechInputState {
+ Idle,
+ Recording,
+ Recognizing,
+ };
+
+ static PassRefPtr<InputFieldSpeechButtonElement> create(HTMLElement*);
+ virtual ~InputFieldSpeechButtonElement();
+
+ virtual void detach();
+ virtual void defaultEventHandler(Event*);
+ SpeechInputState state() const { return m_state; }
+
+ // SpeechInputListener methods.
+ void didCompleteRecording(int);
+ void didCompleteRecognition(int);
+ void setRecognitionResult(int, const SpeechInputResultArray&);
+
+private:
+ InputFieldSpeechButtonElement(HTMLElement*);
+ SpeechInput* speechInput();
+ void setState(SpeechInputState state);
+
+ bool m_capturing;
+ SpeechInputState m_state;
+ int m_listenerId;
+ SpeechInputResultArray m_results;
+};
+
+#endif // ENABLE(INPUT_SPEECH)
+
+} // namespace
+
+#endif
diff --git a/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h b/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h
new file mode 100644
index 0000000..6629857
--- /dev/null
+++ b/Source/WebCore/rendering/TrailingFloatsRootInlineBox.h
@@ -0,0 +1,47 @@
+/*
+ * 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 TrailingFloatsRootInlineBox_h
+#define TrailingFloatsRootInlineBox_h
+
+#include "RootInlineBox.h"
+
+namespace WebCore {
+
+class TrailingFloatsRootInlineBox : public RootInlineBox {
+public:
+ TrailingFloatsRootInlineBox(RenderBlock* block)
+ : RootInlineBox(block)
+ {
+ setHasVirtualLogicalHeight();
+ }
+
+private:
+ virtual int virtualLogicalHeight() const { return 0; }
+};
+
+} // namespace WebCore
+
+#endif // TrailingFloatsRootInlineBox_h
diff --git a/Source/WebCore/rendering/TransformState.cpp b/Source/WebCore/rendering/TransformState.cpp
new file mode 100644
index 0000000..ecc614e
--- /dev/null
+++ b/Source/WebCore/rendering/TransformState.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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 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 "TransformState.h"
+
+namespace WebCore {
+
+void TransformState::move(int x, int y, TransformAccumulation accumulate)
+{
+ if (m_accumulatingTransform && m_accumulatedTransform) {
+ // If we're accumulating into an existing transform, apply the translation.
+ if (m_direction == ApplyTransformDirection)
+ m_accumulatedTransform->translateRight(x, y);
+ else
+ m_accumulatedTransform->translate(-x, -y); // We're unapplying, so negate
+
+ // Then flatten if necessary.
+ if (accumulate == FlattenTransform)
+ flatten();
+ } else {
+ // Just move the point and, optionally, the quad.
+ m_lastPlanarPoint.move(x, y);
+ if (m_mapQuad)
+ m_lastPlanarQuad.move(x, y);
+ }
+ m_accumulatingTransform = accumulate == AccumulateTransform;
+}
+
+// FIXME: We transform AffineTransform to TransformationMatrix. This is rather inefficient.
+void TransformState::applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation accumulate)
+{
+ applyTransform(transformFromContainer.toTransformationMatrix(), accumulate);
+}
+
+void TransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate)
+{
+ // If we have an accumulated transform from last time, multiply in this transform
+ if (m_accumulatedTransform) {
+ if (m_direction == ApplyTransformDirection)
+ m_accumulatedTransform->multiply(transformFromContainer);
+ else
+ m_accumulatedTransform->multLeft(transformFromContainer);
+ } else if (accumulate == AccumulateTransform) {
+ // Make one if we started to accumulate
+ m_accumulatedTransform.set(new TransformationMatrix(transformFromContainer));
+ }
+
+ if (accumulate == FlattenTransform) {
+ const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer;
+ flattenWithTransform(*finalTransform);
+ }
+ m_accumulatingTransform = accumulate == AccumulateTransform;
+}
+
+void TransformState::flatten()
+{
+ if (!m_accumulatedTransform) {
+ m_accumulatingTransform = false;
+ return;
+ }
+
+ flattenWithTransform(*m_accumulatedTransform);
+}
+
+FloatPoint TransformState::mappedPoint() const
+{
+ if (!m_accumulatedTransform)
+ return m_lastPlanarPoint;
+
+ if (m_direction == ApplyTransformDirection)
+ return m_accumulatedTransform->mapPoint(m_lastPlanarPoint);
+
+ return m_accumulatedTransform->inverse().projectPoint(m_lastPlanarPoint);
+}
+
+FloatQuad TransformState::mappedQuad() const
+{
+ if (!m_accumulatedTransform)
+ return m_lastPlanarQuad;
+
+ if (m_direction == ApplyTransformDirection)
+ return m_accumulatedTransform->mapQuad(m_lastPlanarQuad);
+
+ return m_accumulatedTransform->inverse().projectQuad(m_lastPlanarQuad);
+}
+
+void TransformState::flattenWithTransform(const TransformationMatrix& t)
+{
+ if (m_direction == ApplyTransformDirection) {
+ m_lastPlanarPoint = t.mapPoint(m_lastPlanarPoint);
+ if (m_mapQuad)
+ m_lastPlanarQuad = t.mapQuad(m_lastPlanarQuad);
+ } else {
+ TransformationMatrix inverseTransform = t.inverse();
+ m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
+ if (m_mapQuad)
+ m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad);
+ }
+
+ // We could throw away m_accumulatedTransform if we wanted to here, but that
+ // would cause thrash when traversing hierarchies with alternating
+ // preserve-3d and flat elements.
+ if (m_accumulatedTransform)
+ m_accumulatedTransform->makeIdentity();
+ m_accumulatingTransform = false;
+}
+
+// HitTestingTransformState methods
+void HitTestingTransformState::translate(int x, int y, TransformAccumulation accumulate)
+{
+ m_accumulatedTransform.translate(x, y);
+ if (accumulate == FlattenTransform)
+ flattenWithTransform(m_accumulatedTransform);
+
+ m_accumulatingTransform = accumulate == AccumulateTransform;
+}
+
+void HitTestingTransformState::applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation accumulate)
+{
+ m_accumulatedTransform.multLeft(transformFromContainer);
+ if (accumulate == FlattenTransform)
+ flattenWithTransform(m_accumulatedTransform);
+
+ m_accumulatingTransform = accumulate == AccumulateTransform;
+}
+
+void HitTestingTransformState::flatten()
+{
+ flattenWithTransform(m_accumulatedTransform);
+}
+
+void HitTestingTransformState::flattenWithTransform(const TransformationMatrix& t)
+{
+ TransformationMatrix inverseTransform = t.inverse();
+ m_lastPlanarPoint = inverseTransform.projectPoint(m_lastPlanarPoint);
+ m_lastPlanarQuad = inverseTransform.projectQuad(m_lastPlanarQuad);
+
+ m_accumulatedTransform.makeIdentity();
+ m_accumulatingTransform = false;
+}
+
+FloatPoint HitTestingTransformState::mappedPoint() const
+{
+ return m_accumulatedTransform.inverse().projectPoint(m_lastPlanarPoint);
+}
+
+FloatQuad HitTestingTransformState::mappedQuad() const
+{
+ return m_accumulatedTransform.inverse().projectQuad(m_lastPlanarQuad);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/TransformState.h b/Source/WebCore/rendering/TransformState.h
new file mode 100644
index 0000000..0b4ca46
--- /dev/null
+++ b/Source/WebCore/rendering/TransformState.h
@@ -0,0 +1,135 @@
+/*
+ * 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 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 TransformState_h
+#define TransformState_h
+
+#include "AffineTransform.h"
+#include "FloatPoint.h"
+#include "FloatQuad.h"
+#include "IntSize.h"
+#include "TransformationMatrix.h"
+#include <wtf/Noncopyable.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class TransformState : public Noncopyable {
+public:
+ enum TransformDirection { ApplyTransformDirection, UnapplyInverseTransformDirection };
+ enum TransformAccumulation { FlattenTransform, AccumulateTransform };
+
+ // If quad is non-null, it will be mapped
+ TransformState(TransformDirection mappingDirection, const FloatPoint& p, const FloatQuad* quad = 0)
+ : m_lastPlanarPoint(p)
+ , m_accumulatingTransform(false)
+ , m_mapQuad(quad != 0)
+ , m_direction(mappingDirection)
+ {
+ if (quad)
+ m_lastPlanarQuad = *quad;
+ }
+
+ void move(const IntSize& s, TransformAccumulation accumulate = FlattenTransform)
+ {
+ move(s.width(), s.height(), accumulate);
+ }
+
+ void move(int x, int y, TransformAccumulation = FlattenTransform);
+ void applyTransform(const AffineTransform& transformFromContainer, TransformAccumulation = FlattenTransform);
+ void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation = FlattenTransform);
+ void flatten();
+
+ // Return the coords of the point or quad in the last flattened layer
+ FloatPoint lastPlanarPoint() const { return m_lastPlanarPoint; }
+ FloatQuad lastPlanarQuad() const { return m_lastPlanarQuad; }
+
+ // Return the point or quad mapped through the current transform
+ FloatPoint mappedPoint() const;
+ FloatQuad mappedQuad() const;
+
+private:
+ void flattenWithTransform(const TransformationMatrix&);
+
+ FloatPoint m_lastPlanarPoint;
+ FloatQuad m_lastPlanarQuad;
+
+ // We only allocate the transform if we need to
+ OwnPtr<TransformationMatrix> m_accumulatedTransform;
+ bool m_accumulatingTransform;
+ bool m_mapQuad;
+ TransformDirection m_direction;
+};
+
+class HitTestingTransformState : public RefCounted<HitTestingTransformState> {
+public:
+ static PassRefPtr<HitTestingTransformState> create(const FloatPoint& p, const FloatQuad& quad)
+ {
+ return adoptRef(new HitTestingTransformState(p, quad));
+ }
+
+ static PassRefPtr<HitTestingTransformState> create(const HitTestingTransformState& other)
+ {
+ return adoptRef(new HitTestingTransformState(other));
+ }
+
+ enum TransformAccumulation { FlattenTransform, AccumulateTransform };
+ void translate(int x, int y, TransformAccumulation);
+ void applyTransform(const TransformationMatrix& transformFromContainer, TransformAccumulation);
+
+ FloatPoint mappedPoint() const;
+ FloatQuad mappedQuad() const;
+ void flatten();
+
+ FloatPoint m_lastPlanarPoint;
+ FloatQuad m_lastPlanarQuad;
+ TransformationMatrix m_accumulatedTransform;
+ bool m_accumulatingTransform;
+
+private:
+ HitTestingTransformState(const FloatPoint& p, const FloatQuad& quad)
+ : m_lastPlanarPoint(p)
+ , m_lastPlanarQuad(quad)
+ , m_accumulatingTransform(false)
+ {
+ }
+
+ HitTestingTransformState(const HitTestingTransformState& other)
+ : RefCounted<HitTestingTransformState>()
+ , m_lastPlanarPoint(other.m_lastPlanarPoint)
+ , m_lastPlanarQuad(other.m_lastPlanarQuad)
+ , m_accumulatedTransform(other.m_accumulatedTransform)
+ , m_accumulatingTransform(other.m_accumulatingTransform)
+ {
+ }
+
+ void flattenWithTransform(const TransformationMatrix&);
+};
+
+} // namespace WebCore
+
+#endif // TransformState_h
diff --git a/Source/WebCore/rendering/VerticalPositionCache.h b/Source/WebCore/rendering/VerticalPositionCache.h
new file mode 100644
index 0000000..4deaef5
--- /dev/null
+++ b/Source/WebCore/rendering/VerticalPositionCache.h
@@ -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.
+ */
+
+#ifndef VerticalPositionCache_h
+#define VerticalPositionCache_h
+
+#include "FontBaseline.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+class RenderObject;
+
+// Values for vertical alignment.
+const int PositionTop = -0x7fffffff;
+const int PositionBottom = 0x7fffffff;
+const int PositionUndefined = 0x80000000;
+
+class VerticalPositionCache : public Noncopyable {
+public:
+ VerticalPositionCache()
+ { }
+
+ int get(RenderObject* renderer, FontBaseline baselineType) const
+ {
+ const HashMap<RenderObject*, int>& mapToCheck = baselineType == AlphabeticBaseline ? m_alphabeticPositions : m_ideographicPositions;
+ const HashMap<RenderObject*, int>::const_iterator it = mapToCheck.find(renderer);
+ if (it == mapToCheck.end())
+ return PositionUndefined;
+ return it->second;
+ }
+
+ void set(RenderObject* renderer, FontBaseline baselineType, int position)
+ {
+ if (baselineType == AlphabeticBaseline)
+ m_alphabeticPositions.set(renderer, position);
+ else
+ m_ideographicPositions.set(renderer, position);
+ }
+
+private:
+ HashMap<RenderObject*, int> m_alphabeticPositions;
+ HashMap<RenderObject*, int> m_ideographicPositions;
+};
+
+} // namespace WebCore
+
+#endif // VerticalPositionCache_h
diff --git a/Source/WebCore/rendering/break_lines.cpp b/Source/WebCore/rendering/break_lines.cpp
new file mode 100644
index 0000000..16bfcc2
--- /dev/null
+++ b/Source/WebCore/rendering/break_lines.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2005, 2007, 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"
+#include "break_lines.h"
+
+#include "CharacterNames.h"
+#include "TextBreakIterator.h"
+#include <wtf/StdLibExtras.h>
+
+#if PLATFORM(MAC)
+#include <CoreServices/CoreServices.h>
+#endif
+
+namespace WebCore {
+
+static inline bool isBreakableSpace(UChar ch, bool treatNoBreakSpaceAsBreak)
+{
+ switch (ch) {
+ case ' ':
+ case '\n':
+ case '\t':
+ return true;
+ case noBreakSpace:
+ return treatNoBreakSpaceAsBreak;
+ default:
+ return false;
+ }
+}
+
+static const UChar asciiLineBreakTableFirstChar = '!';
+static const UChar asciiLineBreakTableLastChar = 127;
+
+// Pack 8 bits into one byte
+#define B(a, b, c, d, e, f, g, h) \
+ ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3) | ((e) << 4) | ((f) << 5) | ((g) << 6) | ((h) << 7))
+
+// Line breaking table row for each digit (0-9)
+#define DI { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+// Line breaking table row for ascii letters (a-z A-Z)
+#define AL { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+#define F 0xFF
+
+// Line breaking table for printable ASCII characters. Line breaking opportunities in this table are as below:
+// - before openning punctuations such as '(', '<', '[', '{' after certain characters (compatible with Firefox 3.6);
+// - after '-' and '?' (backward-compatible, and compatible with Internet Explorer).
+// Please refer to <https://bugs.webkit.org/show_bug.cgi?id=37698> for line breaking matrixes of different browsers
+// and the ICU standard.
+static const unsigned char asciiLineBreakTable[][(asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar) / 8 + 1] = {
+ // ! " # $ % & ' ( ) * + , - . / 0 1-8 9 : ; < = > ? @ A-X Y Z [ \ ] ^ _ ` a-x y z { | } ~ DEL
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // !
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // "
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // #
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // $
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // %
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // &
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // '
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // (
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // )
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // *
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // +
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ,
+ { B(1, 1, 1, 1, 1, 1, 1, 1), B(1, 1, 1, 1, 1, 1, 1, 1), F, B(1, 1, 1, 1, 1, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 1, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 1, 1, 1, 1) }, // -
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // .
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // /
+ DI, DI, DI, DI, DI, DI, DI, DI, DI, DI, // 0-9
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // :
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ;
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // <
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // =
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // >
+ { B(0, 0, 1, 1, 1, 1, 0, 1), B(0, 1, 1, 0, 1, 0, 0, 1), F, B(1, 0, 0, 1, 1, 1, 0, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 1), F, F, F, B(1, 1, 1, 1, 0, 1, 1, 0) }, // ?
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // @
+ AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // A-Z
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // [
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // '\'
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ]
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // ^
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // _
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // `
+ AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, AL, // a-z
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // {
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // |
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // }
+ { B(0, 0, 0, 0, 0, 0, 0, 1), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 1, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 1, 0, 0, 0, 0, 0) }, // ~
+ { B(0, 0, 0, 0, 0, 0, 0, 0), B(0, 0, 0, 0, 0, 0, 0, 0), 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0), 0, 0, 0, B(0, 0, 0, 0, 0, 0, 0, 0) }, // DEL
+};
+
+#undef B
+#undef F
+#undef DI
+#undef AL
+
+COMPILE_ASSERT(WTF_ARRAY_LENGTH(asciiLineBreakTable) == asciiLineBreakTableLastChar - asciiLineBreakTableFirstChar + 1, TestLineBreakTableConsistency);
+
+static inline bool shouldBreakAfter(UChar ch, UChar nextCh)
+{
+ switch (ch) {
+ case ideographicComma:
+ case ideographicFullStop:
+ // FIXME: cases for ideographicComma and ideographicFullStop are a workaround for an issue in Unicode 5.0
+ // which is likely to be resolved in Unicode 5.1 <http://bugs.webkit.org/show_bug.cgi?id=17411>.
+ // We may want to remove or conditionalize this workaround at some point.
+#ifdef ANDROID_LAYOUT
+ // as '/' is used in uri which is always long, we would like to break it
+ case '/':
+#endif
+ return true;
+ default:
+ // If both ch and nextCh are ASCII characters, use a lookup table for enhanced speed and for compatibility
+ // with other browsers (see comments for asciiLineBreakTable for details).
+ if (ch >= asciiLineBreakTableFirstChar && ch <= asciiLineBreakTableLastChar
+ && nextCh >= asciiLineBreakTableFirstChar && nextCh <= asciiLineBreakTableLastChar) {
+ const unsigned char* tableRow = asciiLineBreakTable[ch - asciiLineBreakTableFirstChar];
+ int nextChIndex = nextCh - asciiLineBreakTableFirstChar;
+ return tableRow[nextChIndex / 8] & (1 << (nextChIndex % 8));
+ }
+ // Otherwise defer to the Unicode algorithm by returning false.
+ return false;
+ }
+}
+
+static inline bool needsLineBreakIterator(UChar ch)
+{
+ return ch > asciiLineBreakTableLastChar && ch != noBreakSpace;
+}
+
+#if PLATFORM(MAC) && defined(BUILDING_ON_TIGER)
+static inline TextBreakLocatorRef lineBreakLocator()
+{
+ TextBreakLocatorRef locator = 0;
+ UCCreateTextBreakLocator(0, 0, kUCTextBreakLineMask, &locator);
+ return locator;
+}
+#endif
+
+int nextBreakablePosition(const UChar* str, int pos, int len, bool treatNoBreakSpaceAsBreak)
+{
+#if !PLATFORM(MAC) || !defined(BUILDING_ON_TIGER)
+ TextBreakIterator* breakIterator = 0;
+#endif
+ int nextBreak = -1;
+
+ UChar lastCh = pos > 0 ? str[pos - 1] : 0;
+ for (int i = pos; i < len; i++) {
+ UChar ch = str[i];
+
+ if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh, ch))
+ return i;
+
+ if (needsLineBreakIterator(ch) || needsLineBreakIterator(lastCh)) {
+ if (nextBreak < i && i) {
+#if !PLATFORM(MAC) || !defined(BUILDING_ON_TIGER)
+ if (!breakIterator)
+ breakIterator = lineBreakIterator(str, len);
+ if (breakIterator)
+ nextBreak = textBreakFollowing(breakIterator, i - 1);
+#else
+ static TextBreakLocatorRef breakLocator = lineBreakLocator();
+ if (breakLocator) {
+ UniCharArrayOffset nextUCBreak;
+ if (UCFindTextBreak(breakLocator, kUCTextBreakLineMask, 0, str, len, i, &nextUCBreak) == 0)
+ nextBreak = nextUCBreak;
+ }
+#endif
+ }
+ if (i == nextBreak && !isBreakableSpace(lastCh, treatNoBreakSpaceAsBreak))
+ return i;
+ }
+
+ lastCh = ch;
+ }
+
+ return len;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/break_lines.h b/Source/WebCore/rendering/break_lines.h
new file mode 100644
index 0000000..4d6b8dc
--- /dev/null
+++ b/Source/WebCore/rendering/break_lines.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef break_lines_h
+#define break_lines_h
+
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+ int nextBreakablePosition(const UChar*, int pos, int len, bool breakNBSP = false);
+
+ inline bool isBreakable(const UChar* str, int pos, int len, int& nextBreakable, bool breakNBSP = false)
+ {
+ if (pos > nextBreakable)
+ nextBreakable = nextBreakablePosition(str, pos, len, breakNBSP);
+ return pos == nextBreakable;
+ }
+
+} // namespace WebCore
+
+#endif // break_lines_h
diff --git a/Source/WebCore/rendering/style/BorderData.h b/Source/WebCore/rendering/style/BorderData.h
new file mode 100644
index 0000000..03635d9
--- /dev/null
+++ b/Source/WebCore/rendering/style/BorderData.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 BorderData_h
+#define BorderData_h
+
+#include "BorderValue.h"
+#include "LengthSize.h"
+#include "NinePieceImage.h"
+
+namespace WebCore {
+
+class BorderData {
+friend class RenderStyle;
+public:
+ BorderData() : m_topLeft(Length(0, Fixed), Length(0, Fixed))
+ , m_topRight(Length(0, Fixed), Length(0, Fixed))
+ , m_bottomLeft(Length(0, Fixed), Length(0, Fixed))
+ , m_bottomRight(Length(0, Fixed), Length(0, Fixed))
+ {
+ }
+ bool hasBorder() const
+ {
+ bool haveImage = m_image.hasImage();
+ return m_left.nonZero(!haveImage) || m_right.nonZero(!haveImage) || m_top.nonZero(!haveImage) || m_bottom.nonZero(!haveImage);
+ }
+
+ bool hasBorderRadius() const
+ {
+ if (m_topLeft.width().rawValue() > 0)
+ return true;
+ if (m_topRight.width().rawValue() > 0)
+ return true;
+ if (m_bottomLeft.width().rawValue() > 0)
+ return true;
+ if (m_bottomRight.width().rawValue() > 0)
+ return true;
+ return false;
+ }
+
+ unsigned short borderLeftWidth() const
+ {
+ if (!m_image.hasImage() && (m_left.style() == BNONE || m_left.style() == BHIDDEN))
+ return 0;
+ return m_left.width();
+ }
+
+ unsigned short borderRightWidth() const
+ {
+ if (!m_image.hasImage() && (m_right.style() == BNONE || m_right.style() == BHIDDEN))
+ return 0;
+ return m_right.width();
+ }
+
+ unsigned short borderTopWidth() const
+ {
+ if (!m_image.hasImage() && (m_top.style() == BNONE || m_top.style() == BHIDDEN))
+ return 0;
+ return m_top.width();
+ }
+
+ unsigned short borderBottomWidth() const
+ {
+ if (!m_image.hasImage() && (m_bottom.style() == BNONE || m_bottom.style() == BHIDDEN))
+ return 0;
+ return m_bottom.width();
+ }
+
+ bool operator==(const BorderData& o) const
+ {
+ return m_left == o.m_left && m_right == o.m_right && m_top == o.m_top && m_bottom == o.m_bottom && m_image == o.m_image
+ && m_topLeft == o.m_topLeft && m_topRight == o.m_topRight && m_bottomLeft == o.m_bottomLeft && m_bottomRight == o.m_bottomRight;
+ }
+
+ bool operator!=(const BorderData& o) const
+ {
+ return !(*this == o);
+ }
+
+ const BorderValue& left() const { return m_left; }
+ const BorderValue& right() const { return m_right; }
+ const BorderValue& top() const { return m_top; }
+ const BorderValue& bottom() const { return m_bottom; }
+
+ const NinePieceImage& image() const { return m_image; }
+
+ const LengthSize& topLeft() const { return m_topLeft; }
+ const LengthSize& topRight() const { return m_topRight; }
+ const LengthSize& bottomLeft() const { return m_bottomLeft; }
+ const LengthSize& bottomRight() const { return m_bottomRight; }
+
+private:
+ BorderValue m_left;
+ BorderValue m_right;
+ BorderValue m_top;
+ BorderValue m_bottom;
+
+ NinePieceImage m_image;
+
+ LengthSize m_topLeft;
+ LengthSize m_topRight;
+ LengthSize m_bottomLeft;
+ LengthSize m_bottomRight;
+};
+
+} // namespace WebCore
+
+#endif // BorderData_h
diff --git a/Source/WebCore/rendering/style/BorderValue.h b/Source/WebCore/rendering/style/BorderValue.h
new file mode 100644
index 0000000..3e6fd5d
--- /dev/null
+++ b/Source/WebCore/rendering/style/BorderValue.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 BorderValue_h
+#define BorderValue_h
+
+#include "Color.h"
+#include "RenderStyleConstants.h"
+
+namespace WebCore {
+
+class BorderValue {
+friend class RenderStyle;
+public:
+ BorderValue()
+ : m_width(3)
+ , m_style(BNONE)
+ {
+ }
+
+ bool nonZero(bool checkStyle = true) const
+ {
+ return width() && (!checkStyle || m_style != BNONE);
+ }
+
+ bool isTransparent() const
+ {
+ return m_color.isValid() && !m_color.alpha();
+ }
+
+ bool isVisible(bool checkStyle = true) const
+ {
+ return nonZero(checkStyle) && !isTransparent() && (!checkStyle || m_style != BHIDDEN);
+ }
+
+ bool operator==(const BorderValue& o) const
+ {
+ return m_width == o.m_width && m_style == o.m_style && m_color == o.m_color;
+ }
+
+ bool operator!=(const BorderValue& o) const
+ {
+ return !(*this == o);
+ }
+
+ const Color& color() const { return m_color; }
+ unsigned short width() const { return m_width; }
+ EBorderStyle style() const { return static_cast<EBorderStyle>(m_style); }
+
+protected:
+ Color m_color;
+ unsigned m_width : 12;
+ unsigned m_style : 4; // EBorderStyle
+};
+
+} // namespace WebCore
+
+#endif // BorderValue_h
diff --git a/Source/WebCore/rendering/style/CollapsedBorderValue.h b/Source/WebCore/rendering/style/CollapsedBorderValue.h
new file mode 100644
index 0000000..6207231
--- /dev/null
+++ b/Source/WebCore/rendering/style/CollapsedBorderValue.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 CollapsedBorderValue_h
+#define CollapsedBorderValue_h
+
+#include "BorderValue.h"
+
+namespace WebCore {
+
+class CollapsedBorderValue {
+friend class RenderStyle;
+public:
+ CollapsedBorderValue()
+ : m_border(0)
+ , m_precedence(BOFF)
+ {
+ }
+
+ CollapsedBorderValue(const BorderValue* b, Color c, EBorderPrecedence p)
+ : m_border(b)
+ , m_borderColor(c)
+ , m_precedence(p)
+ {
+ }
+
+ int width() const { return m_border && m_border->nonZero() ? m_border->width() : 0; }
+ EBorderStyle style() const { return m_border ? m_border->style() : BHIDDEN; }
+ bool exists() const { return m_border; }
+ const Color& color() const { return m_borderColor; }
+ bool isTransparent() const { return m_border ? m_border->isTransparent() : true; }
+ EBorderPrecedence precedence() const { return m_precedence; }
+
+ bool operator==(const CollapsedBorderValue& o) const
+ {
+ if (!m_border)
+ return !o.m_border;
+ if (!o.m_border)
+ return false;
+ return *m_border == *o.m_border && m_borderColor == o.m_borderColor && m_precedence == o.m_precedence;
+ }
+
+private:
+ const BorderValue* m_border;
+ Color m_borderColor;
+ EBorderPrecedence m_precedence;
+};
+
+} // namespace WebCore
+
+#endif // CollapsedBorderValue_h
diff --git a/Source/WebCore/rendering/style/ContentData.cpp b/Source/WebCore/rendering/style/ContentData.cpp
new file mode 100644
index 0000000..d150f77
--- /dev/null
+++ b/Source/WebCore/rendering/style/ContentData.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@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 "ContentData.h"
+
+#include "StyleImage.h"
+#include <wtf/text/StringImpl.h>
+
+namespace WebCore {
+
+void ContentData::clear()
+{
+ deleteContent();
+
+ // Delete the singly-linked list without recursing.
+ for (OwnPtr<ContentData> next = m_next.release(); next; next = next->m_next.release()) { }
+}
+
+// FIXME: Why isn't this just operator==?
+// FIXME: This is not a good name for a boolean-returning function.
+bool ContentData::dataEquivalent(const ContentData& other) const
+{
+ if (type() != other.type())
+ return false;
+
+ switch (type()) {
+ case CONTENT_NONE:
+ return true;
+ case CONTENT_TEXT:
+ return equal(text(), other.text());
+ case CONTENT_OBJECT:
+ return StyleImage::imagesEquivalent(image(), other.image());
+ case CONTENT_COUNTER:
+ return *counter() == *other.counter();
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+void ContentData::deleteContent()
+{
+ switch (m_type) {
+ case CONTENT_NONE:
+ break;
+ case CONTENT_OBJECT:
+ m_content.m_image->deref();
+ break;
+ case CONTENT_TEXT:
+ m_content.m_text->deref();
+ break;
+ case CONTENT_COUNTER:
+ delete m_content.m_counter;
+ break;
+ }
+
+ m_type = CONTENT_NONE;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/ContentData.h b/Source/WebCore/rendering/style/ContentData.h
new file mode 100644
index 0000000..4f964a2
--- /dev/null
+++ b/Source/WebCore/rendering/style/ContentData.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 ContentData_h
+#define ContentData_h
+
+#include "CounterContent.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class StyleImage;
+
+struct ContentData : Noncopyable {
+public:
+ ContentData()
+ : m_type(CONTENT_NONE)
+ {
+ }
+
+ ~ContentData()
+ {
+ clear();
+ }
+
+ void clear();
+
+ bool isCounter() const { return m_type == CONTENT_COUNTER; }
+ bool isImage() const { return m_type == CONTENT_OBJECT; }
+ bool isNone() const { return m_type == CONTENT_NONE; }
+ bool isText() const { return m_type == CONTENT_TEXT; }
+
+ StyleContentType type() const { return m_type; }
+
+ bool dataEquivalent(const ContentData&) const;
+
+ StyleImage* image() const
+ {
+ ASSERT(isImage());
+ return m_content.m_image;
+ }
+ void setImage(PassRefPtr<StyleImage> image)
+ {
+ deleteContent();
+ m_type = CONTENT_OBJECT;
+ m_content.m_image = image.leakRef();
+ }
+
+ StringImpl* text() const
+ {
+ ASSERT(isText());
+ return m_content.m_text;
+ }
+ void setText(PassRefPtr<StringImpl> text)
+ {
+ deleteContent();
+ m_type = CONTENT_TEXT;
+ m_content.m_text = text.leakRef();
+ }
+
+ CounterContent* counter() const
+ {
+ ASSERT(isCounter());
+ return m_content.m_counter;
+ }
+ void setCounter(PassOwnPtr<CounterContent> counter)
+ {
+ deleteContent();
+ m_type = CONTENT_COUNTER;
+ m_content.m_counter = counter.leakPtr();
+ }
+
+ ContentData* next() const { return m_next.get(); }
+ void setNext(PassOwnPtr<ContentData> next) { m_next = next; }
+
+private:
+ void deleteContent();
+
+ StyleContentType m_type;
+ union {
+ StyleImage* m_image;
+ StringImpl* m_text;
+ CounterContent* m_counter;
+ } m_content;
+ OwnPtr<ContentData> m_next;
+};
+
+} // namespace WebCore
+
+#endif // ContentData_h
diff --git a/Source/WebCore/rendering/style/CounterContent.h b/Source/WebCore/rendering/style/CounterContent.h
new file mode 100644
index 0000000..52757ad
--- /dev/null
+++ b/Source/WebCore/rendering/style/CounterContent.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 CounterContent_h
+#define CounterContent_h
+
+#include "RenderStyleConstants.h"
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class CounterContent : public FastAllocBase {
+public:
+ CounterContent(const AtomicString& identifier, EListStyleType style, const AtomicString& separator)
+ : m_identifier(identifier)
+ , m_listStyle(style)
+ , m_separator(separator)
+ {
+ }
+
+ const AtomicString& identifier() const { return m_identifier; }
+ EListStyleType listStyle() const { return m_listStyle; }
+ const AtomicString& separator() const { return m_separator; }
+
+private:
+ AtomicString m_identifier;
+ EListStyleType m_listStyle;
+ AtomicString m_separator;
+};
+
+static inline bool operator==(const CounterContent& a, const CounterContent& b)
+{
+ return a.identifier() == b.identifier()
+ && a.listStyle() == b.listStyle()
+ && a.separator() == b.separator();
+}
+
+
+} // namespace WebCore
+
+#endif // CounterContent_h
diff --git a/Source/WebCore/rendering/style/CounterDirectives.cpp b/Source/WebCore/rendering/style/CounterDirectives.cpp
new file mode 100644
index 0000000..a0ff52f
--- /dev/null
+++ b/Source/WebCore/rendering/style/CounterDirectives.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "CounterDirectives.h"
+
+namespace WebCore {
+
+bool operator==(const CounterDirectives& a, const CounterDirectives& b)
+{
+ if (a.m_reset != b.m_reset || a.m_increment != b.m_increment)
+ return false;
+ if (a.m_reset && a.m_resetValue != b.m_resetValue)
+ return false;
+ if (a.m_increment && a.m_incrementValue != b.m_incrementValue)
+ return false;
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/CounterDirectives.h b/Source/WebCore/rendering/style/CounterDirectives.h
new file mode 100644
index 0000000..e54028e
--- /dev/null
+++ b/Source/WebCore/rendering/style/CounterDirectives.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 CounterDirectives_h
+#define CounterDirectives_h
+
+#include <wtf/HashMap.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/AtomicStringImpl.h>
+
+namespace WebCore {
+
+struct CounterDirectives {
+ CounterDirectives()
+ : m_reset(false)
+ , m_increment(false)
+ {
+ }
+
+ bool m_reset;
+ int m_resetValue;
+ bool m_increment;
+ int m_incrementValue;
+};
+
+bool operator==(const CounterDirectives&, const CounterDirectives&);
+inline bool operator!=(const CounterDirectives& a, const CounterDirectives& b) { return !(a == b); }
+
+typedef HashMap<RefPtr<AtomicStringImpl>, CounterDirectives> CounterDirectiveMap;
+
+} // namespace WebCore
+
+#endif // CounterDirectives_h
diff --git a/Source/WebCore/rendering/style/CursorData.h b/Source/WebCore/rendering/style/CursorData.h
new file mode 100644
index 0000000..6d0a273
--- /dev/null
+++ b/Source/WebCore/rendering/style/CursorData.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 CursorData_h
+#define CursorData_h
+
+#include "IntPoint.h"
+#include "StyleImage.h"
+
+namespace WebCore {
+
+class CursorData {
+public:
+ CursorData(PassRefPtr<StyleImage> image, const IntPoint& hotSpot)
+ : m_image(image)
+ , m_hotSpot(hotSpot)
+ {
+ }
+
+ bool operator==(const CursorData& o) const
+ {
+ return m_hotSpot == o.m_hotSpot && m_image == o.m_image;
+ }
+
+ bool operator!=(const CursorData& o) const
+ {
+ return !(*this == o);
+ }
+
+ StyleImage* image() const { return m_image.get(); }
+ void setImage(PassRefPtr<StyleImage> image) { m_image = image; }
+
+ const IntPoint& hotSpot() const { return m_hotSpot; }
+
+private:
+ RefPtr<StyleImage> m_image;
+ IntPoint m_hotSpot; // for CSS3 support
+};
+
+} // namespace WebCore
+
+#endif // CursorData_h
diff --git a/Source/WebCore/rendering/style/CursorList.h b/Source/WebCore/rendering/style/CursorList.h
new file mode 100644
index 0000000..a1d1fe7
--- /dev/null
+++ b/Source/WebCore/rendering/style/CursorList.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 CursorList_h
+#define CursorList_h
+
+#include "CursorData.h"
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class CursorList : public RefCounted<CursorList> {
+public:
+ static PassRefPtr<CursorList> create()
+ {
+ return adoptRef(new CursorList);
+ }
+
+ const CursorData& operator[](int i) const { return m_vector[i]; }
+ CursorData& operator[](int i) { return m_vector[i]; }
+ const CursorData& at(size_t i) const { return m_vector.at(i); }
+ CursorData& at(size_t i) { return m_vector.at(i); }
+
+ bool operator==(const CursorList& o) const { return m_vector == o.m_vector; }
+ bool operator!=(const CursorList& o) const { return m_vector != o.m_vector; }
+
+ size_t size() const { return m_vector.size(); }
+ void append(const CursorData& cursorData) { m_vector.append(cursorData); }
+
+private:
+ CursorList()
+ {
+ }
+
+ Vector<CursorData> m_vector;
+};
+
+} // namespace WebCore
+
+#endif // CursorList_h
diff --git a/Source/WebCore/rendering/style/DataRef.h b/Source/WebCore/rendering/style/DataRef.h
new file mode 100644
index 0000000..c8d8072
--- /dev/null
+++ b/Source/WebCore/rendering/style/DataRef.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 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 DataRef_h
+#define DataRef_h
+
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+template <typename T> class DataRef {
+public:
+ const T* get() const { return m_data.get(); }
+
+ const T& operator*() const { return *get(); }
+ const T* operator->() const { return get(); }
+
+ T* access()
+ {
+ if (!m_data->hasOneRef())
+ m_data = m_data->copy();
+ return m_data.get();
+ }
+
+ void init()
+ {
+ ASSERT(!m_data);
+ m_data = T::create();
+ }
+
+ bool operator==(const DataRef<T>& o) const
+ {
+ ASSERT(m_data);
+ ASSERT(o.m_data);
+ return m_data == o.m_data || *m_data == *o.m_data;
+ }
+
+ bool operator!=(const DataRef<T>& o) const
+ {
+ ASSERT(m_data);
+ ASSERT(o.m_data);
+ return m_data != o.m_data && *m_data != *o.m_data;
+ }
+
+private:
+ RefPtr<T> m_data;
+};
+
+} // namespace WebCore
+
+#endif // DataRef_h
diff --git a/Source/WebCore/rendering/style/FillLayer.cpp b/Source/WebCore/rendering/style/FillLayer.cpp
new file mode 100644
index 0000000..59f3bb2
--- /dev/null
+++ b/Source/WebCore/rendering/style/FillLayer.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "FillLayer.h"
+
+namespace WebCore {
+
+FillLayer::FillLayer(EFillLayerType type)
+ : m_next(0)
+ , m_image(FillLayer::initialFillImage(type))
+ , m_xPosition(FillLayer::initialFillXPosition(type))
+ , m_yPosition(FillLayer::initialFillYPosition(type))
+ , m_attachment(FillLayer::initialFillAttachment(type))
+ , m_clip(FillLayer::initialFillClip(type))
+ , m_origin(FillLayer::initialFillOrigin(type))
+ , m_repeatX(FillLayer::initialFillRepeatX(type))
+ , m_repeatY(FillLayer::initialFillRepeatY(type))
+ , m_composite(FillLayer::initialFillComposite(type))
+ , m_sizeType(SizeNone)
+ , m_sizeLength(FillLayer::initialFillSizeLength(type))
+ , m_imageSet(false)
+ , m_attachmentSet(false)
+ , m_clipSet(false)
+ , m_originSet(false)
+ , m_repeatXSet(false)
+ , m_repeatYSet(false)
+ , m_xPosSet(false)
+ , m_yPosSet(false)
+ , m_compositeSet(type == MaskFillLayer)
+ , m_type(type)
+{
+}
+
+FillLayer::FillLayer(const FillLayer& o)
+ : m_next(o.m_next ? new FillLayer(*o.m_next) : 0)
+ , m_image(o.m_image)
+ , m_xPosition(o.m_xPosition)
+ , m_yPosition(o.m_yPosition)
+ , m_attachment(o.m_attachment)
+ , m_clip(o.m_clip)
+ , m_origin(o.m_origin)
+ , m_repeatX(o.m_repeatX)
+ , m_repeatY(o.m_repeatY)
+ , m_composite(o.m_composite)
+ , m_sizeType(o.m_sizeType)
+ , m_sizeLength(o.m_sizeLength)
+ , m_imageSet(o.m_imageSet)
+ , m_attachmentSet(o.m_attachmentSet)
+ , m_clipSet(o.m_clipSet)
+ , m_originSet(o.m_originSet)
+ , m_repeatXSet(o.m_repeatXSet)
+ , m_repeatYSet(o.m_repeatYSet)
+ , m_xPosSet(o.m_xPosSet)
+ , m_yPosSet(o.m_yPosSet)
+ , m_compositeSet(o.m_compositeSet)
+ , m_type(o.m_type)
+{
+}
+
+FillLayer::~FillLayer()
+{
+ delete m_next;
+}
+
+FillLayer& FillLayer::operator=(const FillLayer& o)
+{
+ if (m_next != o.m_next) {
+ delete m_next;
+ m_next = o.m_next ? new FillLayer(*o.m_next) : 0;
+ }
+
+ m_image = o.m_image;
+ m_xPosition = o.m_xPosition;
+ m_yPosition = o.m_yPosition;
+ m_attachment = o.m_attachment;
+ m_clip = o.m_clip;
+ m_composite = o.m_composite;
+ m_origin = o.m_origin;
+ m_repeatX = o.m_repeatX;
+ m_repeatY = o.m_repeatY;
+ m_sizeType = o.m_sizeType;
+ m_sizeLength = o.m_sizeLength;
+
+ m_imageSet = o.m_imageSet;
+ m_attachmentSet = o.m_attachmentSet;
+ m_clipSet = o.m_clipSet;
+ m_compositeSet = o.m_compositeSet;
+ m_originSet = o.m_originSet;
+ m_repeatXSet = o.m_repeatXSet;
+ m_repeatYSet = o.m_repeatYSet;
+ m_xPosSet = o.m_xPosSet;
+ m_yPosSet = o.m_yPosSet;
+
+ m_type = o.m_type;
+
+ return *this;
+}
+
+bool FillLayer::operator==(const FillLayer& o) const
+{
+ // We do not check the "isSet" booleans for each property, since those are only used during initial construction
+ // to propagate patterns into layers. All layer comparisons happen after values have all been filled in anyway.
+ return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition &&
+ m_attachment == o.m_attachment && m_clip == o.m_clip &&
+ m_composite == o.m_composite && m_origin == o.m_origin && m_repeatX == o.m_repeatX &&
+ m_repeatY == o.m_repeatY && m_sizeType == o.m_sizeType && m_sizeLength == o.m_sizeLength &&
+ m_type == o.m_type && ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next);
+}
+
+void FillLayer::fillUnsetProperties()
+{
+ FillLayer* curr;
+ for (curr = this; curr && curr->isImageSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_image = pattern->m_image;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_xPosition = pattern->m_xPosition;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_yPosition = pattern->m_yPosition;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_attachment = pattern->m_attachment;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_clip = pattern->m_clip;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_composite = pattern->m_composite;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_origin = pattern->m_origin;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isRepeatXSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_repeatX = pattern->m_repeatX;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isRepeatYSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_repeatY = pattern->m_repeatY;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+
+ for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { }
+ if (curr && curr != this) {
+ // We need to fill in the remaining values with the pattern specified.
+ for (FillLayer* pattern = this; curr; curr = curr->next()) {
+ curr->m_sizeType = pattern->m_sizeType;
+ curr->m_sizeLength = pattern->m_sizeLength;
+ pattern = pattern->next();
+ if (pattern == curr || !pattern)
+ pattern = this;
+ }
+ }
+}
+
+void FillLayer::cullEmptyLayers()
+{
+ FillLayer* next;
+ for (FillLayer* p = this; p; p = next) {
+ next = p->m_next;
+ if (next && !next->isImageSet() &&
+ !next->isXPositionSet() && !next->isYPositionSet() &&
+ !next->isAttachmentSet() && !next->isClipSet() &&
+ !next->isCompositeSet() && !next->isOriginSet() &&
+ !next->isRepeatXSet() && !next->isRepeatYSet()
+ && !next->isSizeSet()) {
+ delete next;
+ p->m_next = 0;
+ break;
+ }
+ }
+}
+
+bool FillLayer::containsImage(StyleImage* s) const
+{
+ if (!s)
+ return false;
+ if (m_image && *s == *m_image)
+ return true;
+ if (m_next)
+ return m_next->containsImage(s);
+ return false;
+}
+
+bool FillLayer::imagesAreLoaded() const
+{
+ const FillLayer* curr;
+ for (curr = this; curr; curr = curr->next()) {
+ if (curr->m_image && !curr->m_image->isLoaded())
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/FillLayer.h b/Source/WebCore/rendering/style/FillLayer.h
new file mode 100644
index 0000000..49fb294
--- /dev/null
+++ b/Source/WebCore/rendering/style/FillLayer.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 FillLayer_h
+#define FillLayer_h
+
+#include "GraphicsTypes.h"
+#include "Length.h"
+#include "LengthSize.h"
+#include "RenderStyleConstants.h"
+#include "StyleImage.h"
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+struct FillSize {
+ FillSize()
+ : type(SizeLength)
+ {
+ }
+
+ FillSize(EFillSizeType t, LengthSize l)
+ : type(t)
+ , size(l)
+ {
+ }
+
+ bool operator==(const FillSize& o) const
+ {
+ return type == o.type && size == o.size;
+ }
+ bool operator!=(const FillSize& o) const
+ {
+ return !(*this == o);
+ }
+
+ EFillSizeType type;
+ LengthSize size;
+};
+
+class FillLayer : public FastAllocBase {
+public:
+ FillLayer(EFillLayerType);
+ ~FillLayer();
+
+ StyleImage* image() const { return m_image.get(); }
+ Length xPosition() const { return m_xPosition; }
+ Length yPosition() const { return m_yPosition; }
+ EFillAttachment attachment() const { return static_cast<EFillAttachment>(m_attachment); }
+ EFillBox clip() const { return static_cast<EFillBox>(m_clip); }
+ EFillBox origin() const { return static_cast<EFillBox>(m_origin); }
+ EFillRepeat repeatX() const { return static_cast<EFillRepeat>(m_repeatX); }
+ EFillRepeat repeatY() const { return static_cast<EFillRepeat>(m_repeatY); }
+ CompositeOperator composite() const { return static_cast<CompositeOperator>(m_composite); }
+ LengthSize sizeLength() const { return m_sizeLength; }
+ EFillSizeType sizeType() const { return static_cast<EFillSizeType>(m_sizeType); }
+ FillSize size() const { return FillSize(static_cast<EFillSizeType>(m_sizeType), m_sizeLength); }
+
+ const FillLayer* next() const { return m_next; }
+ FillLayer* next() { return m_next; }
+
+ bool isImageSet() const { return m_imageSet; }
+ bool isXPositionSet() const { return m_xPosSet; }
+ bool isYPositionSet() const { return m_yPosSet; }
+ bool isAttachmentSet() const { return m_attachmentSet; }
+ bool isClipSet() const { return m_clipSet; }
+ bool isOriginSet() const { return m_originSet; }
+ bool isRepeatXSet() const { return m_repeatXSet; }
+ bool isRepeatYSet() const { return m_repeatYSet; }
+ bool isCompositeSet() const { return m_compositeSet; }
+ bool isSizeSet() const { return m_sizeType != SizeNone; }
+
+ void setImage(StyleImage* i) { m_image = i; m_imageSet = true; }
+ void setXPosition(Length l) { m_xPosition = l; m_xPosSet = true; }
+ void setYPosition(Length l) { m_yPosition = l; m_yPosSet = true; }
+ void setAttachment(EFillAttachment attachment) { m_attachment = attachment; m_attachmentSet = true; }
+ void setClip(EFillBox b) { m_clip = b; m_clipSet = true; }
+ void setOrigin(EFillBox b) { m_origin = b; m_originSet = true; }
+ void setRepeatX(EFillRepeat r) { m_repeatX = r; m_repeatXSet = true; }
+ void setRepeatY(EFillRepeat r) { m_repeatY = r; m_repeatYSet = true; }
+ void setComposite(CompositeOperator c) { m_composite = c; m_compositeSet = true; }
+ void setSizeType(EFillSizeType b) { m_sizeType = b; }
+ void setSizeLength(LengthSize l) { m_sizeLength = l; }
+ void setSize(FillSize f) { m_sizeType = f.type; m_sizeLength = f.size; }
+
+ void clearImage() { m_imageSet = false; }
+ void clearXPosition() { m_xPosSet = false; }
+ void clearYPosition() { m_yPosSet = false; }
+ void clearAttachment() { m_attachmentSet = false; }
+ void clearClip() { m_clipSet = false; }
+ void clearOrigin() { m_originSet = false; }
+ void clearRepeatX() { m_repeatXSet = false; }
+ void clearRepeatY() { m_repeatYSet = false; }
+ void clearComposite() { m_compositeSet = false; }
+ void clearSize() { m_sizeType = SizeNone; }
+
+ void setNext(FillLayer* n) { if (m_next != n) { delete m_next; m_next = n; } }
+
+ FillLayer& operator=(const FillLayer& o);
+ FillLayer(const FillLayer& o);
+
+ bool operator==(const FillLayer& o) const;
+ bool operator!=(const FillLayer& o) const
+ {
+ return !(*this == o);
+ }
+
+ bool containsImage(StyleImage*) const;
+ bool imagesAreLoaded() const;
+
+ bool hasImage() const
+ {
+ if (m_image)
+ return true;
+ return m_next ? m_next->hasImage() : false;
+ }
+
+ bool hasFixedImage() const
+ {
+ if (m_image && m_attachment == FixedBackgroundAttachment)
+ return true;
+ return m_next ? m_next->hasFixedImage() : false;
+ }
+
+ EFillLayerType type() const { return static_cast<EFillLayerType>(m_type); }
+
+ void fillUnsetProperties();
+ void cullEmptyLayers();
+
+ static EFillAttachment initialFillAttachment(EFillLayerType) { return ScrollBackgroundAttachment; }
+ static EFillBox initialFillClip(EFillLayerType) { return BorderFillBox; }
+ static EFillBox initialFillOrigin(EFillLayerType type) { return type == BackgroundFillLayer ? PaddingFillBox : BorderFillBox; }
+ static EFillRepeat initialFillRepeatX(EFillLayerType) { return RepeatFill; }
+ static EFillRepeat initialFillRepeatY(EFillLayerType) { return RepeatFill; }
+ static CompositeOperator initialFillComposite(EFillLayerType) { return CompositeSourceOver; }
+ static EFillSizeType initialFillSizeType(EFillLayerType) { return SizeLength; }
+ static LengthSize initialFillSizeLength(EFillLayerType) { return LengthSize(); }
+ static FillSize initialFillSize(EFillLayerType) { return FillSize(); }
+ static Length initialFillXPosition(EFillLayerType) { return Length(0.0, Percent); }
+ static Length initialFillYPosition(EFillLayerType) { return Length(0.0, Percent); }
+ static StyleImage* initialFillImage(EFillLayerType) { return 0; }
+
+private:
+ friend class RenderStyle;
+
+ FillLayer() { }
+
+ FillLayer* m_next;
+
+ RefPtr<StyleImage> m_image;
+
+ Length m_xPosition;
+ Length m_yPosition;
+
+ unsigned m_attachment : 2; // EFillAttachment
+ unsigned m_clip : 2; // EFillBox
+ unsigned m_origin : 2; // EFillBox
+ unsigned m_repeatX : 3; // EFillRepeat
+ unsigned m_repeatY : 3; // EFillRepeat
+ unsigned m_composite : 4; // CompositeOperator
+ unsigned m_sizeType : 2; // EFillSizeType
+
+ LengthSize m_sizeLength;
+
+ bool m_imageSet : 1;
+ bool m_attachmentSet : 1;
+ bool m_clipSet : 1;
+ bool m_originSet : 1;
+ bool m_repeatXSet : 1;
+ bool m_repeatYSet : 1;
+ bool m_xPosSet : 1;
+ bool m_yPosSet : 1;
+ bool m_compositeSet : 1;
+
+ unsigned m_type : 1; // EFillLayerType
+};
+
+} // namespace WebCore
+
+#endif // FillLayer_h
diff --git a/Source/WebCore/rendering/style/KeyframeList.cpp b/Source/WebCore/rendering/style/KeyframeList.cpp
new file mode 100644
index 0000000..bafa426
--- /dev/null
+++ b/Source/WebCore/rendering/style/KeyframeList.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "KeyframeList.h"
+#include "RenderObject.h"
+
+namespace WebCore {
+
+KeyframeList::~KeyframeList()
+{
+ clear();
+}
+
+void KeyframeList::clear()
+{
+ m_keyframes.clear();
+ m_properties.clear();
+}
+
+bool KeyframeList::operator==(const KeyframeList& o) const
+{
+ if (m_keyframes.size() != o.m_keyframes.size())
+ return false;
+
+ Vector<KeyframeValue>::const_iterator it2 = o.m_keyframes.begin();
+ for (Vector<KeyframeValue>::const_iterator it1 = m_keyframes.begin(); it1 != m_keyframes.end(); ++it1) {
+ if (it1->key() != it2->key())
+ return false;
+ const RenderStyle& style1 = *it1->style();
+ const RenderStyle& style2 = *it2->style();
+ if (style1 != style2)
+ return false;
+ ++it2;
+ }
+
+ return true;
+}
+
+void KeyframeList::insert(const KeyframeValue& keyframe)
+{
+ if (keyframe.key() < 0 || keyframe.key() > 1)
+ return;
+
+ bool inserted = false;
+ bool replaced = false;
+ for (size_t i = 0; i < m_keyframes.size(); ++i) {
+ if (m_keyframes[i].key() == keyframe.key()) {
+ m_keyframes[i] = keyframe;
+ replaced = true;
+ break;
+ }
+
+ if (m_keyframes[i].key() > keyframe.key()) {
+ // insert before
+ m_keyframes.insert(i, keyframe);
+ inserted = true;
+ break;
+ }
+ }
+
+ if (!replaced && !inserted)
+ m_keyframes.append(keyframe);
+
+ if (replaced) {
+ // We have to rebuild the properties list from scratch.
+ m_properties.clear();
+ for (Vector<KeyframeValue>::const_iterator it = m_keyframes.begin(); it != m_keyframes.end(); ++it) {
+ const KeyframeValue& currKeyframe = *it;
+ for (HashSet<int>::const_iterator it = currKeyframe.properties().begin(); it != currKeyframe.properties().end(); ++it)
+ m_properties.add(*it);
+ }
+ } else {
+ for (HashSet<int>::const_iterator it = keyframe.properties().begin(); it != keyframe.properties().end(); ++it)
+ m_properties.add(*it);
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/KeyframeList.h b/Source/WebCore/rendering/style/KeyframeList.h
new file mode 100644
index 0000000..64170ce
--- /dev/null
+++ b/Source/WebCore/rendering/style/KeyframeList.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 KeyframeList_h
+#define KeyframeList_h
+
+#include <wtf/Vector.h>
+#include <wtf/HashSet.h>
+#include <wtf/RefPtr.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class RenderObject;
+class RenderStyle;
+
+class KeyframeValue {
+public:
+ KeyframeValue(float key, PassRefPtr<RenderStyle> style)
+ : m_key(key)
+ , m_style(style)
+ {
+ }
+
+ void addProperty(int prop) { m_properties.add(prop); }
+ bool containsProperty(int prop) const { return m_properties.contains(prop); }
+ const HashSet<int>& properties() const { return m_properties; }
+
+ float key() const { return m_key; }
+ void setKey(float key) { m_key = key; }
+
+ const RenderStyle* style() const { return m_style.get(); }
+ void setStyle(PassRefPtr<RenderStyle> style) { m_style = style; }
+
+private:
+ float m_key;
+ HashSet<int> m_properties; // The properties specified in this keyframe.
+ RefPtr<RenderStyle> m_style;
+};
+
+class KeyframeList {
+public:
+ KeyframeList(RenderObject* renderer, const AtomicString& animationName)
+ : m_animationName(animationName)
+ , m_renderer(renderer)
+ {
+ insert(KeyframeValue(0, 0));
+ insert(KeyframeValue(1, 0));
+ }
+ ~KeyframeList();
+
+ bool operator==(const KeyframeList& o) const;
+ bool operator!=(const KeyframeList& o) const { return !(*this == o); }
+
+ const AtomicString& animationName() const { return m_animationName; }
+
+ void insert(const KeyframeValue& keyframe);
+
+ void addProperty(int prop) { m_properties.add(prop); }
+ bool containsProperty(int prop) const { return m_properties.contains(prop); }
+ HashSet<int>::const_iterator beginProperties() const { return m_properties.begin(); }
+ HashSet<int>::const_iterator endProperties() const { return m_properties.end(); }
+
+ void clear();
+ bool isEmpty() const { return m_keyframes.isEmpty(); }
+ size_t size() const { return m_keyframes.size(); }
+ const KeyframeValue& operator[](size_t index) const { return m_keyframes[index]; }
+
+private:
+ AtomicString m_animationName;
+ Vector<KeyframeValue> m_keyframes; // kept sorted by key
+ HashSet<int> m_properties; // the properties being animated
+ RenderObject* m_renderer;
+};
+
+} // namespace WebCore
+
+#endif // KeyframeList_h
diff --git a/Source/WebCore/rendering/style/LineClampValue.h b/Source/WebCore/rendering/style/LineClampValue.h
new file mode 100644
index 0000000..2119ca2
--- /dev/null
+++ b/Source/WebCore/rendering/style/LineClampValue.h
@@ -0,0 +1,69 @@
+/*
+ * 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. 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 LineClampValue_h
+#define LineClampValue_h
+
+#include "RenderStyleConstants.h"
+
+namespace WebCore {
+
+class LineClampValue {
+public:
+ LineClampValue()
+ : m_type(LineClampLineCount)
+ , m_value(-1)
+ {
+ }
+
+ LineClampValue(int value, ELineClampType type)
+ : m_type(type)
+ , m_value(value)
+ {
+ }
+
+ int value() const { return m_value; }
+
+ bool isPercentage() const { return m_type == LineClampPercentage; }
+
+ bool isNone() const { return m_value == -1; }
+
+ bool operator==(const LineClampValue& o) const
+ {
+ return value() == o.value() && isPercentage() == o.isPercentage();
+ }
+
+ bool operator!=(const LineClampValue& o) const
+ {
+ return !(*this == o);
+ }
+
+private:
+ ELineClampType m_type;
+ int m_value;
+};
+
+} // namespace WebCore
+
+#endif // LineClampValue_h
diff --git a/Source/WebCore/rendering/style/NinePieceImage.cpp b/Source/WebCore/rendering/style/NinePieceImage.cpp
new file mode 100644
index 0000000..d585e8f
--- /dev/null
+++ b/Source/WebCore/rendering/style/NinePieceImage.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 "NinePieceImage.h"
+
+namespace WebCore {
+
+bool NinePieceImage::operator==(const NinePieceImage& o) const
+{
+ return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_slices == o.m_slices && m_horizontalRule == o.m_horizontalRule &&
+ m_verticalRule == o.m_verticalRule;
+}
+
+}
diff --git a/Source/WebCore/rendering/style/NinePieceImage.h b/Source/WebCore/rendering/style/NinePieceImage.h
new file mode 100644
index 0000000..c400551
--- /dev/null
+++ b/Source/WebCore/rendering/style/NinePieceImage.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 NinePieceImage_h
+#define NinePieceImage_h
+
+#include "LengthBox.h"
+#include "StyleImage.h"
+
+namespace WebCore {
+
+enum ENinePieceImageRule {
+ StretchImageRule, RoundImageRule, RepeatImageRule
+};
+
+class NinePieceImage {
+public:
+ NinePieceImage()
+ : m_image(0)
+ , m_horizontalRule(StretchImageRule)
+ , m_verticalRule(StretchImageRule)
+ {
+ }
+
+ NinePieceImage(StyleImage* image, LengthBox slices, ENinePieceImageRule h, ENinePieceImageRule v)
+ : m_image(image)
+ , m_slices(slices)
+ , m_horizontalRule(h)
+ , m_verticalRule(v)
+ {
+ }
+
+ bool operator==(const NinePieceImage& o) const;
+ bool operator!=(const NinePieceImage& o) const { return !(*this == o); }
+
+ bool hasImage() const { return m_image != 0; }
+ StyleImage* image() const { return m_image.get(); }
+ void setImage(StyleImage* image) { m_image = image; }
+
+ const LengthBox& slices() const { return m_slices; }
+ void setSlices(const LengthBox& l) { m_slices = l; }
+
+ ENinePieceImageRule horizontalRule() const { return static_cast<ENinePieceImageRule>(m_horizontalRule); }
+ void setHorizontalRule(ENinePieceImageRule rule) { m_horizontalRule = rule; }
+
+ ENinePieceImageRule verticalRule() const { return static_cast<ENinePieceImageRule>(m_verticalRule); }
+ void setVerticalRule(ENinePieceImageRule rule) { m_verticalRule = rule; }
+
+private:
+ RefPtr<StyleImage> m_image;
+ LengthBox m_slices;
+ unsigned m_horizontalRule : 2; // ENinePieceImageRule
+ unsigned m_verticalRule : 2; // ENinePieceImageRule
+};
+
+} // namespace WebCore
+
+#endif // NinePieceImage_h
diff --git a/Source/WebCore/rendering/style/OutlineValue.h b/Source/WebCore/rendering/style/OutlineValue.h
new file mode 100644
index 0000000..19c17a7
--- /dev/null
+++ b/Source/WebCore/rendering/style/OutlineValue.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 OutlineValue_h
+#define OutlineValue_h
+
+#include "BorderValue.h"
+
+namespace WebCore {
+
+class OutlineValue : public BorderValue {
+friend class RenderStyle;
+public:
+ OutlineValue()
+ : m_offset(0)
+ , m_isAuto(false)
+ {
+ }
+
+ bool operator==(const OutlineValue& o) const
+ {
+ return m_width == o.m_width && m_style == o.m_style && m_color == o.m_color && m_offset == o.m_offset && m_isAuto == o.m_isAuto;
+ }
+
+ bool operator!=(const OutlineValue& o) const
+ {
+ return !(*this == o);
+ }
+
+ int offset() const { return m_offset; }
+ bool isAuto() const { return m_isAuto; }
+
+private:
+ int m_offset;
+ bool m_isAuto;
+};
+
+} // namespace WebCore
+
+#endif // OutlineValue_h
diff --git a/Source/WebCore/rendering/style/RenderStyle.cpp b/Source/WebCore/rendering/style/RenderStyle.cpp
new file mode 100644
index 0000000..881818c
--- /dev/null
+++ b/Source/WebCore/rendering/style/RenderStyle.cpp
@@ -0,0 +1,1473 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@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 "RenderStyle.h"
+
+#include "CSSPropertyNames.h"
+#include "CSSStyleSelector.h"
+#include "FontSelector.h"
+#include "RenderArena.h"
+#include "RenderObject.h"
+#include "ScaleTransformOperation.h"
+#include "StyleImage.h"
+#include <wtf/StdLibExtras.h>
+#include <algorithm>
+
+using namespace std;
+
+namespace WebCore {
+
+inline RenderStyle* defaultStyle()
+{
+ static RenderStyle* s_defaultStyle = RenderStyle::createDefaultStyle().releaseRef();
+ return s_defaultStyle;
+}
+
+PassRefPtr<RenderStyle> RenderStyle::create()
+{
+ return adoptRef(new RenderStyle());
+}
+
+PassRefPtr<RenderStyle> RenderStyle::createDefaultStyle()
+{
+ return adoptRef(new RenderStyle(true));
+}
+
+PassRefPtr<RenderStyle> RenderStyle::clone(const RenderStyle* other)
+{
+ return adoptRef(new RenderStyle(*other));
+}
+
+ALWAYS_INLINE RenderStyle::RenderStyle()
+ : m_affectedByAttributeSelectors(false)
+ , m_unique(false)
+ , m_affectedByEmpty(false)
+ , m_emptyState(false)
+ , m_childrenAffectedByFirstChildRules(false)
+ , m_childrenAffectedByLastChildRules(false)
+ , m_childrenAffectedByDirectAdjacentRules(false)
+ , m_childrenAffectedByForwardPositionalRules(false)
+ , m_childrenAffectedByBackwardPositionalRules(false)
+ , m_firstChildState(false)
+ , m_lastChildState(false)
+ , m_childIndex(0)
+ , m_box(defaultStyle()->m_box)
+ , visual(defaultStyle()->visual)
+ , m_background(defaultStyle()->m_background)
+ , surround(defaultStyle()->surround)
+ , rareNonInheritedData(defaultStyle()->rareNonInheritedData)
+ , rareInheritedData(defaultStyle()->rareInheritedData)
+ , inherited(defaultStyle()->inherited)
+#if ENABLE(SVG)
+ , m_svgStyle(defaultStyle()->m_svgStyle)
+#endif
+{
+ setBitDefaults(); // Would it be faster to copy this from the default style?
+}
+
+ALWAYS_INLINE RenderStyle::RenderStyle(bool)
+ : m_affectedByAttributeSelectors(false)
+ , m_unique(false)
+ , m_affectedByEmpty(false)
+ , m_emptyState(false)
+ , m_childrenAffectedByFirstChildRules(false)
+ , m_childrenAffectedByLastChildRules(false)
+ , m_childrenAffectedByDirectAdjacentRules(false)
+ , m_childrenAffectedByForwardPositionalRules(false)
+ , m_childrenAffectedByBackwardPositionalRules(false)
+ , m_firstChildState(false)
+ , m_lastChildState(false)
+ , m_childIndex(0)
+{
+ setBitDefaults();
+
+ m_box.init();
+ visual.init();
+ m_background.init();
+ surround.init();
+ rareNonInheritedData.init();
+ rareNonInheritedData.access()->flexibleBox.init();
+ rareNonInheritedData.access()->marquee.init();
+ rareNonInheritedData.access()->m_multiCol.init();
+ rareNonInheritedData.access()->m_transform.init();
+ rareInheritedData.init();
+ inherited.init();
+
+#if ENABLE(SVG)
+ m_svgStyle.init();
+#endif
+}
+
+ALWAYS_INLINE RenderStyle::RenderStyle(const RenderStyle& o)
+ : RefCounted<RenderStyle>()
+ , m_affectedByAttributeSelectors(false)
+ , m_unique(false)
+ , m_affectedByEmpty(false)
+ , m_emptyState(false)
+ , m_childrenAffectedByFirstChildRules(false)
+ , m_childrenAffectedByLastChildRules(false)
+ , m_childrenAffectedByDirectAdjacentRules(false)
+ , m_childrenAffectedByForwardPositionalRules(false)
+ , m_childrenAffectedByBackwardPositionalRules(false)
+ , m_firstChildState(false)
+ , m_lastChildState(false)
+ , m_childIndex(0)
+ , m_box(o.m_box)
+ , visual(o.visual)
+ , m_background(o.m_background)
+ , surround(o.surround)
+ , rareNonInheritedData(o.rareNonInheritedData)
+ , rareInheritedData(o.rareInheritedData)
+ , inherited(o.inherited)
+#if ENABLE(SVG)
+ , m_svgStyle(o.m_svgStyle)
+#endif
+ , inherited_flags(o.inherited_flags)
+ , noninherited_flags(o.noninherited_flags)
+{
+}
+
+void RenderStyle::inheritFrom(const RenderStyle* inheritParent)
+{
+ rareInheritedData = inheritParent->rareInheritedData;
+ inherited = inheritParent->inherited;
+ inherited_flags = inheritParent->inherited_flags;
+#if ENABLE(SVG)
+ if (m_svgStyle != inheritParent->m_svgStyle)
+ m_svgStyle.access()->inheritFrom(inheritParent->m_svgStyle.get());
+#endif
+}
+
+RenderStyle::~RenderStyle()
+{
+}
+
+bool RenderStyle::operator==(const RenderStyle& o) const
+{
+ // compare everything except the pseudoStyle pointer
+ return inherited_flags == o.inherited_flags &&
+ noninherited_flags == o.noninherited_flags &&
+ m_box == o.m_box &&
+ visual == o.visual &&
+ m_background == o.m_background &&
+ surround == o.surround &&
+ rareNonInheritedData == o.rareNonInheritedData &&
+ rareInheritedData == o.rareInheritedData &&
+ inherited == o.inherited
+#if ENABLE(SVG)
+ && m_svgStyle == o.m_svgStyle
+#endif
+ ;
+}
+
+bool RenderStyle::isStyleAvailable() const
+{
+ return this != CSSStyleSelector::styleNotYetAvailable();
+}
+
+static inline int pseudoBit(PseudoId pseudo)
+{
+ return 1 << (pseudo - 1);
+}
+
+bool RenderStyle::hasAnyPublicPseudoStyles() const
+{
+ return PUBLIC_PSEUDOID_MASK & noninherited_flags._pseudoBits;
+}
+
+bool RenderStyle::hasPseudoStyle(PseudoId pseudo) const
+{
+ ASSERT(pseudo > NOPSEUDO);
+ ASSERT(pseudo < FIRST_INTERNAL_PSEUDOID);
+ return pseudoBit(pseudo) & noninherited_flags._pseudoBits;
+}
+
+void RenderStyle::setHasPseudoStyle(PseudoId pseudo)
+{
+ ASSERT(pseudo > NOPSEUDO);
+ ASSERT(pseudo < FIRST_INTERNAL_PSEUDOID);
+ noninherited_flags._pseudoBits |= pseudoBit(pseudo);
+}
+
+RenderStyle* RenderStyle::getCachedPseudoStyle(PseudoId pid) const
+{
+ ASSERT(styleType() != VISITED_LINK);
+
+ if (!m_cachedPseudoStyles || !m_cachedPseudoStyles->size())
+ return 0;
+
+ if (styleType() != NOPSEUDO) {
+ if (pid == VISITED_LINK)
+ return m_cachedPseudoStyles->at(0)->styleType() == VISITED_LINK ? m_cachedPseudoStyles->at(0).get() : 0;
+ return 0;
+ }
+
+ for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) {
+ RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get();
+ if (pseudoStyle->styleType() == pid)
+ return pseudoStyle;
+ }
+
+ return 0;
+}
+
+RenderStyle* RenderStyle::addCachedPseudoStyle(PassRefPtr<RenderStyle> pseudo)
+{
+ if (!pseudo)
+ return 0;
+
+ RenderStyle* result = pseudo.get();
+
+ if (!m_cachedPseudoStyles)
+ m_cachedPseudoStyles.set(new PseudoStyleCache);
+
+ m_cachedPseudoStyles->append(pseudo);
+
+ return result;
+}
+
+void RenderStyle::removeCachedPseudoStyle(PseudoId pid)
+{
+ if (!m_cachedPseudoStyles)
+ return;
+ for (size_t i = 0; i < m_cachedPseudoStyles->size(); ++i) {
+ RenderStyle* pseudoStyle = m_cachedPseudoStyles->at(i).get();
+ if (pseudoStyle->styleType() == pid) {
+ m_cachedPseudoStyles->remove(i);
+ return;
+ }
+ }
+}
+
+bool RenderStyle::inheritedNotEqual(const RenderStyle* other) const
+{
+ return inherited_flags != other->inherited_flags ||
+ inherited != other->inherited ||
+#if ENABLE(SVG)
+ m_svgStyle->inheritedNotEqual(other->m_svgStyle.get()) ||
+#endif
+ rareInheritedData != other->rareInheritedData;
+}
+
+static bool positionedObjectMoved(const LengthBox& a, const LengthBox& b)
+{
+ // If any unit types are different, then we can't guarantee
+ // that this was just a movement.
+ if (a.left().type() != b.left().type() ||
+ a.right().type() != b.right().type() ||
+ a.top().type() != b.top().type() ||
+ a.bottom().type() != b.bottom().type())
+ return false;
+
+ // Only one unit can be non-auto in the horizontal direction and
+ // in the vertical direction. Otherwise the adjustment of values
+ // is changing the size of the box.
+ if (!a.left().isIntrinsicOrAuto() && !a.right().isIntrinsicOrAuto())
+ return false;
+ if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto())
+ return false;
+
+ // One of the units is fixed or percent in both directions and stayed
+ // that way in the new style. Therefore all we are doing is moving.
+ return true;
+}
+
+/*
+ compares two styles. The result gives an idea of the action that
+ needs to be taken when replacing the old style with a new one.
+
+ CbLayout: The containing block of the object needs a relayout.
+ Layout: the RenderObject needs a relayout after the style change
+ Visible: The change is visible, but no relayout is needed
+ NonVisible: The object does need neither repaint nor relayout after
+ the change.
+
+ ### TODO:
+ A lot can be optimised here based on the display type, lots of
+ optimisations are unimplemented, and currently result in the
+ worst case result causing a relayout of the containing block.
+*/
+StyleDifference RenderStyle::diff(const RenderStyle* other, unsigned& changedContextSensitiveProperties) const
+{
+ changedContextSensitiveProperties = ContextSensitivePropertyNone;
+
+#if ENABLE(SVG)
+ StyleDifference svgChange = StyleDifferenceEqual;
+ if (m_svgStyle != other->m_svgStyle) {
+ svgChange = m_svgStyle->diff(other->m_svgStyle.get());
+ if (svgChange == StyleDifferenceLayout)
+ return svgChange;
+ }
+#endif
+
+ if (m_box->width() != other->m_box->width() ||
+ m_box->minWidth() != other->m_box->minWidth() ||
+ m_box->maxWidth() != other->m_box->maxWidth() ||
+ m_box->height() != other->m_box->height() ||
+ m_box->minHeight() != other->m_box->minHeight() ||
+ m_box->maxHeight() != other->m_box->maxHeight())
+ return StyleDifferenceLayout;
+
+ if (m_box->verticalAlign() != other->m_box->verticalAlign() || noninherited_flags._vertical_align != other->noninherited_flags._vertical_align)
+ return StyleDifferenceLayout;
+
+ if (m_box->boxSizing() != other->m_box->boxSizing())
+ return StyleDifferenceLayout;
+
+ if (surround->margin != other->surround->margin)
+ return StyleDifferenceLayout;
+
+ if (surround->padding != other->surround->padding)
+ return StyleDifferenceLayout;
+
+ if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) {
+ if (rareNonInheritedData->m_appearance != other->rareNonInheritedData->m_appearance ||
+ rareNonInheritedData->marginBeforeCollapse != other->rareNonInheritedData->marginBeforeCollapse ||
+ rareNonInheritedData->marginAfterCollapse != other->rareNonInheritedData->marginAfterCollapse ||
+ rareNonInheritedData->lineClamp != other->rareNonInheritedData->lineClamp ||
+ rareNonInheritedData->textOverflow != other->rareNonInheritedData->textOverflow)
+ return StyleDifferenceLayout;
+
+ if (rareNonInheritedData->flexibleBox.get() != other->rareNonInheritedData->flexibleBox.get() &&
+ *rareNonInheritedData->flexibleBox.get() != *other->rareNonInheritedData->flexibleBox.get())
+ return StyleDifferenceLayout;
+
+ // FIXME: We should add an optimized form of layout that just recomputes visual overflow.
+ if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get()))
+ return StyleDifferenceLayout;
+
+ if (!rareNonInheritedData->reflectionDataEquivalent(*other->rareNonInheritedData.get()))
+ return StyleDifferenceLayout;
+
+ if (rareNonInheritedData->m_multiCol.get() != other->rareNonInheritedData->m_multiCol.get() &&
+ *rareNonInheritedData->m_multiCol.get() != *other->rareNonInheritedData->m_multiCol.get())
+ return StyleDifferenceLayout;
+
+ if (rareNonInheritedData->m_transform.get() != other->rareNonInheritedData->m_transform.get() &&
+ *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) {
+#if USE(ACCELERATED_COMPOSITING)
+ changedContextSensitiveProperties |= ContextSensitivePropertyTransform;
+ // Don't return; keep looking for another change
+#else
+ return StyleDifferenceLayout;
+#endif
+ }
+
+#if !USE(ACCELERATED_COMPOSITING)
+ if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) {
+ if (rareNonInheritedData->m_transformStyle3D != other->rareNonInheritedData->m_transformStyle3D ||
+ rareNonInheritedData->m_backfaceVisibility != other->rareNonInheritedData->m_backfaceVisibility ||
+ rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective ||
+ rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX ||
+ rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY)
+ return StyleDifferenceLayout;
+ }
+#endif
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ // If regions change, trigger a relayout to re-calc regions.
+ if (rareNonInheritedData->m_dashboardRegions != other->rareNonInheritedData->m_dashboardRegions)
+ return StyleDifferenceLayout;
+#endif
+ }
+
+ if (rareInheritedData.get() != other->rareInheritedData.get()) {
+ if (rareInheritedData->highlight != other->rareInheritedData->highlight ||
+ rareInheritedData->indent != other->rareInheritedData->indent ||
+ rareInheritedData->m_effectiveZoom != other->rareInheritedData->m_effectiveZoom ||
+ rareInheritedData->textSizeAdjust != other->rareInheritedData->textSizeAdjust ||
+ rareInheritedData->wordBreak != other->rareInheritedData->wordBreak ||
+ rareInheritedData->wordWrap != other->rareInheritedData->wordWrap ||
+ rareInheritedData->nbspMode != other->rareInheritedData->nbspMode ||
+ rareInheritedData->khtmlLineBreak != other->rareInheritedData->khtmlLineBreak ||
+ rareInheritedData->textSecurity != other->rareInheritedData->textSecurity ||
+ rareInheritedData->hyphens != other->rareInheritedData->hyphens ||
+ rareInheritedData->hyphenationString != other->rareInheritedData->hyphenationString ||
+ rareInheritedData->hyphenationLocale != other->rareInheritedData->hyphenationLocale ||
+ rareInheritedData->textEmphasisMark != other->rareInheritedData->textEmphasisMark ||
+ rareInheritedData->textEmphasisPosition != other->rareInheritedData->textEmphasisPosition ||
+ rareInheritedData->textEmphasisCustomMark != other->rareInheritedData->textEmphasisCustomMark)
+ return StyleDifferenceLayout;
+
+ if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get()))
+ return StyleDifferenceLayout;
+
+ if (textStrokeWidth() != other->textStrokeWidth())
+ return StyleDifferenceLayout;
+ }
+
+ if (inherited->line_height != other->inherited->line_height ||
+ inherited->list_style_image != other->inherited->list_style_image ||
+ inherited->font != other->inherited->font ||
+ inherited->horizontal_border_spacing != other->inherited->horizontal_border_spacing ||
+ inherited->vertical_border_spacing != other->inherited->vertical_border_spacing ||
+ inherited_flags._box_direction != other->inherited_flags._box_direction ||
+ inherited_flags._visuallyOrdered != other->inherited_flags._visuallyOrdered ||
+ noninherited_flags._position != other->noninherited_flags._position ||
+ noninherited_flags._floating != other->noninherited_flags._floating ||
+ noninherited_flags._originalDisplay != other->noninherited_flags._originalDisplay)
+ return StyleDifferenceLayout;
+
+
+ if (((int)noninherited_flags._effectiveDisplay) >= TABLE) {
+ if (inherited_flags._border_collapse != other->inherited_flags._border_collapse ||
+ inherited_flags._empty_cells != other->inherited_flags._empty_cells ||
+ inherited_flags._caption_side != other->inherited_flags._caption_side ||
+ noninherited_flags._table_layout != other->noninherited_flags._table_layout)
+ return StyleDifferenceLayout;
+
+ // In the collapsing border model, 'hidden' suppresses other borders, while 'none'
+ // does not, so these style differences can be width differences.
+ if (inherited_flags._border_collapse &&
+ ((borderTopStyle() == BHIDDEN && other->borderTopStyle() == BNONE) ||
+ (borderTopStyle() == BNONE && other->borderTopStyle() == BHIDDEN) ||
+ (borderBottomStyle() == BHIDDEN && other->borderBottomStyle() == BNONE) ||
+ (borderBottomStyle() == BNONE && other->borderBottomStyle() == BHIDDEN) ||
+ (borderLeftStyle() == BHIDDEN && other->borderLeftStyle() == BNONE) ||
+ (borderLeftStyle() == BNONE && other->borderLeftStyle() == BHIDDEN) ||
+ (borderRightStyle() == BHIDDEN && other->borderRightStyle() == BNONE) ||
+ (borderRightStyle() == BNONE && other->borderRightStyle() == BHIDDEN)))
+ return StyleDifferenceLayout;
+ }
+
+ if (noninherited_flags._effectiveDisplay == LIST_ITEM) {
+ if (inherited_flags._list_style_type != other->inherited_flags._list_style_type ||
+ inherited_flags._list_style_position != other->inherited_flags._list_style_position)
+ return StyleDifferenceLayout;
+ }
+
+ if (inherited_flags._text_align != other->inherited_flags._text_align ||
+ inherited_flags._text_transform != other->inherited_flags._text_transform ||
+ inherited_flags._direction != other->inherited_flags._direction ||
+ inherited_flags._white_space != other->inherited_flags._white_space ||
+ noninherited_flags._clear != other->noninherited_flags._clear)
+ return StyleDifferenceLayout;
+
+ // Check block flow direction.
+ if (inherited_flags.m_writingMode != other->inherited_flags.m_writingMode)
+ return StyleDifferenceLayout;
+
+ // Check text combine mode.
+ if (rareNonInheritedData->m_textCombine != other->rareNonInheritedData->m_textCombine)
+ return StyleDifferenceLayout;
+
+ // Overflow returns a layout hint.
+ if (noninherited_flags._overflowX != other->noninherited_flags._overflowX ||
+ noninherited_flags._overflowY != other->noninherited_flags._overflowY)
+ return StyleDifferenceLayout;
+
+ // If our border widths change, then we need to layout. Other changes to borders
+ // only necessitate a repaint.
+ if (borderLeftWidth() != other->borderLeftWidth() ||
+ borderTopWidth() != other->borderTopWidth() ||
+ borderBottomWidth() != other->borderBottomWidth() ||
+ borderRightWidth() != other->borderRightWidth())
+ return StyleDifferenceLayout;
+
+ // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree.
+ const CounterDirectiveMap* mapA = rareNonInheritedData->m_counterDirectives.get();
+ const CounterDirectiveMap* mapB = other->rareNonInheritedData->m_counterDirectives.get();
+ if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB)))
+ return StyleDifferenceLayout;
+ if (rareNonInheritedData->m_counterIncrement != other->rareNonInheritedData->m_counterIncrement ||
+ rareNonInheritedData->m_counterReset != other->rareNonInheritedData->m_counterReset)
+ return StyleDifferenceLayout;
+
+ if ((rareNonInheritedData->opacity == 1 && other->rareNonInheritedData->opacity < 1) ||
+ (rareNonInheritedData->opacity < 1 && other->rareNonInheritedData->opacity == 1)) {
+ // FIXME: We should add an optimized form of layout that just recomputes visual overflow.
+ return StyleDifferenceLayout;
+ }
+
+ if ((visibility() == COLLAPSE) != (other->visibility() == COLLAPSE))
+ return StyleDifferenceLayout;
+
+#if ENABLE(SVG)
+ // SVGRenderStyle::diff() might have returned StyleDifferenceRepaint, eg. if fill changes.
+ // If eg. the font-size changed at the same time, we're not allowed to return StyleDifferenceRepaint,
+ // but have to return StyleDifferenceLayout, that's why this if branch comes after all branches
+ // that are relevant for SVG and might return StyleDifferenceLayout.
+ if (svgChange != StyleDifferenceEqual)
+ return svgChange;
+#endif
+
+ // Make sure these left/top/right/bottom checks stay below all layout checks and above
+ // all visible checks.
+ if (position() != StaticPosition) {
+ if (surround->offset != other->surround->offset) {
+ // Optimize for the case where a positioned layer is moving but not changing size.
+ if (position() == AbsolutePosition && positionedObjectMoved(surround->offset, other->surround->offset))
+ return StyleDifferenceLayoutPositionedMovementOnly;
+
+ // FIXME: We will need to do a bit of work in RenderObject/Box::setStyle before we
+ // can stop doing a layout when relative positioned objects move. In particular, we'll need
+ // to update scrolling positions and figure out how to do a repaint properly of the updated layer.
+ //if (other->position() == RelativePosition)
+ // return RepaintLayer;
+ //else
+ return StyleDifferenceLayout;
+ } else if (m_box->zIndex() != other->m_box->zIndex() || m_box->hasAutoZIndex() != other->m_box->hasAutoZIndex() ||
+ visual->clip != other->visual->clip || visual->hasClip != other->visual->hasClip)
+ return StyleDifferenceRepaintLayer;
+ }
+
+ if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity) {
+#if USE(ACCELERATED_COMPOSITING)
+ changedContextSensitiveProperties |= ContextSensitivePropertyOpacity;
+ // Don't return; keep looking for another change.
+#else
+ return StyleDifferenceRepaintLayer;
+#endif
+ }
+
+ if (rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask ||
+ rareNonInheritedData->m_maskBoxImage != other->rareNonInheritedData->m_maskBoxImage)
+ return StyleDifferenceRepaintLayer;
+
+ if (inherited->color != other->inherited->color ||
+ inherited_flags._visibility != other->inherited_flags._visibility ||
+ inherited_flags._text_decorations != other->inherited_flags._text_decorations ||
+ inherited_flags._force_backgrounds_to_white != other->inherited_flags._force_backgrounds_to_white ||
+ inherited_flags._insideLink != other->inherited_flags._insideLink ||
+ surround->border != other->surround->border ||
+ *m_background.get() != *other->m_background.get() ||
+ visual->textDecoration != other->visual->textDecoration ||
+ rareInheritedData->userModify != other->rareInheritedData->userModify ||
+ rareInheritedData->userSelect != other->rareInheritedData->userSelect ||
+ rareNonInheritedData->userDrag != other->rareNonInheritedData->userDrag ||
+ rareNonInheritedData->m_borderFit != other->rareNonInheritedData->m_borderFit ||
+ rareInheritedData->textFillColor != other->rareInheritedData->textFillColor ||
+ rareInheritedData->textStrokeColor != other->rareInheritedData->textStrokeColor ||
+ rareInheritedData->textEmphasisColor != other->rareInheritedData->textEmphasisColor ||
+ rareInheritedData->textEmphasisFill != other->rareInheritedData->textEmphasisFill)
+ return StyleDifferenceRepaint;
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) {
+ if (rareNonInheritedData->m_transformStyle3D != other->rareNonInheritedData->m_transformStyle3D ||
+ rareNonInheritedData->m_backfaceVisibility != other->rareNonInheritedData->m_backfaceVisibility ||
+ rareNonInheritedData->m_perspective != other->rareNonInheritedData->m_perspective ||
+ rareNonInheritedData->m_perspectiveOriginX != other->rareNonInheritedData->m_perspectiveOriginX ||
+ rareNonInheritedData->m_perspectiveOriginY != other->rareNonInheritedData->m_perspectiveOriginY)
+ return StyleDifferenceRecompositeLayer;
+ }
+#endif
+
+ // Cursors are not checked, since they will be set appropriately in response to mouse events,
+ // so they don't need to cause any repaint or layout.
+
+ // Animations don't need to be checked either. We always set the new style on the RenderObject, so we will get a chance to fire off
+ // the resulting transition properly.
+ return StyleDifferenceEqual;
+}
+
+void RenderStyle::setClip(Length top, Length right, Length bottom, Length left)
+{
+ StyleVisualData* data = visual.access();
+ data->clip.m_top = top;
+ data->clip.m_right = right;
+ data->clip.m_bottom = bottom;
+ data->clip.m_left = left;
+}
+
+void RenderStyle::addCursor(PassRefPtr<StyleImage> image, const IntPoint& hotSpot)
+{
+ if (!rareInheritedData.access()->cursorData)
+ rareInheritedData.access()->cursorData = CursorList::create();
+ rareInheritedData.access()->cursorData->append(CursorData(image, hotSpot));
+}
+
+void RenderStyle::setCursorList(PassRefPtr<CursorList> other)
+{
+ rareInheritedData.access()->cursorData = other;
+}
+
+void RenderStyle::clearCursorList()
+{
+ if (rareInheritedData->cursorData)
+ rareInheritedData.access()->cursorData = 0;
+}
+
+void RenderStyle::clearContent()
+{
+ if (rareNonInheritedData->m_content)
+ rareNonInheritedData->m_content->clear();
+}
+
+ContentData* RenderStyle::prepareToSetContent(StringImpl* string, bool add)
+{
+ OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content;
+ ContentData* lastContent = content.get();
+ while (lastContent && lastContent->next())
+ lastContent = lastContent->next();
+
+ if (string && add && lastContent && lastContent->isText()) {
+ // Augment the existing string and share the existing ContentData node.
+ String newText = lastContent->text();
+ newText.append(string);
+ lastContent->setText(newText.impl());
+ return 0;
+ }
+
+ bool reuseContent = !add;
+ OwnPtr<ContentData> newContentData;
+ if (reuseContent && content) {
+ content->clear();
+ newContentData = content.release();
+ } else
+ newContentData = adoptPtr(new ContentData);
+
+ ContentData* result = newContentData.get();
+
+ if (lastContent && !reuseContent)
+ lastContent->setNext(newContentData.release());
+ else
+ content = newContentData.release();
+
+ return result;
+}
+
+void RenderStyle::setContent(PassRefPtr<StyleImage> image, bool add)
+{
+ if (!image)
+ return;
+ prepareToSetContent(0, add)->setImage(image);
+}
+
+void RenderStyle::setContent(PassRefPtr<StringImpl> string, bool add)
+{
+ if (!string)
+ return;
+ if (ContentData* data = prepareToSetContent(string.get(), add))
+ data->setText(string);
+}
+
+void RenderStyle::setContent(PassOwnPtr<CounterContent> counter, bool add)
+{
+ if (!counter)
+ return;
+ prepareToSetContent(0, add)->setCounter(counter);
+}
+
+void RenderStyle::applyTransform(TransformationMatrix& transform, const IntSize& borderBoxSize, ApplyTransformOrigin applyOrigin) const
+{
+ // transform-origin brackets the transform with translate operations.
+ // Optimize for the case where the only transform is a translation, since the transform-origin is irrelevant
+ // in that case.
+ bool applyTransformOrigin = false;
+ unsigned s = rareNonInheritedData->m_transform->m_operations.operations().size();
+ unsigned i;
+ if (applyOrigin == IncludeTransformOrigin) {
+ for (i = 0; i < s; i++) {
+ TransformOperation::OperationType type = rareNonInheritedData->m_transform->m_operations.operations()[i]->getOperationType();
+ if (type != TransformOperation::TRANSLATE_X &&
+ type != TransformOperation::TRANSLATE_Y &&
+ type != TransformOperation::TRANSLATE &&
+ type != TransformOperation::TRANSLATE_Z &&
+ type != TransformOperation::TRANSLATE_3D
+ ) {
+ applyTransformOrigin = true;
+ break;
+ }
+ }
+ }
+
+ if (applyTransformOrigin) {
+ transform.translate3d(transformOriginX().calcFloatValue(borderBoxSize.width()), transformOriginY().calcFloatValue(borderBoxSize.height()), transformOriginZ());
+ }
+
+ for (i = 0; i < s; i++)
+ rareNonInheritedData->m_transform->m_operations.operations()[i]->apply(transform, borderBoxSize);
+
+ if (applyTransformOrigin) {
+ transform.translate3d(-transformOriginX().calcFloatValue(borderBoxSize.width()), -transformOriginY().calcFloatValue(borderBoxSize.height()), -transformOriginZ());
+ }
+}
+
+void RenderStyle::setPageScaleTransform(float scale)
+{
+ if (scale == 1)
+ return;
+ TransformOperations transform;
+ transform.operations().append(ScaleTransformOperation::create(scale, scale, ScaleTransformOperation::SCALE));
+ setTransform(transform);
+ setTransformOriginX(Length(0, Fixed));
+ setTransformOriginY(Length(0, Fixed));
+}
+
+void RenderStyle::setTextShadow(ShadowData* val, bool add)
+{
+ ASSERT(!val || (!val->spread() && val->style() == Normal));
+
+ StyleRareInheritedData* rareData = rareInheritedData.access();
+ if (!add) {
+ delete rareData->textShadow;
+ rareData->textShadow = val;
+ return;
+ }
+
+ val->setNext(rareData->textShadow);
+ rareData->textShadow = val;
+}
+
+void RenderStyle::setBoxShadow(ShadowData* shadowData, bool add)
+{
+ StyleRareNonInheritedData* rareData = rareNonInheritedData.access();
+ if (!add) {
+ rareData->m_boxShadow.set(shadowData);
+ return;
+ }
+
+ shadowData->setNext(rareData->m_boxShadow.leakPtr());
+ rareData->m_boxShadow.set(shadowData);
+}
+
+static void constrainCornerRadiiForRect(const IntRect& r, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight)
+{
+ // Constrain corner radii using CSS3 rules:
+ // http://www.w3.org/TR/css3-background/#the-border-radius
+
+ float factor = 1;
+ unsigned radiiSum;
+
+ // top
+ radiiSum = static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()); // Casts to avoid integer overflow.
+ if (radiiSum > static_cast<unsigned>(r.width()))
+ factor = min(static_cast<float>(r.width()) / radiiSum, factor);
+
+ // bottom
+ radiiSum = static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width());
+ if (radiiSum > static_cast<unsigned>(r.width()))
+ factor = min(static_cast<float>(r.width()) / radiiSum, factor);
+
+ // left
+ radiiSum = static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height());
+ if (radiiSum > static_cast<unsigned>(r.height()))
+ factor = min(static_cast<float>(r.height()) / radiiSum, factor);
+
+ // right
+ radiiSum = static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height());
+ if (radiiSum > static_cast<unsigned>(r.height()))
+ factor = min(static_cast<float>(r.height()) / radiiSum, factor);
+
+ // Scale all radii by f if necessary.
+ if (factor < 1) {
+ // If either radius on a corner becomes zero, reset both radii on that corner.
+ topLeft.scale(factor);
+ if (!topLeft.width() || !topLeft.height())
+ topLeft = IntSize();
+ topRight.scale(factor);
+ if (!topRight.width() || !topRight.height())
+ topRight = IntSize();
+ bottomLeft.scale(factor);
+ if (!bottomLeft.width() || !bottomLeft.height())
+ bottomLeft = IntSize();
+ bottomRight.scale(factor);
+ if (!bottomRight.width() || !bottomRight.height())
+ bottomRight = IntSize();
+ }
+}
+
+void RenderStyle::getBorderRadiiForRect(const IntRect& r, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight) const
+{
+ topLeft = IntSize(surround->border.topLeft().width().calcValue(r.width()), surround->border.topLeft().height().calcValue(r.height()));
+ topRight = IntSize(surround->border.topRight().width().calcValue(r.width()), surround->border.topRight().height().calcValue(r.height()));
+
+ bottomLeft = IntSize(surround->border.bottomLeft().width().calcValue(r.width()), surround->border.bottomLeft().height().calcValue(r.height()));
+ bottomRight = IntSize(surround->border.bottomRight().width().calcValue(r.width()), surround->border.bottomRight().height().calcValue(r.height()));
+
+ constrainCornerRadiiForRect(r, topLeft, topRight, bottomLeft, bottomRight);
+}
+
+void RenderStyle::getInnerBorderRadiiForRectWithBorderWidths(const IntRect& innerRect, unsigned short topWidth, unsigned short bottomWidth, unsigned short leftWidth, unsigned short rightWidth, IntSize& innerTopLeft, IntSize& innerTopRight, IntSize& innerBottomLeft, IntSize& innerBottomRight) const
+{
+ innerTopLeft = IntSize(surround->border.topLeft().width().calcValue(innerRect.width()), surround->border.topLeft().height().calcValue(innerRect.height()));
+ innerTopRight = IntSize(surround->border.topRight().width().calcValue(innerRect.width()), surround->border.topRight().height().calcValue(innerRect.height()));
+ innerBottomLeft = IntSize(surround->border.bottomLeft().width().calcValue(innerRect.width()), surround->border.bottomLeft().height().calcValue(innerRect.height()));
+ innerBottomRight = IntSize(surround->border.bottomRight().width().calcValue(innerRect.width()), surround->border.bottomRight().height().calcValue(innerRect.height()));
+
+
+ innerTopLeft.setWidth(max(0, innerTopLeft.width() - leftWidth));
+ innerTopLeft.setHeight(max(0, innerTopLeft.height() - topWidth));
+
+ innerTopRight.setWidth(max(0, innerTopRight.width() - rightWidth));
+ innerTopRight.setHeight(max(0, innerTopRight.height() - topWidth));
+
+ innerBottomLeft.setWidth(max(0, innerBottomLeft.width() - leftWidth));
+ innerBottomLeft.setHeight(max(0, innerBottomLeft.height() - bottomWidth));
+
+ innerBottomRight.setWidth(max(0, innerBottomRight.width() - rightWidth));
+ innerBottomRight.setHeight(max(0, innerBottomRight.height() - bottomWidth));
+
+ constrainCornerRadiiForRect(innerRect, innerTopLeft, innerTopRight, innerBottomLeft, innerBottomRight);
+}
+
+const CounterDirectiveMap* RenderStyle::counterDirectives() const
+{
+ return rareNonInheritedData->m_counterDirectives.get();
+}
+
+CounterDirectiveMap& RenderStyle::accessCounterDirectives()
+{
+ OwnPtr<CounterDirectiveMap>& map = rareNonInheritedData.access()->m_counterDirectives;
+ if (!map)
+ map.set(new CounterDirectiveMap);
+ return *map.get();
+}
+
+const AtomicString& RenderStyle::hyphenString() const
+{
+ ASSERT(hyphens() != HyphensNone);
+
+ const AtomicString& hyphenationString = rareInheritedData.get()->hyphenationString;
+ if (!hyphenationString.isNull())
+ return hyphenationString;
+
+ // FIXME: This should depend on locale.
+ DEFINE_STATIC_LOCAL(AtomicString, hyphenMinusString, (&hyphenMinus, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, hyphenString, (&hyphen, 1));
+ return font().primaryFontHasGlyphForCharacter(hyphen) ? hyphenString : hyphenMinusString;
+}
+
+const AtomicString& RenderStyle::textEmphasisMarkString() const
+{
+ switch (textEmphasisMark()) {
+ case TextEmphasisMarkNone:
+ return nullAtom;
+ case TextEmphasisMarkCustom:
+ return textEmphasisCustomMark();
+ case TextEmphasisMarkDot: {
+ DEFINE_STATIC_LOCAL(AtomicString, filledDotString, (&bullet, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, openDotString, (&whiteBullet, 1));
+ return textEmphasisFill() == TextEmphasisFillFilled ? filledDotString : openDotString;
+ }
+ case TextEmphasisMarkCircle: {
+ DEFINE_STATIC_LOCAL(AtomicString, filledCircleString, (&blackCircle, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, openCircleString, (&whiteCircle, 1));
+ return textEmphasisFill() == TextEmphasisFillFilled ? filledCircleString : openCircleString;
+ }
+ case TextEmphasisMarkDoubleCircle: {
+ DEFINE_STATIC_LOCAL(AtomicString, filledDoubleCircleString, (&fisheye, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, openDoubleCircleString, (&bullseye, 1));
+ return textEmphasisFill() == TextEmphasisFillFilled ? filledDoubleCircleString : openDoubleCircleString;
+ }
+ case TextEmphasisMarkTriangle: {
+ DEFINE_STATIC_LOCAL(AtomicString, filledTriangleString, (&blackUpPointingTriangle, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, openTriangleString, (&whiteUpPointingTriangle, 1));
+ return textEmphasisFill() == TextEmphasisFillFilled ? filledTriangleString : openTriangleString;
+ }
+ case TextEmphasisMarkSesame: {
+ DEFINE_STATIC_LOCAL(AtomicString, filledSesameString, (&sesameDot, 1));
+ DEFINE_STATIC_LOCAL(AtomicString, openSesameString, (&whiteSesameDot, 1));
+ return textEmphasisFill() == TextEmphasisFillFilled ? filledSesameString : openSesameString;
+ }
+ case TextEmphasisMarkAuto:
+ ASSERT_NOT_REACHED();
+ return nullAtom;
+ }
+
+ ASSERT_NOT_REACHED();
+ return nullAtom;
+}
+
+#if ENABLE(DASHBOARD_SUPPORT)
+const Vector<StyleDashboardRegion>& RenderStyle::initialDashboardRegions()
+{
+ DEFINE_STATIC_LOCAL(Vector<StyleDashboardRegion>, emptyList, ());
+ return emptyList;
+}
+
+const Vector<StyleDashboardRegion>& RenderStyle::noneDashboardRegions()
+{
+ DEFINE_STATIC_LOCAL(Vector<StyleDashboardRegion>, noneList, ());
+ static bool noneListInitialized = false;
+
+ if (!noneListInitialized) {
+ StyleDashboardRegion region;
+ region.label = "";
+ region.offset.m_top = Length();
+ region.offset.m_right = Length();
+ region.offset.m_bottom = Length();
+ region.offset.m_left = Length();
+ region.type = StyleDashboardRegion::None;
+ noneList.append(region);
+ noneListInitialized = true;
+ }
+ return noneList;
+}
+#endif
+
+void RenderStyle::adjustAnimations()
+{
+ AnimationList* animationList = rareNonInheritedData->m_animations.get();
+ if (!animationList)
+ return;
+
+ // Get rid of empty animations and anything beyond them
+ for (size_t i = 0; i < animationList->size(); ++i) {
+ if (animationList->animation(i)->isEmpty()) {
+ animationList->resize(i);
+ break;
+ }
+ }
+
+ if (animationList->isEmpty()) {
+ clearAnimations();
+ return;
+ }
+
+ // Repeat patterns into layers that don't have some properties set.
+ animationList->fillUnsetProperties();
+}
+
+void RenderStyle::adjustTransitions()
+{
+ AnimationList* transitionList = rareNonInheritedData->m_transitions.get();
+ if (!transitionList)
+ return;
+
+ // Get rid of empty transitions and anything beyond them
+ for (size_t i = 0; i < transitionList->size(); ++i) {
+ if (transitionList->animation(i)->isEmpty()) {
+ transitionList->resize(i);
+ break;
+ }
+ }
+
+ if (transitionList->isEmpty()) {
+ clearTransitions();
+ return;
+ }
+
+ // Repeat patterns into layers that don't have some properties set.
+ transitionList->fillUnsetProperties();
+
+ // Make sure there are no duplicate properties. This is an O(n^2) algorithm
+ // but the lists tend to be very short, so it is probably ok
+ for (size_t i = 0; i < transitionList->size(); ++i) {
+ for (size_t j = i+1; j < transitionList->size(); ++j) {
+ if (transitionList->animation(i)->property() == transitionList->animation(j)->property()) {
+ // toss i
+ transitionList->remove(i);
+ j = i;
+ }
+ }
+ }
+}
+
+AnimationList* RenderStyle::accessAnimations()
+{
+ if (!rareNonInheritedData.access()->m_animations)
+ rareNonInheritedData.access()->m_animations.set(new AnimationList());
+ return rareNonInheritedData->m_animations.get();
+}
+
+AnimationList* RenderStyle::accessTransitions()
+{
+ if (!rareNonInheritedData.access()->m_transitions)
+ rareNonInheritedData.access()->m_transitions.set(new AnimationList());
+ return rareNonInheritedData->m_transitions.get();
+}
+
+const Animation* RenderStyle::transitionForProperty(int property) const
+{
+ if (transitions()) {
+ for (size_t i = 0; i < transitions()->size(); ++i) {
+ const Animation* p = transitions()->animation(i);
+ if (p->property() == cAnimateAll || p->property() == property) {
+ return p;
+ }
+ }
+ }
+ return 0;
+}
+
+void RenderStyle::setBlendedFontSize(int size)
+{
+ FontSelector* currentFontSelector = font().fontSelector();
+ FontDescription desc(fontDescription());
+ desc.setSpecifiedSize(size);
+ desc.setComputedSize(size);
+ setFontDescription(desc);
+ font().update(currentFontSelector);
+}
+
+void RenderStyle::getShadowExtent(const ShadowData* shadow, int &top, int &right, int &bottom, int &left) const
+{
+ top = 0;
+ right = 0;
+ bottom = 0;
+ left = 0;
+
+ for ( ; shadow; shadow = shadow->next()) {
+ if (shadow->style() == Inset)
+ continue;
+ int blurAndSpread = shadow->blur() + shadow->spread();
+
+ top = min(top, shadow->y() - blurAndSpread);
+ right = max(right, shadow->x() + blurAndSpread);
+ bottom = max(bottom, shadow->y() + blurAndSpread);
+ left = min(left, shadow->x() - blurAndSpread);
+ }
+}
+
+void RenderStyle::getShadowHorizontalExtent(const ShadowData* shadow, int &left, int &right) const
+{
+ left = 0;
+ right = 0;
+
+ for ( ; shadow; shadow = shadow->next()) {
+ if (shadow->style() == Inset)
+ continue;
+ int blurAndSpread = shadow->blur() + shadow->spread();
+
+ left = min(left, shadow->x() - blurAndSpread);
+ right = max(right, shadow->x() + blurAndSpread);
+ }
+}
+
+void RenderStyle::getShadowVerticalExtent(const ShadowData* shadow, int &top, int &bottom) const
+{
+ top = 0;
+ bottom = 0;
+
+ for ( ; shadow; shadow = shadow->next()) {
+ if (shadow->style() == Inset)
+ continue;
+ int blurAndSpread = shadow->blur() + shadow->spread();
+
+ top = min(top, shadow->y() - blurAndSpread);
+ bottom = max(bottom, shadow->y() + blurAndSpread);
+ }
+}
+
+static EBorderStyle borderStyleForColorProperty(const RenderStyle* style, int colorProperty)
+{
+ EBorderStyle borderStyle;
+ switch (colorProperty) {
+ case CSSPropertyBorderLeftColor:
+ borderStyle = style->borderLeftStyle();
+ break;
+ case CSSPropertyBorderRightColor:
+ borderStyle = style->borderRightStyle();
+ break;
+ case CSSPropertyBorderTopColor:
+ borderStyle = style->borderTopStyle();
+ break;
+ case CSSPropertyBorderBottomColor:
+ borderStyle = style->borderBottomStyle();
+ break;
+ default:
+ borderStyle = BNONE;
+ break;
+ }
+ return borderStyle;
+}
+
+const Color RenderStyle::colorIncludingFallback(int colorProperty, EBorderStyle borderStyle) const
+{
+ Color result;
+ switch (colorProperty) {
+ case CSSPropertyBackgroundColor:
+ return backgroundColor(); // Background color doesn't fall back.
+ case CSSPropertyBorderLeftColor:
+ result = borderLeftColor();
+ borderStyle = borderLeftStyle();
+ break;
+ case CSSPropertyBorderRightColor:
+ result = borderRightColor();
+ borderStyle = borderRightStyle();
+ break;
+ case CSSPropertyBorderTopColor:
+ result = borderTopColor();
+ borderStyle = borderTopStyle();
+ break;
+ case CSSPropertyBorderBottomColor:
+ result = borderBottomColor();
+ borderStyle = borderBottomStyle();
+ break;
+ case CSSPropertyColor:
+ result = color();
+ break;
+ case CSSPropertyOutlineColor:
+ result = outlineColor();
+ break;
+ case CSSPropertyWebkitColumnRuleColor:
+ result = columnRuleColor();
+ break;
+ case CSSPropertyWebkitTextEmphasisColor:
+ result = textEmphasisColor();
+ break;
+ case CSSPropertyWebkitTextFillColor:
+ result = textFillColor();
+ break;
+ case CSSPropertyWebkitTextStrokeColor:
+ result = textStrokeColor();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+
+ if (!result.isValid()) {
+ if ((colorProperty == CSSPropertyBorderLeftColor || colorProperty == CSSPropertyBorderRightColor
+ || colorProperty == CSSPropertyBorderTopColor || colorProperty == CSSPropertyBorderBottomColor)
+ && (borderStyle == INSET || borderStyle == OUTSET || borderStyle == RIDGE || borderStyle == GROOVE))
+ result.setRGB(238, 238, 238);
+ else
+ result = color();
+ }
+
+ return result;
+}
+
+const Color RenderStyle::visitedDependentColor(int colorProperty) const
+{
+ EBorderStyle borderStyle = borderStyleForColorProperty(this, colorProperty);
+ Color unvisitedColor = colorIncludingFallback(colorProperty, borderStyle);
+ if (insideLink() != InsideVisitedLink)
+ return unvisitedColor;
+
+ RenderStyle* visitedStyle = getCachedPseudoStyle(VISITED_LINK);
+ if (!visitedStyle)
+ return unvisitedColor;
+ Color visitedColor = visitedStyle->colorIncludingFallback(colorProperty, borderStyle);
+
+ // Take the alpha from the unvisited color, but get the RGB values from the visited color.
+ return Color(visitedColor.red(), visitedColor.green(), visitedColor.blue(), unvisitedColor.alpha());
+}
+
+Length RenderStyle::logicalWidth() const
+{
+ if (isHorizontalWritingMode())
+ return width();
+ return height();
+}
+
+Length RenderStyle::logicalHeight() const
+{
+ if (isHorizontalWritingMode())
+ return height();
+ return width();
+}
+
+Length RenderStyle::logicalMinWidth() const
+{
+ if (isHorizontalWritingMode())
+ return minWidth();
+ return minHeight();
+}
+
+Length RenderStyle::logicalMaxWidth() const
+{
+ if (isHorizontalWritingMode())
+ return maxWidth();
+ return maxHeight();
+}
+
+Length RenderStyle::logicalMinHeight() const
+{
+ if (isHorizontalWritingMode())
+ return minHeight();
+ return minWidth();
+}
+
+Length RenderStyle::logicalMaxHeight() const
+{
+ if (isHorizontalWritingMode())
+ return maxHeight();
+ return maxWidth();
+}
+
+const BorderValue& RenderStyle::borderBefore() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return borderTop();
+ case BottomToTopWritingMode:
+ return borderBottom();
+ case LeftToRightWritingMode:
+ return borderLeft();
+ case RightToLeftWritingMode:
+ return borderRight();
+ }
+ ASSERT_NOT_REACHED();
+ return borderTop();
+}
+
+const BorderValue& RenderStyle::borderAfter() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return borderBottom();
+ case BottomToTopWritingMode:
+ return borderTop();
+ case LeftToRightWritingMode:
+ return borderRight();
+ case RightToLeftWritingMode:
+ return borderLeft();
+ }
+ ASSERT_NOT_REACHED();
+ return borderBottom();
+}
+
+const BorderValue& RenderStyle::borderStart() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? borderLeft() : borderRight();
+ return isLeftToRightDirection() ? borderTop() : borderBottom();
+}
+
+const BorderValue& RenderStyle::borderEnd() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? borderRight() : borderLeft();
+ return isLeftToRightDirection() ? borderBottom() : borderTop();
+}
+
+unsigned short RenderStyle::borderBeforeWidth() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return borderTopWidth();
+ case BottomToTopWritingMode:
+ return borderBottomWidth();
+ case LeftToRightWritingMode:
+ return borderLeftWidth();
+ case RightToLeftWritingMode:
+ return borderRightWidth();
+ }
+ ASSERT_NOT_REACHED();
+ return borderTopWidth();
+}
+
+unsigned short RenderStyle::borderAfterWidth() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return borderBottomWidth();
+ case BottomToTopWritingMode:
+ return borderTopWidth();
+ case LeftToRightWritingMode:
+ return borderRightWidth();
+ case RightToLeftWritingMode:
+ return borderLeftWidth();
+ }
+ ASSERT_NOT_REACHED();
+ return borderBottomWidth();
+}
+
+unsigned short RenderStyle::borderStartWidth() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? borderLeftWidth() : borderRightWidth();
+ return isLeftToRightDirection() ? borderTopWidth() : borderBottomWidth();
+}
+
+unsigned short RenderStyle::borderEndWidth() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? borderRightWidth() : borderLeftWidth();
+ return isLeftToRightDirection() ? borderBottomWidth() : borderTopWidth();
+}
+
+Length RenderStyle::marginBefore() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return marginTop();
+ case BottomToTopWritingMode:
+ return marginBottom();
+ case LeftToRightWritingMode:
+ return marginLeft();
+ case RightToLeftWritingMode:
+ return marginRight();
+ }
+ ASSERT_NOT_REACHED();
+ return marginTop();
+}
+
+Length RenderStyle::marginAfter() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return marginBottom();
+ case BottomToTopWritingMode:
+ return marginTop();
+ case LeftToRightWritingMode:
+ return marginRight();
+ case RightToLeftWritingMode:
+ return marginLeft();
+ }
+ ASSERT_NOT_REACHED();
+ return marginBottom();
+}
+
+Length RenderStyle::marginBeforeUsing(const RenderStyle* otherStyle) const
+{
+ switch (otherStyle->writingMode()) {
+ case TopToBottomWritingMode:
+ return marginTop();
+ case BottomToTopWritingMode:
+ return marginBottom();
+ case LeftToRightWritingMode:
+ return marginLeft();
+ case RightToLeftWritingMode:
+ return marginRight();
+ }
+ ASSERT_NOT_REACHED();
+ return marginTop();
+}
+
+Length RenderStyle::marginAfterUsing(const RenderStyle* otherStyle) const
+{
+ switch (otherStyle->writingMode()) {
+ case TopToBottomWritingMode:
+ return marginBottom();
+ case BottomToTopWritingMode:
+ return marginTop();
+ case LeftToRightWritingMode:
+ return marginRight();
+ case RightToLeftWritingMode:
+ return marginLeft();
+ }
+ ASSERT_NOT_REACHED();
+ return marginBottom();
+}
+
+Length RenderStyle::marginStart() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? marginLeft() : marginRight();
+ return isLeftToRightDirection() ? marginTop() : marginBottom();
+}
+
+Length RenderStyle::marginEnd() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? marginRight() : marginLeft();
+ return isLeftToRightDirection() ? marginBottom() : marginTop();
+}
+
+Length RenderStyle::marginStartUsing(const RenderStyle* otherStyle) const
+{
+ if (otherStyle->isHorizontalWritingMode())
+ return otherStyle->isLeftToRightDirection() ? marginLeft() : marginRight();
+ return otherStyle->isLeftToRightDirection() ? marginTop() : marginBottom();
+}
+
+Length RenderStyle::marginEndUsing(const RenderStyle* otherStyle) const
+{
+ if (otherStyle->isHorizontalWritingMode())
+ return otherStyle->isLeftToRightDirection() ? marginRight() : marginLeft();
+ return otherStyle->isLeftToRightDirection() ? marginBottom() : marginTop();
+}
+
+void RenderStyle::setMarginStart(Length margin)
+{
+ if (isHorizontalWritingMode()) {
+ if (isLeftToRightDirection())
+ setMarginLeft(margin);
+ else
+ setMarginRight(margin);
+ } else {
+ if (isLeftToRightDirection())
+ setMarginTop(margin);
+ else
+ setMarginBottom(margin);
+ }
+}
+
+void RenderStyle::setMarginEnd(Length margin)
+{
+ if (isHorizontalWritingMode()) {
+ if (isLeftToRightDirection())
+ setMarginRight(margin);
+ else
+ setMarginLeft(margin);
+ } else {
+ if (isLeftToRightDirection())
+ setMarginBottom(margin);
+ else
+ setMarginTop(margin);
+ }
+}
+
+Length RenderStyle::paddingBefore() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return paddingTop();
+ case BottomToTopWritingMode:
+ return paddingBottom();
+ case LeftToRightWritingMode:
+ return paddingLeft();
+ case RightToLeftWritingMode:
+ return paddingRight();
+ }
+ ASSERT_NOT_REACHED();
+ return paddingTop();
+}
+
+Length RenderStyle::paddingAfter() const
+{
+ switch (writingMode()) {
+ case TopToBottomWritingMode:
+ return paddingBottom();
+ case BottomToTopWritingMode:
+ return paddingTop();
+ case LeftToRightWritingMode:
+ return paddingRight();
+ case RightToLeftWritingMode:
+ return paddingLeft();
+ }
+ ASSERT_NOT_REACHED();
+ return paddingBottom();
+}
+
+Length RenderStyle::paddingStart() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? paddingLeft() : paddingRight();
+ return isLeftToRightDirection() ? paddingTop() : paddingBottom();
+}
+
+Length RenderStyle::paddingEnd() const
+{
+ if (isHorizontalWritingMode())
+ return isLeftToRightDirection() ? paddingRight() : paddingLeft();
+ return isLeftToRightDirection() ? paddingBottom() : paddingTop();
+}
+
+TextEmphasisMark RenderStyle::textEmphasisMark() const
+{
+ TextEmphasisMark mark = static_cast<TextEmphasisMark>(rareInheritedData->textEmphasisMark);
+ if (mark != TextEmphasisMarkAuto)
+ return mark;
+
+ if (isHorizontalWritingMode())
+ return TextEmphasisMarkDot;
+
+ return TextEmphasisMarkSesame;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h
new file mode 100644
index 0000000..7e61e46
--- /dev/null
+++ b/Source/WebCore/rendering/style/RenderStyle.h
@@ -0,0 +1,1409 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 RenderStyle_h
+#define RenderStyle_h
+
+#include "TransformationMatrix.h"
+#include "AnimationList.h"
+#include "BorderData.h"
+#include "BorderValue.h"
+#include "CSSImageGeneratorValue.h"
+#include "CSSPrimitiveValue.h"
+#include "CSSPropertyNames.h"
+#include "CSSReflectionDirection.h"
+#include "CSSValueList.h"
+#include "CollapsedBorderValue.h"
+#include "Color.h"
+#include "ColorSpace.h"
+#include "ContentData.h"
+#include "CounterDirectives.h"
+#include "CursorList.h"
+#include "DataRef.h"
+#include "FillLayer.h"
+#include "FloatPoint.h"
+#include "Font.h"
+#include "GraphicsTypes.h"
+#include "IntRect.h"
+#include "Length.h"
+#include "LengthBox.h"
+#include "LengthSize.h"
+#include "LineClampValue.h"
+#include "NinePieceImage.h"
+#include "OutlineValue.h"
+#include "Pair.h"
+#include "RenderStyleConstants.h"
+#include "ShadowData.h"
+#include "StyleBackgroundData.h"
+#include "StyleBoxData.h"
+#include "StyleFlexibleBoxData.h"
+#include "StyleInheritedData.h"
+#include "StyleMarqueeData.h"
+#include "StyleMultiColData.h"
+#include "StyleRareInheritedData.h"
+#include "StyleRareNonInheritedData.h"
+#include "StyleReflection.h"
+#include "StyleSurroundData.h"
+#include "StyleTransformData.h"
+#include "StyleVisualData.h"
+#include "TextDirection.h"
+#include "ThemeTypes.h"
+#include "TimingFunction.h"
+#include "TransformOperations.h"
+#include <wtf/Forward.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+#if ENABLE(DASHBOARD_SUPPORT)
+#include "StyleDashboardRegion.h"
+#endif
+
+#if ENABLE(SVG)
+#include "SVGRenderStyle.h"
+#endif
+
+#if COMPILER(WINSCW)
+#define compareEqual(t, u) ((t) == (u))
+#else
+template<typename T, typename U> inline bool compareEqual(const T& t, const U& u) { return t == static_cast<T>(u); }
+#endif
+
+#define SET_VAR(group, variable, value) \
+ if (!compareEqual(group->variable, value)) \
+ group.access()->variable = value;
+
+namespace WebCore {
+
+using std::max;
+
+class CSSStyleSelector;
+class CSSValueList;
+class Pair;
+class StyleImage;
+
+typedef Vector<RefPtr<RenderStyle>, 4> PseudoStyleCache;
+
+class RenderStyle: public RefCounted<RenderStyle> {
+ friend class AnimationBase; // Used by CSS animations. We can't allow them to animate based off visited colors.
+ friend class ApplyStyleCommand; // Editing has to only reveal unvisited info.
+ friend class EditingStyle; // Editing has to only reveal unvisited info.
+ friend class CSSStyleSelector; // Sets members directly.
+ friend class CSSComputedStyleDeclaration; // Ignores visited styles, so needs to be able to see unvisited info.
+ friend class PropertyWrapperMaybeInvalidColor; // Used by CSS animations. We can't allow them to animate based off visited colors.
+ friend class RenderSVGResource; // FIXME: Needs to alter the visited state by hand. Should clean the SVG code up and move it into RenderStyle perhaps.
+ friend class RenderTreeAsText; // FIXME: Only needed so the render tree can keep lying and dump the wrong colors. Rebaselining would allow this to be yanked.
+protected:
+
+ // The following bitfield is 32-bits long, which optimizes padding with the
+ // int refCount in the base class. Beware when adding more bits.
+ bool m_affectedByAttributeSelectors : 1;
+ bool m_unique : 1;
+
+ // Bits for dynamic child matching.
+ bool m_affectedByEmpty : 1;
+ bool m_emptyState : 1;
+
+ // We optimize for :first-child and :last-child. The other positional child selectors like nth-child or
+ // *-child-of-type, we will just give up and re-evaluate whenever children change at all.
+ bool m_childrenAffectedByFirstChildRules : 1;
+ bool m_childrenAffectedByLastChildRules : 1;
+ bool m_childrenAffectedByDirectAdjacentRules : 1;
+ bool m_childrenAffectedByForwardPositionalRules : 1;
+ bool m_childrenAffectedByBackwardPositionalRules : 1;
+ bool m_firstChildState : 1;
+ bool m_lastChildState : 1;
+ unsigned m_childIndex : 21; // Plenty of bits to cache an index.
+
+ // non-inherited attributes
+ DataRef<StyleBoxData> m_box;
+ DataRef<StyleVisualData> visual;
+ DataRef<StyleBackgroundData> m_background;
+ DataRef<StyleSurroundData> surround;
+ DataRef<StyleRareNonInheritedData> rareNonInheritedData;
+
+ // inherited attributes
+ DataRef<StyleRareInheritedData> rareInheritedData;
+ DataRef<StyleInheritedData> inherited;
+
+ // list of associated pseudo styles
+ OwnPtr<PseudoStyleCache> m_cachedPseudoStyles;
+
+#if ENABLE(SVG)
+ DataRef<SVGRenderStyle> m_svgStyle;
+#endif
+
+// !START SYNC!: Keep this in sync with the copy constructor in RenderStyle.cpp
+
+ // inherit
+ struct InheritedFlags {
+ bool operator==(const InheritedFlags& other) const
+ {
+ return (_empty_cells == other._empty_cells) &&
+ (_caption_side == other._caption_side) &&
+ (_list_style_type == other._list_style_type) &&
+ (_list_style_position == other._list_style_position) &&
+ (_visibility == other._visibility) &&
+ (_text_align == other._text_align) &&
+ (_text_transform == other._text_transform) &&
+ (_text_decorations == other._text_decorations) &&
+ (_text_transform == other._text_transform) &&
+ (_cursor_style == other._cursor_style) &&
+ (_direction == other._direction) &&
+ (_border_collapse == other._border_collapse) &&
+ (_white_space == other._white_space) &&
+ (_box_direction == other._box_direction) &&
+ (_visuallyOrdered == other._visuallyOrdered) &&
+ (_force_backgrounds_to_white == other._force_backgrounds_to_white) &&
+ (_pointerEvents == other._pointerEvents) &&
+ (_insideLink == other._insideLink) &&
+ (m_writingMode == other.m_writingMode);
+ }
+
+ bool operator!=(const InheritedFlags& other) const { return !(*this == other); }
+
+ unsigned _empty_cells : 1; // EEmptyCell
+ unsigned _caption_side : 2; // ECaptionSide
+ unsigned _list_style_type : 7; // EListStyleType
+ unsigned _list_style_position : 1; // EListStylePosition
+ unsigned _visibility : 2; // EVisibility
+ unsigned _text_align : 3; // ETextAlign
+ unsigned _text_transform : 2; // ETextTransform
+ unsigned _text_decorations : 4;
+ unsigned _cursor_style : 6; // ECursor
+ unsigned _direction : 1; // TextDirection
+ bool _border_collapse : 1 ;
+ unsigned _white_space : 3; // EWhiteSpace
+ unsigned _box_direction : 1; // EBoxDirection (CSS3 box_direction property, flexible box layout module)
+ // 34 bits
+
+ // non CSS2 inherited
+ bool _visuallyOrdered : 1;
+ bool _force_backgrounds_to_white : 1;
+ unsigned _pointerEvents : 4; // EPointerEvents
+ unsigned _insideLink : 2; // EInsideLink
+ // 43 bits
+
+ // CSS Text Layout Module Level 3: Vertical writing support
+ unsigned m_writingMode : 2; // WritingMode
+ // 45 bits
+ } inherited_flags;
+
+// don't inherit
+ struct NonInheritedFlags {
+ bool operator==(const NonInheritedFlags& other) const
+ {
+ return _effectiveDisplay == other._effectiveDisplay
+ && _originalDisplay == other._originalDisplay
+ && _overflowX == other._overflowX
+ && _overflowY == other._overflowY
+ && _vertical_align == other._vertical_align
+ && _clear == other._clear
+ && _position == other._position
+ && _floating == other._floating
+ && _table_layout == other._table_layout
+ && _page_break_before == other._page_break_before
+ && _page_break_after == other._page_break_after
+ && _page_break_inside == other._page_break_inside
+ && _styleType == other._styleType
+ && _affectedByHover == other._affectedByHover
+ && _affectedByActive == other._affectedByActive
+ && _affectedByDrag == other._affectedByDrag
+ && _pseudoBits == other._pseudoBits
+ && _unicodeBidi == other._unicodeBidi
+ && _isLink == other._isLink;
+ }
+
+ bool operator!=(const NonInheritedFlags& other) const { return !(*this == other); }
+
+ unsigned _effectiveDisplay : 5; // EDisplay
+ unsigned _originalDisplay : 5; // EDisplay
+ unsigned _overflowX : 3; // EOverflow
+ unsigned _overflowY : 3; // EOverflow
+ unsigned _vertical_align : 4; // EVerticalAlign
+ unsigned _clear : 2; // EClear
+ unsigned _position : 2; // EPosition
+ unsigned _floating : 2; // EFloat
+ unsigned _table_layout : 1; // ETableLayout
+
+ unsigned _page_break_before : 2; // EPageBreak
+ unsigned _page_break_after : 2; // EPageBreak
+ unsigned _page_break_inside : 2; // EPageBreak
+
+ unsigned _styleType : 6; // PseudoId
+ bool _affectedByHover : 1;
+ bool _affectedByActive : 1;
+ bool _affectedByDrag : 1;
+ unsigned _pseudoBits : 7;
+ unsigned _unicodeBidi : 2; // EUnicodeBidi
+ bool _isLink : 1;
+ // 50 bits
+ } noninherited_flags;
+
+// !END SYNC!
+
+protected:
+ void setBitDefaults()
+ {
+ inherited_flags._empty_cells = initialEmptyCells();
+ inherited_flags._caption_side = initialCaptionSide();
+ inherited_flags._list_style_type = initialListStyleType();
+ inherited_flags._list_style_position = initialListStylePosition();
+ inherited_flags._visibility = initialVisibility();
+ inherited_flags._text_align = initialTextAlign();
+ inherited_flags._text_transform = initialTextTransform();
+ inherited_flags._text_decorations = initialTextDecoration();
+ inherited_flags._cursor_style = initialCursor();
+ inherited_flags._direction = initialDirection();
+ inherited_flags._border_collapse = initialBorderCollapse();
+ inherited_flags._white_space = initialWhiteSpace();
+ inherited_flags._visuallyOrdered = initialVisuallyOrdered();
+ inherited_flags._box_direction = initialBoxDirection();
+ inherited_flags._force_backgrounds_to_white = false;
+ inherited_flags._pointerEvents = initialPointerEvents();
+ inherited_flags._insideLink = NotInsideLink;
+ inherited_flags.m_writingMode = initialWritingMode();
+
+ noninherited_flags._effectiveDisplay = noninherited_flags._originalDisplay = initialDisplay();
+ noninherited_flags._overflowX = initialOverflowX();
+ noninherited_flags._overflowY = initialOverflowY();
+ noninherited_flags._vertical_align = initialVerticalAlign();
+ noninherited_flags._clear = initialClear();
+ noninherited_flags._position = initialPosition();
+ noninherited_flags._floating = initialFloating();
+ noninherited_flags._table_layout = initialTableLayout();
+ noninherited_flags._page_break_before = initialPageBreak();
+ noninherited_flags._page_break_after = initialPageBreak();
+ noninherited_flags._page_break_inside = initialPageBreak();
+ noninherited_flags._styleType = NOPSEUDO;
+ noninherited_flags._affectedByHover = false;
+ noninherited_flags._affectedByActive = false;
+ noninherited_flags._affectedByDrag = false;
+ noninherited_flags._pseudoBits = 0;
+ noninherited_flags._unicodeBidi = initialUnicodeBidi();
+ noninherited_flags._isLink = false;
+ }
+
+private:
+ ALWAYS_INLINE RenderStyle();
+ // used to create the default style.
+ ALWAYS_INLINE RenderStyle(bool);
+ ALWAYS_INLINE RenderStyle(const RenderStyle&);
+
+public:
+ static PassRefPtr<RenderStyle> create();
+ static PassRefPtr<RenderStyle> createDefaultStyle();
+ static PassRefPtr<RenderStyle> clone(const RenderStyle*);
+
+ ~RenderStyle();
+
+ void inheritFrom(const RenderStyle* inheritParent);
+
+ PseudoId styleType() const { return static_cast<PseudoId>(noninherited_flags._styleType); }
+ void setStyleType(PseudoId styleType) { noninherited_flags._styleType = styleType; }
+
+ RenderStyle* getCachedPseudoStyle(PseudoId) const;
+ RenderStyle* addCachedPseudoStyle(PassRefPtr<RenderStyle>);
+ void removeCachedPseudoStyle(PseudoId);
+
+ const PseudoStyleCache* cachedPseudoStyles() const { return m_cachedPseudoStyles.get(); }
+
+ bool affectedByHoverRules() const { return noninherited_flags._affectedByHover; }
+ bool affectedByActiveRules() const { return noninherited_flags._affectedByActive; }
+ bool affectedByDragRules() const { return noninherited_flags._affectedByDrag; }
+
+ void setAffectedByHoverRules(bool b) { noninherited_flags._affectedByHover = b; }
+ void setAffectedByActiveRules(bool b) { noninherited_flags._affectedByActive = b; }
+ void setAffectedByDragRules(bool b) { noninherited_flags._affectedByDrag = b; }
+
+ bool operator==(const RenderStyle& other) const;
+ bool operator!=(const RenderStyle& other) const { return !(*this == other); }
+ bool isFloating() const { return !(noninherited_flags._floating == FNONE); }
+ bool hasMargin() const { return surround->margin.nonZero(); }
+ bool hasBorder() const { return surround->border.hasBorder(); }
+ bool hasPadding() const { return surround->padding.nonZero(); }
+ bool hasOffset() const { return surround->offset.nonZero(); }
+
+ bool hasBackgroundImage() const { return m_background->background().hasImage(); }
+ bool hasFixedBackgroundImage() const { return m_background->background().hasFixedImage(); }
+ bool hasAppearance() const { return appearance() != NoControlPart; }
+
+ bool hasBackground() const
+ {
+ Color color = visitedDependentColor(CSSPropertyBackgroundColor);
+ if (color.isValid() && color.alpha() > 0)
+ return true;
+ return hasBackgroundImage();
+ }
+
+ bool visuallyOrdered() const { return inherited_flags._visuallyOrdered; }
+ void setVisuallyOrdered(bool b) { inherited_flags._visuallyOrdered = b; }
+
+ bool isStyleAvailable() const;
+
+ bool hasAnyPublicPseudoStyles() const;
+ bool hasPseudoStyle(PseudoId pseudo) const;
+ void setHasPseudoStyle(PseudoId pseudo);
+
+ // attribute getter methods
+
+ EDisplay display() const { return static_cast<EDisplay>(noninherited_flags._effectiveDisplay); }
+ EDisplay originalDisplay() const { return static_cast<EDisplay>(noninherited_flags._originalDisplay); }
+
+ Length left() const { return surround->offset.left(); }
+ Length right() const { return surround->offset.right(); }
+ Length top() const { return surround->offset.top(); }
+ Length bottom() const { return surround->offset.bottom(); }
+
+ // Whether or not a positioned element requires normal flow x/y to be computed
+ // to determine its position.
+ bool hasStaticX() const { return (left().isAuto() && right().isAuto()) || left().isStatic() || right().isStatic(); }
+ bool hasStaticY() const { return (top().isAuto() && bottom().isAuto()) || top().isStatic(); }
+
+ EPosition position() const { return static_cast<EPosition>(noninherited_flags._position); }
+ EFloat floating() const { return static_cast<EFloat>(noninherited_flags._floating); }
+
+ Length width() const { return m_box->width(); }
+ Length height() const { return m_box->height(); }
+ Length minWidth() const { return m_box->minWidth(); }
+ Length maxWidth() const { return m_box->maxWidth(); }
+ Length minHeight() const { return m_box->minHeight(); }
+ Length maxHeight() const { return m_box->maxHeight(); }
+
+ Length logicalWidth() const;
+ Length logicalHeight() const;
+ Length logicalMinWidth() const;
+ Length logicalMaxWidth() const;
+ Length logicalMinHeight() const;
+ Length logicalMaxHeight() const;
+
+ const BorderData& border() const { return surround->border; }
+ const BorderValue& borderLeft() const { return surround->border.left(); }
+ const BorderValue& borderRight() const { return surround->border.right(); }
+ const BorderValue& borderTop() const { return surround->border.top(); }
+ const BorderValue& borderBottom() const { return surround->border.bottom(); }
+
+ const BorderValue& borderBefore() const;
+ const BorderValue& borderAfter() const;
+ const BorderValue& borderStart() const;
+ const BorderValue& borderEnd() const;
+
+ const NinePieceImage& borderImage() const { return surround->border.image(); }
+
+ const LengthSize& borderTopLeftRadius() const { return surround->border.topLeft(); }
+ const LengthSize& borderTopRightRadius() const { return surround->border.topRight(); }
+ const LengthSize& borderBottomLeftRadius() const { return surround->border.bottomLeft(); }
+ const LengthSize& borderBottomRightRadius() const { return surround->border.bottomRight(); }
+ bool hasBorderRadius() const { return surround->border.hasBorderRadius(); }
+
+ unsigned short borderLeftWidth() const { return surround->border.borderLeftWidth(); }
+ EBorderStyle borderLeftStyle() const { return surround->border.left().style(); }
+ bool borderLeftIsTransparent() const { return surround->border.left().isTransparent(); }
+ unsigned short borderRightWidth() const { return surround->border.borderRightWidth(); }
+ EBorderStyle borderRightStyle() const { return surround->border.right().style(); }
+ bool borderRightIsTransparent() const { return surround->border.right().isTransparent(); }
+ unsigned short borderTopWidth() const { return surround->border.borderTopWidth(); }
+ EBorderStyle borderTopStyle() const { return surround->border.top().style(); }
+ bool borderTopIsTransparent() const { return surround->border.top().isTransparent(); }
+ unsigned short borderBottomWidth() const { return surround->border.borderBottomWidth(); }
+ EBorderStyle borderBottomStyle() const { return surround->border.bottom().style(); }
+ bool borderBottomIsTransparent() const { return surround->border.bottom().isTransparent(); }
+
+ unsigned short borderBeforeWidth() const;
+ unsigned short borderAfterWidth() const;
+ unsigned short borderStartWidth() const;
+ unsigned short borderEndWidth() const;
+
+ unsigned short outlineSize() const { return max(0, outlineWidth() + outlineOffset()); }
+ unsigned short outlineWidth() const
+ {
+ if (m_background->outline().style() == BNONE)
+ return 0;
+ return m_background->outline().width();
+ }
+ bool hasOutline() const { return outlineWidth() > 0 && outlineStyle() > BHIDDEN; }
+ EBorderStyle outlineStyle() const { return m_background->outline().style(); }
+ bool outlineStyleIsAuto() const { return m_background->outline().isAuto(); }
+
+ EOverflow overflowX() const { return static_cast<EOverflow>(noninherited_flags._overflowX); }
+ EOverflow overflowY() const { return static_cast<EOverflow>(noninherited_flags._overflowY); }
+
+ EVisibility visibility() const { return static_cast<EVisibility>(inherited_flags._visibility); }
+ EVerticalAlign verticalAlign() const { return static_cast<EVerticalAlign>(noninherited_flags._vertical_align); }
+ Length verticalAlignLength() const { return m_box->verticalAlign(); }
+
+ Length clipLeft() const { return visual->clip.left(); }
+ Length clipRight() const { return visual->clip.right(); }
+ Length clipTop() const { return visual->clip.top(); }
+ Length clipBottom() const { return visual->clip.bottom(); }
+ LengthBox clip() const { return visual->clip; }
+ bool hasClip() const { return visual->hasClip; }
+
+ EUnicodeBidi unicodeBidi() const { return static_cast<EUnicodeBidi>(noninherited_flags._unicodeBidi); }
+
+ EClear clear() const { return static_cast<EClear>(noninherited_flags._clear); }
+ ETableLayout tableLayout() const { return static_cast<ETableLayout>(noninherited_flags._table_layout); }
+
+ const Font& font() const { return inherited->font; }
+ const FontDescription& fontDescription() const { return inherited->font.fontDescription(); }
+ int fontSize() const { return inherited->font.pixelSize(); }
+
+ Length textIndent() const { return rareInheritedData->indent; }
+ ETextAlign textAlign() const { return static_cast<ETextAlign>(inherited_flags._text_align); }
+ ETextTransform textTransform() const { return static_cast<ETextTransform>(inherited_flags._text_transform); }
+ int textDecorationsInEffect() const { return inherited_flags._text_decorations; }
+ int textDecoration() const { return visual->textDecoration; }
+ int wordSpacing() const { return inherited->font.wordSpacing(); }
+ int letterSpacing() const { return inherited->font.letterSpacing(); }
+
+ float zoom() const { return visual->m_zoom; }
+ float effectiveZoom() const { return rareInheritedData->m_effectiveZoom; }
+
+ TextDirection direction() const { return static_cast<TextDirection>(inherited_flags._direction); }
+ bool isLeftToRightDirection() const { return direction() == LTR; }
+
+ Length lineHeight() const { return inherited->line_height; }
+ int computedLineHeight() const
+ {
+ Length lh = lineHeight();
+
+ // Negative value means the line height is not set. Use the font's built-in spacing.
+ if (lh.isNegative())
+ return font().lineSpacing();
+
+ if (lh.isPercent())
+ return lh.calcMinValue(fontSize());
+
+ return lh.value();
+ }
+
+ EWhiteSpace whiteSpace() const { return static_cast<EWhiteSpace>(inherited_flags._white_space); }
+ static bool autoWrap(EWhiteSpace ws)
+ {
+ // Nowrap and pre don't automatically wrap.
+ return ws != NOWRAP && ws != PRE;
+ }
+
+ bool autoWrap() const
+ {
+ return autoWrap(whiteSpace());
+ }
+
+ static bool preserveNewline(EWhiteSpace ws)
+ {
+ // Normal and nowrap do not preserve newlines.
+ return ws != NORMAL && ws != NOWRAP;
+ }
+
+ bool preserveNewline() const
+ {
+ return preserveNewline(whiteSpace());
+ }
+
+ static bool collapseWhiteSpace(EWhiteSpace ws)
+ {
+ // Pre and prewrap do not collapse whitespace.
+ return ws != PRE && ws != PRE_WRAP;
+ }
+
+ bool collapseWhiteSpace() const
+ {
+ return collapseWhiteSpace(whiteSpace());
+ }
+
+ bool isCollapsibleWhiteSpace(UChar c) const
+ {
+ switch (c) {
+ case ' ':
+ case '\t':
+ return collapseWhiteSpace();
+ case '\n':
+ return !preserveNewline();
+ }
+ return false;
+ }
+
+ bool breakOnlyAfterWhiteSpace() const
+ {
+ return whiteSpace() == PRE_WRAP || khtmlLineBreak() == AFTER_WHITE_SPACE;
+ }
+
+ bool breakWords() const
+ {
+ return wordBreak() == BreakWordBreak || wordWrap() == BreakWordWrap;
+ }
+
+ StyleImage* backgroundImage() const { return m_background->background().image(); }
+ EFillRepeat backgroundRepeatX() const { return static_cast<EFillRepeat>(m_background->background().repeatX()); }
+ EFillRepeat backgroundRepeatY() const { return static_cast<EFillRepeat>(m_background->background().repeatY()); }
+ CompositeOperator backgroundComposite() const { return static_cast<CompositeOperator>(m_background->background().composite()); }
+ EFillAttachment backgroundAttachment() const { return static_cast<EFillAttachment>(m_background->background().attachment()); }
+ EFillBox backgroundClip() const { return static_cast<EFillBox>(m_background->background().clip()); }
+ EFillBox backgroundOrigin() const { return static_cast<EFillBox>(m_background->background().origin()); }
+ Length backgroundXPosition() const { return m_background->background().xPosition(); }
+ Length backgroundYPosition() const { return m_background->background().yPosition(); }
+ EFillSizeType backgroundSizeType() const { return m_background->background().sizeType(); }
+ LengthSize backgroundSizeLength() const { return m_background->background().sizeLength(); }
+ FillLayer* accessBackgroundLayers() { return &(m_background.access()->m_background); }
+ const FillLayer* backgroundLayers() const { return &(m_background->background()); }
+
+ StyleImage* maskImage() const { return rareNonInheritedData->m_mask.image(); }
+ EFillRepeat maskRepeatX() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.repeatX()); }
+ EFillRepeat maskRepeatY() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.repeatY()); }
+ CompositeOperator maskComposite() const { return static_cast<CompositeOperator>(rareNonInheritedData->m_mask.composite()); }
+ EFillAttachment maskAttachment() const { return static_cast<EFillAttachment>(rareNonInheritedData->m_mask.attachment()); }
+ EFillBox maskClip() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.clip()); }
+ EFillBox maskOrigin() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.origin()); }
+ Length maskXPosition() const { return rareNonInheritedData->m_mask.xPosition(); }
+ Length maskYPosition() const { return rareNonInheritedData->m_mask.yPosition(); }
+ EFillSizeType maskSizeType() const { return rareNonInheritedData->m_mask.sizeType(); }
+ LengthSize maskSizeLength() const { return rareNonInheritedData->m_mask.sizeLength(); }
+ FillLayer* accessMaskLayers() { return &(rareNonInheritedData.access()->m_mask); }
+ const FillLayer* maskLayers() const { return &(rareNonInheritedData->m_mask); }
+ const NinePieceImage& maskBoxImage() const { return rareNonInheritedData->m_maskBoxImage; }
+
+ // returns true for collapsing borders, false for separate borders
+ bool borderCollapse() const { return inherited_flags._border_collapse; }
+ short horizontalBorderSpacing() const { return inherited->horizontal_border_spacing; }
+ short verticalBorderSpacing() const { return inherited->vertical_border_spacing; }
+ EEmptyCell emptyCells() const { return static_cast<EEmptyCell>(inherited_flags._empty_cells); }
+ ECaptionSide captionSide() const { return static_cast<ECaptionSide>(inherited_flags._caption_side); }
+
+ short counterIncrement() const { return rareNonInheritedData->m_counterIncrement; }
+ short counterReset() const { return rareNonInheritedData->m_counterReset; }
+
+ EListStyleType listStyleType() const { return static_cast<EListStyleType>(inherited_flags._list_style_type); }
+ StyleImage* listStyleImage() const { return inherited->list_style_image.get(); }
+ EListStylePosition listStylePosition() const { return static_cast<EListStylePosition>(inherited_flags._list_style_position); }
+
+ Length marginTop() const { return surround->margin.top(); }
+ Length marginBottom() const { return surround->margin.bottom(); }
+ Length marginLeft() const { return surround->margin.left(); }
+ Length marginRight() const { return surround->margin.right(); }
+ Length marginBefore() const;
+ Length marginAfter() const;
+ Length marginStart() const;
+ Length marginEnd() const;
+ Length marginStartUsing(const RenderStyle* otherStyle) const;
+ Length marginEndUsing(const RenderStyle* otherStyle) const;
+ Length marginBeforeUsing(const RenderStyle* otherStyle) const;
+ Length marginAfterUsing(const RenderStyle* otherStyle) const;
+
+ LengthBox paddingBox() const { return surround->padding; }
+ Length paddingTop() const { return surround->padding.top(); }
+ Length paddingBottom() const { return surround->padding.bottom(); }
+ Length paddingLeft() const { return surround->padding.left(); }
+ Length paddingRight() const { return surround->padding.right(); }
+ Length paddingBefore() const;
+ Length paddingAfter() const;
+ Length paddingStart() const;
+ Length paddingEnd() const;
+
+ ECursor cursor() const { return static_cast<ECursor>(inherited_flags._cursor_style); }
+
+ CursorList* cursors() const { return rareInheritedData->cursorData.get(); }
+
+ EInsideLink insideLink() const { return static_cast<EInsideLink>(inherited_flags._insideLink); }
+ bool isLink() const { return noninherited_flags._isLink; }
+
+ short widows() const { return rareInheritedData->widows; }
+ short orphans() const { return rareInheritedData->orphans; }
+ EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(noninherited_flags._page_break_inside); }
+ EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags._page_break_before); }
+ EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags._page_break_after); }
+
+ // CSS3 Getter Methods
+
+ int outlineOffset() const
+ {
+ if (m_background->outline().style() == BNONE)
+ return 0;
+ return m_background->outline().offset();
+ }
+
+ const ShadowData* textShadow() const { return rareInheritedData->textShadow; }
+ void getTextShadowExtent(int& top, int& right, int& bottom, int& left) const { getShadowExtent(textShadow(), top, right, bottom, left); }
+ void getTextShadowHorizontalExtent(int& left, int& right) const { getShadowHorizontalExtent(textShadow(), left, right); }
+ void getTextShadowVerticalExtent(int& top, int& bottom) const { getShadowVerticalExtent(textShadow(), top, bottom); }
+ void getTextShadowInlineDirectionExtent(int& logicalLeft, int& logicalRight) { getShadowInlineDirectionExtent(textShadow(), logicalLeft, logicalRight); }
+ void getTextShadowBlockDirectionExtent(int& logicalTop, int& logicalBottom) { getShadowBlockDirectionExtent(textShadow(), logicalTop, logicalBottom); }
+
+ float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; }
+ ColorSpace colorSpace() const { return static_cast<ColorSpace>(rareInheritedData->colorSpace); }
+ float opacity() const { return rareNonInheritedData->opacity; }
+ ControlPart appearance() const { return static_cast<ControlPart>(rareNonInheritedData->m_appearance); }
+ EBoxAlignment boxAlign() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->align); }
+ EBoxDirection boxDirection() const { return static_cast<EBoxDirection>(inherited_flags._box_direction); }
+ float boxFlex() { return rareNonInheritedData->flexibleBox->flex; }
+ unsigned int boxFlexGroup() const { return rareNonInheritedData->flexibleBox->flex_group; }
+ EBoxLines boxLines() { return static_cast<EBoxLines>(rareNonInheritedData->flexibleBox->lines); }
+ unsigned int boxOrdinalGroup() const { return rareNonInheritedData->flexibleBox->ordinal_group; }
+ EBoxOrient boxOrient() const { return static_cast<EBoxOrient>(rareNonInheritedData->flexibleBox->orient); }
+ EBoxAlignment boxPack() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->pack); }
+
+ const ShadowData* boxShadow() const { return rareNonInheritedData->m_boxShadow.get(); }
+ void getBoxShadowExtent(int& top, int& right, int& bottom, int& left) const { getShadowExtent(boxShadow(), top, right, bottom, left); }
+ void getBoxShadowHorizontalExtent(int& left, int& right) const { getShadowHorizontalExtent(boxShadow(), left, right); }
+ void getBoxShadowVerticalExtent(int& top, int& bottom) const { getShadowVerticalExtent(boxShadow(), top, bottom); }
+ void getBoxShadowInlineDirectionExtent(int& logicalLeft, int& logicalRight) { getShadowInlineDirectionExtent(boxShadow(), logicalLeft, logicalRight); }
+ void getBoxShadowBlockDirectionExtent(int& logicalTop, int& logicalBottom) { getShadowBlockDirectionExtent(boxShadow(), logicalTop, logicalBottom); }
+
+ StyleReflection* boxReflect() const { return rareNonInheritedData->m_boxReflect.get(); }
+ EBoxSizing boxSizing() const { return m_box->boxSizing(); }
+ Length marqueeIncrement() const { return rareNonInheritedData->marquee->increment; }
+ int marqueeSpeed() const { return rareNonInheritedData->marquee->speed; }
+ int marqueeLoopCount() const { return rareNonInheritedData->marquee->loops; }
+ EMarqueeBehavior marqueeBehavior() const { return static_cast<EMarqueeBehavior>(rareNonInheritedData->marquee->behavior); }
+ EMarqueeDirection marqueeDirection() const { return static_cast<EMarqueeDirection>(rareNonInheritedData->marquee->direction); }
+ EUserModify userModify() const { return static_cast<EUserModify>(rareInheritedData->userModify); }
+ EUserDrag userDrag() const { return static_cast<EUserDrag>(rareNonInheritedData->userDrag); }
+ EUserSelect userSelect() const { return static_cast<EUserSelect>(rareInheritedData->userSelect); }
+ bool textOverflow() const { return rareNonInheritedData->textOverflow; }
+ EMarginCollapse marginBeforeCollapse() const { return static_cast<EMarginCollapse>(rareNonInheritedData->marginBeforeCollapse); }
+ EMarginCollapse marginAfterCollapse() const { return static_cast<EMarginCollapse>(rareNonInheritedData->marginAfterCollapse); }
+ EWordBreak wordBreak() const { return static_cast<EWordBreak>(rareInheritedData->wordBreak); }
+ EWordWrap wordWrap() const { return static_cast<EWordWrap>(rareInheritedData->wordWrap); }
+ ENBSPMode nbspMode() const { return static_cast<ENBSPMode>(rareInheritedData->nbspMode); }
+ EKHTMLLineBreak khtmlLineBreak() const { return static_cast<EKHTMLLineBreak>(rareInheritedData->khtmlLineBreak); }
+ EMatchNearestMailBlockquoteColor matchNearestMailBlockquoteColor() const { return static_cast<EMatchNearestMailBlockquoteColor>(rareNonInheritedData->matchNearestMailBlockquoteColor); }
+ const AtomicString& highlight() const { return rareInheritedData->highlight; }
+ Hyphens hyphens() const { return static_cast<Hyphens>(rareInheritedData->hyphens); }
+ const AtomicString& hyphenationString() const { return rareInheritedData->hyphenationString; }
+ const AtomicString& hyphenationLocale() const { return rareInheritedData->hyphenationLocale; }
+ EBorderFit borderFit() const { return static_cast<EBorderFit>(rareNonInheritedData->m_borderFit); }
+ EResize resize() const { return static_cast<EResize>(rareInheritedData->resize); }
+ float columnWidth() const { return rareNonInheritedData->m_multiCol->m_width; }
+ bool hasAutoColumnWidth() const { return rareNonInheritedData->m_multiCol->m_autoWidth; }
+ unsigned short columnCount() const { return rareNonInheritedData->m_multiCol->m_count; }
+ bool hasAutoColumnCount() const { return rareNonInheritedData->m_multiCol->m_autoCount; }
+ bool specifiesColumns() const { return !hasAutoColumnCount() || !hasAutoColumnWidth(); }
+ float columnGap() const { return rareNonInheritedData->m_multiCol->m_gap; }
+ bool hasNormalColumnGap() const { return rareNonInheritedData->m_multiCol->m_normalGap; }
+ EBorderStyle columnRuleStyle() const { return rareNonInheritedData->m_multiCol->m_rule.style(); }
+ unsigned short columnRuleWidth() const { return rareNonInheritedData->m_multiCol->ruleWidth(); }
+ bool columnRuleIsTransparent() const { return rareNonInheritedData->m_multiCol->m_rule.isTransparent(); }
+ bool columnSpan() const { return rareNonInheritedData->m_multiCol->m_columnSpan; }
+ EPageBreak columnBreakBefore() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakBefore); }
+ EPageBreak columnBreakInside() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakInside); }
+ EPageBreak columnBreakAfter() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakAfter); }
+ const TransformOperations& transform() const { return rareNonInheritedData->m_transform->m_operations; }
+ Length transformOriginX() const { return rareNonInheritedData->m_transform->m_x; }
+ Length transformOriginY() const { return rareNonInheritedData->m_transform->m_y; }
+ float transformOriginZ() const { return rareNonInheritedData->m_transform->m_z; }
+ bool hasTransform() const { return !rareNonInheritedData->m_transform->m_operations.operations().isEmpty(); }
+
+ TextEmphasisFill textEmphasisFill() const { return static_cast<TextEmphasisFill>(rareInheritedData->textEmphasisFill); }
+ TextEmphasisMark textEmphasisMark() const;
+ const AtomicString& textEmphasisCustomMark() const { return rareInheritedData->textEmphasisCustomMark; }
+ TextEmphasisPosition textEmphasisPosition() const { return static_cast<TextEmphasisPosition>(rareInheritedData->textEmphasisPosition); }
+ const AtomicString& textEmphasisMarkString() const;
+
+ // Return true if any transform related property (currently transform, transformStyle3D or perspective)
+ // indicates that we are transforming
+ bool hasTransformRelatedProperty() const { return hasTransform() || preserves3D() || hasPerspective(); }
+
+ enum ApplyTransformOrigin { IncludeTransformOrigin, ExcludeTransformOrigin };
+ void applyTransform(TransformationMatrix&, const IntSize& borderBoxSize, ApplyTransformOrigin = IncludeTransformOrigin) const;
+ void setPageScaleTransform(float);
+
+ bool hasMask() const { return rareNonInheritedData->m_mask.hasImage() || rareNonInheritedData->m_maskBoxImage.hasImage(); }
+
+ TextCombine textCombine() const { return static_cast<TextCombine>(rareNonInheritedData->m_textCombine); }
+ // End CSS3 Getters
+
+ // Apple-specific property getter methods
+ EPointerEvents pointerEvents() const { return static_cast<EPointerEvents>(inherited_flags._pointerEvents); }
+ const AnimationList* animations() const { return rareNonInheritedData->m_animations.get(); }
+ const AnimationList* transitions() const { return rareNonInheritedData->m_transitions.get(); }
+
+ AnimationList* accessAnimations();
+ AnimationList* accessTransitions();
+
+ bool hasAnimations() const { return rareNonInheritedData->m_animations && rareNonInheritedData->m_animations->size() > 0; }
+ bool hasTransitions() const { return rareNonInheritedData->m_transitions && rareNonInheritedData->m_transitions->size() > 0; }
+
+ // return the first found Animation (including 'all' transitions)
+ const Animation* transitionForProperty(int property) const;
+
+ ETransformStyle3D transformStyle3D() const { return rareNonInheritedData->m_transformStyle3D; }
+ bool preserves3D() const { return rareNonInheritedData->m_transformStyle3D == TransformStyle3DPreserve3D; }
+
+ EBackfaceVisibility backfaceVisibility() const { return rareNonInheritedData->m_backfaceVisibility; }
+ float perspective() const { return rareNonInheritedData->m_perspective; }
+ bool hasPerspective() const { return rareNonInheritedData->m_perspective > 0; }
+ Length perspectiveOriginX() const { return rareNonInheritedData->m_perspectiveOriginX; }
+ Length perspectiveOriginY() const { return rareNonInheritedData->m_perspectiveOriginY; }
+ LengthSize pageSize() const { return rareNonInheritedData->m_pageSize; }
+ PageSizeType pageSizeType() const { return rareNonInheritedData->m_pageSizeType; }
+
+#if USE(ACCELERATED_COMPOSITING)
+ // When set, this ensures that styles compare as different. Used during accelerated animations.
+ bool isRunningAcceleratedAnimation() const { return rareNonInheritedData->m_runningAcceleratedAnimation; }
+#endif
+
+ const LineClampValue& lineClamp() const { return rareNonInheritedData->lineClamp; }
+ bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; }
+ ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); }
+
+ WritingMode writingMode() const { return static_cast<WritingMode>(inherited_flags.m_writingMode); }
+ bool isHorizontalWritingMode() const { return writingMode() == TopToBottomWritingMode || writingMode() == BottomToTopWritingMode; }
+ bool isFlippedLinesWritingMode() const { return writingMode() == LeftToRightWritingMode || writingMode() == BottomToTopWritingMode; }
+ bool isFlippedBlocksWritingMode() const { return writingMode() == RightToLeftWritingMode || writingMode() == BottomToTopWritingMode; }
+
+ ESpeak speak() { return static_cast<ESpeak>(rareInheritedData->speak); }
+
+#ifdef ANDROID_CSS_RING
+ // called when building nav cache to determine if the ring data is unchanged
+ const void* ringData() const { return reinterpret_cast<const void*>(rareInheritedData.get()); }
+ Color ringFillColor() const { return rareInheritedData->ringFillColor; }
+ Length ringInnerWidth() const { return rareInheritedData->ringInnerWidth; }
+ Length ringOuterWidth() const { return rareInheritedData->ringOuterWidth; }
+ Length ringOutset() const { return rareInheritedData->ringOutset; }
+ Color ringPressedInnerColor() const { return rareInheritedData->ringPressedInnerColor; }
+ Color ringPressedOuterColor() const { return rareInheritedData->ringPressedOuterColor; }
+ Length ringRadius() const { return rareInheritedData->ringRadius; }
+ Color ringSelectedInnerColor() const { return rareInheritedData->ringSelectedInnerColor; }
+ Color ringSelectedOuterColor() const { return rareInheritedData->ringSelectedOuterColor; }
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ Color tapHighlightColor() const { return rareInheritedData->tapHighlightColor; }
+#endif
+// attribute setter methods
+
+ void setDisplay(EDisplay v) { noninherited_flags._effectiveDisplay = v; }
+ void setOriginalDisplay(EDisplay v) { noninherited_flags._originalDisplay = v; }
+ void setPosition(EPosition v) { noninherited_flags._position = v; }
+ void setFloating(EFloat v) { noninherited_flags._floating = v; }
+
+ void setLeft(Length v) { SET_VAR(surround, offset.m_left, v) }
+ void setRight(Length v) { SET_VAR(surround, offset.m_right, v) }
+ void setTop(Length v) { SET_VAR(surround, offset.m_top, v) }
+ void setBottom(Length v) { SET_VAR(surround, offset.m_bottom, v) }
+
+ void setWidth(Length v) { SET_VAR(m_box, m_width, v) }
+ void setHeight(Length v) { SET_VAR(m_box, m_height, v) }
+
+ void setMinWidth(Length v) { SET_VAR(m_box, m_minWidth, v) }
+ void setMaxWidth(Length v) { SET_VAR(m_box, m_maxWidth, v) }
+ void setMinHeight(Length v) { SET_VAR(m_box, m_minHeight, v) }
+ void setMaxHeight(Length v) { SET_VAR(m_box, m_maxHeight, v) }
+
+#if ENABLE(DASHBOARD_SUPPORT)
+ Vector<StyleDashboardRegion> dashboardRegions() const { return rareNonInheritedData->m_dashboardRegions; }
+ void setDashboardRegions(Vector<StyleDashboardRegion> regions) { SET_VAR(rareNonInheritedData, m_dashboardRegions, regions); }
+
+ void setDashboardRegion(int type, const String& label, Length t, Length r, Length b, Length l, bool append)
+ {
+ StyleDashboardRegion region;
+ region.label = label;
+ region.offset.m_top = t;
+ region.offset.m_right = r;
+ region.offset.m_bottom = b;
+ region.offset.m_left = l;
+ region.type = type;
+ if (!append)
+ rareNonInheritedData.access()->m_dashboardRegions.clear();
+ rareNonInheritedData.access()->m_dashboardRegions.append(region);
+ }
+#endif
+
+ void resetBorder() { resetBorderImage(); resetBorderTop(); resetBorderRight(); resetBorderBottom(); resetBorderLeft(); resetBorderRadius(); }
+ void resetBorderTop() { SET_VAR(surround, border.m_top, BorderValue()) }
+ void resetBorderRight() { SET_VAR(surround, border.m_right, BorderValue()) }
+ void resetBorderBottom() { SET_VAR(surround, border.m_bottom, BorderValue()) }
+ void resetBorderLeft() { SET_VAR(surround, border.m_left, BorderValue()) }
+ void resetBorderImage() { SET_VAR(surround, border.m_image, NinePieceImage()) }
+ void resetBorderRadius() { resetBorderTopLeftRadius(); resetBorderTopRightRadius(); resetBorderBottomLeftRadius(); resetBorderBottomRightRadius(); }
+ void resetBorderTopLeftRadius() { SET_VAR(surround, border.m_topLeft, initialBorderRadius()) }
+ void resetBorderTopRightRadius() { SET_VAR(surround, border.m_topRight, initialBorderRadius()) }
+ void resetBorderBottomLeftRadius() { SET_VAR(surround, border.m_bottomLeft, initialBorderRadius()) }
+ void resetBorderBottomRightRadius() { SET_VAR(surround, border.m_bottomRight, initialBorderRadius()) }
+
+ void resetOutline() { SET_VAR(m_background, m_outline, OutlineValue()) }
+
+ void setBackgroundColor(const Color& v) { SET_VAR(m_background, m_color, v) }
+
+ void setBackgroundXPosition(Length l) { SET_VAR(m_background, m_background.m_xPosition, l) }
+ void setBackgroundYPosition(Length l) { SET_VAR(m_background, m_background.m_yPosition, l) }
+ void setBackgroundSize(EFillSizeType b) { SET_VAR(m_background, m_background.m_sizeType, b) }
+ void setBackgroundSizeLength(LengthSize l) { SET_VAR(m_background, m_background.m_sizeLength, l) }
+
+ void setBorderImage(const NinePieceImage& b) { SET_VAR(surround, border.m_image, b) }
+
+ void setBorderTopLeftRadius(const LengthSize& s) { SET_VAR(surround, border.m_topLeft, s) }
+ void setBorderTopRightRadius(const LengthSize& s) { SET_VAR(surround, border.m_topRight, s) }
+ void setBorderBottomLeftRadius(const LengthSize& s) { SET_VAR(surround, border.m_bottomLeft, s) }
+ void setBorderBottomRightRadius(const LengthSize& s) { SET_VAR(surround, border.m_bottomRight, s) }
+
+ void setBorderRadius(const LengthSize& s)
+ {
+ setBorderTopLeftRadius(s);
+ setBorderTopRightRadius(s);
+ setBorderBottomLeftRadius(s);
+ setBorderBottomRightRadius(s);
+ }
+ void setBorderRadius(const IntSize& s)
+ {
+ setBorderRadius(LengthSize(Length(s.width(), Fixed), Length(s.height(), Fixed)));
+ }
+
+
+ void getBorderRadiiForRect(const IntRect&, IntSize& topLeft, IntSize& topRight, IntSize& bottomLeft, IntSize& bottomRight) const;
+ void getInnerBorderRadiiForRectWithBorderWidths(const IntRect&, unsigned short topWidth,
+ unsigned short bottomWidth, unsigned short leftWidth, unsigned short rightWidth,
+ IntSize& innerTopLeft, IntSize& innerTopRight, IntSize& innerBottomLeft,
+ IntSize& innerBottomRight) const;
+
+ void setBorderLeftWidth(unsigned short v) { SET_VAR(surround, border.m_left.m_width, v) }
+ void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround, border.m_left.m_style, v) }
+ void setBorderLeftColor(const Color& v) { SET_VAR(surround, border.m_left.m_color, v) }
+ void setBorderRightWidth(unsigned short v) { SET_VAR(surround, border.m_right.m_width, v) }
+ void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround, border.m_right.m_style, v) }
+ void setBorderRightColor(const Color& v) { SET_VAR(surround, border.m_right.m_color, v) }
+ void setBorderTopWidth(unsigned short v) { SET_VAR(surround, border.m_top.m_width, v) }
+ void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround, border.m_top.m_style, v) }
+ void setBorderTopColor(const Color& v) { SET_VAR(surround, border.m_top.m_color, v) }
+ void setBorderBottomWidth(unsigned short v) { SET_VAR(surround, border.m_bottom.m_width, v) }
+ void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround, border.m_bottom.m_style, v) }
+ void setBorderBottomColor(const Color& v) { SET_VAR(surround, border.m_bottom.m_color, v) }
+ void setOutlineWidth(unsigned short v) { SET_VAR(m_background, m_outline.m_width, v) }
+
+ void setOutlineStyle(EBorderStyle v, bool isAuto = false)
+ {
+ SET_VAR(m_background, m_outline.m_style, v)
+ SET_VAR(m_background, m_outline.m_isAuto, isAuto)
+ }
+
+ void setOutlineColor(const Color& v) { SET_VAR(m_background, m_outline.m_color, v) }
+
+ void setOverflowX(EOverflow v) { noninherited_flags._overflowX = v; }
+ void setOverflowY(EOverflow v) { noninherited_flags._overflowY = v; }
+ void setVisibility(EVisibility v) { inherited_flags._visibility = v; }
+ void setVerticalAlign(EVerticalAlign v) { noninherited_flags._vertical_align = v; }
+ void setVerticalAlignLength(Length l) { SET_VAR(m_box, m_verticalAlign, l) }
+
+ void setHasClip(bool b = true) { SET_VAR(visual, hasClip, b) }
+ void setClipLeft(Length v) { SET_VAR(visual, clip.m_left, v) }
+ void setClipRight(Length v) { SET_VAR(visual, clip.m_right, v) }
+ void setClipTop(Length v) { SET_VAR(visual, clip.m_top, v) }
+ void setClipBottom(Length v) { SET_VAR(visual, clip.m_bottom, v) }
+ void setClip(Length top, Length right, Length bottom, Length left);
+ void setClip(LengthBox box) { SET_VAR(visual, clip, box) }
+
+ void setUnicodeBidi(EUnicodeBidi b) { noninherited_flags._unicodeBidi = b; }
+
+ void setClear(EClear v) { noninherited_flags._clear = v; }
+ void setTableLayout(ETableLayout v) { noninherited_flags._table_layout = v; }
+
+ bool setFontDescription(const FontDescription& v)
+ {
+ if (inherited->font.fontDescription() != v) {
+ inherited.access()->font = Font(v, inherited->font.letterSpacing(), inherited->font.wordSpacing());
+ return true;
+ }
+ return false;
+ }
+
+ // Only used for blending font sizes when animating.
+ void setBlendedFontSize(int);
+
+ void setColor(const Color& v) { SET_VAR(inherited, color, v) }
+ void setTextIndent(Length v) { SET_VAR(rareInheritedData, indent, v) }
+ void setTextAlign(ETextAlign v) { inherited_flags._text_align = v; }
+ void setTextTransform(ETextTransform v) { inherited_flags._text_transform = v; }
+ void addToTextDecorationsInEffect(int v) { inherited_flags._text_decorations |= v; }
+ void setTextDecorationsInEffect(int v) { inherited_flags._text_decorations = v; }
+ void setTextDecoration(int v) { SET_VAR(visual, textDecoration, v); }
+ void setDirection(TextDirection v) { inherited_flags._direction = v; }
+ void setLineHeight(Length v) { SET_VAR(inherited, line_height, v) }
+ void setZoom(float f) { SET_VAR(visual, m_zoom, f); setEffectiveZoom(effectiveZoom() * zoom()); }
+ void setEffectiveZoom(float f) { SET_VAR(rareInheritedData, m_effectiveZoom, f) }
+
+ void setWhiteSpace(EWhiteSpace v) { inherited_flags._white_space = v; }
+
+ void setWordSpacing(int v) { inherited.access()->font.setWordSpacing(v); }
+ void setLetterSpacing(int v) { inherited.access()->font.setLetterSpacing(v); }
+
+ void clearBackgroundLayers() { m_background.access()->m_background = FillLayer(BackgroundFillLayer); }
+ void inheritBackgroundLayers(const FillLayer& parent) { m_background.access()->m_background = parent; }
+
+ void adjustBackgroundLayers()
+ {
+ if (backgroundLayers()->next()) {
+ accessBackgroundLayers()->cullEmptyLayers();
+ accessBackgroundLayers()->fillUnsetProperties();
+ }
+ }
+
+ void clearMaskLayers() { rareNonInheritedData.access()->m_mask = FillLayer(MaskFillLayer); }
+ void inheritMaskLayers(const FillLayer& parent) { rareNonInheritedData.access()->m_mask = parent; }
+
+ void adjustMaskLayers()
+ {
+ if (maskLayers()->next()) {
+ accessMaskLayers()->cullEmptyLayers();
+ accessMaskLayers()->fillUnsetProperties();
+ }
+ }
+
+ void setMaskBoxImage(const NinePieceImage& b) { SET_VAR(rareNonInheritedData, m_maskBoxImage, b) }
+ void setMaskXPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_xPosition, l) }
+ void setMaskYPosition(Length l) { SET_VAR(rareNonInheritedData, m_mask.m_yPosition, l) }
+ void setMaskSize(LengthSize l) { SET_VAR(rareNonInheritedData, m_mask.m_sizeLength, l) }
+
+ void setBorderCollapse(bool collapse) { inherited_flags._border_collapse = collapse; }
+ void setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v) }
+ void setVerticalBorderSpacing(short v) { SET_VAR(inherited, vertical_border_spacing, v) }
+ void setEmptyCells(EEmptyCell v) { inherited_flags._empty_cells = v; }
+ void setCaptionSide(ECaptionSide v) { inherited_flags._caption_side = v; }
+
+ void setCounterIncrement(short v) { SET_VAR(rareNonInheritedData, m_counterIncrement, v) }
+ void setCounterReset(short v) { SET_VAR(rareNonInheritedData, m_counterReset, v) }
+
+ void setListStyleType(EListStyleType v) { inherited_flags._list_style_type = v; }
+ void setListStyleImage(PassRefPtr<StyleImage> v) { if (inherited->list_style_image != v) inherited.access()->list_style_image = v; }
+ void setListStylePosition(EListStylePosition v) { inherited_flags._list_style_position = v; }
+
+ void resetMargin() { SET_VAR(surround, margin, LengthBox(Fixed)) }
+ void setMarginTop(Length v) { SET_VAR(surround, margin.m_top, v) }
+ void setMarginBottom(Length v) { SET_VAR(surround, margin.m_bottom, v) }
+ void setMarginLeft(Length v) { SET_VAR(surround, margin.m_left, v) }
+ void setMarginRight(Length v) { SET_VAR(surround, margin.m_right, v) }
+ void setMarginStart(Length);
+ void setMarginEnd(Length);
+
+ void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)) }
+ void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b) }
+ void setPaddingTop(Length v) { SET_VAR(surround, padding.m_top, v) }
+ void setPaddingBottom(Length v) { SET_VAR(surround, padding.m_bottom, v) }
+ void setPaddingLeft(Length v) { SET_VAR(surround, padding.m_left, v) }
+ void setPaddingRight(Length v) { SET_VAR(surround, padding.m_right, v) }
+
+ void setCursor(ECursor c) { inherited_flags._cursor_style = c; }
+ void addCursor(PassRefPtr<StyleImage>, const IntPoint& hotSpot = IntPoint());
+ void setCursorList(PassRefPtr<CursorList>);
+ void clearCursorList();
+
+ void setInsideLink(EInsideLink insideLink) { inherited_flags._insideLink = insideLink; }
+ void setIsLink(bool b) { noninherited_flags._isLink = b; }
+
+ bool forceBackgroundsToWhite() const { return inherited_flags._force_backgrounds_to_white; }
+ void setForceBackgroundsToWhite(bool b=true) { inherited_flags._force_backgrounds_to_white = b; }
+
+ bool hasAutoZIndex() const { return m_box->hasAutoZIndex(); }
+ void setHasAutoZIndex() { SET_VAR(m_box, m_hasAutoZIndex, true); SET_VAR(m_box, m_zIndex, 0) }
+ int zIndex() const { return m_box->zIndex(); }
+ void setZIndex(int v) { SET_VAR(m_box, m_hasAutoZIndex, false); SET_VAR(m_box, m_zIndex, v) }
+
+ void setWidows(short w) { SET_VAR(rareInheritedData, widows, w); }
+ void setOrphans(short o) { SET_VAR(rareInheritedData, orphans, o); }
+ void setPageBreakInside(EPageBreak b) { noninherited_flags._page_break_inside = b; }
+ void setPageBreakBefore(EPageBreak b) { noninherited_flags._page_break_before = b; }
+ void setPageBreakAfter(EPageBreak b) { noninherited_flags._page_break_after = b; }
+
+ // CSS3 Setters
+ void setOutlineOffset(int v) { SET_VAR(m_background, m_outline.m_offset, v) }
+ void setTextShadow(ShadowData* val, bool add=false);
+ void setTextStrokeColor(const Color& c) { SET_VAR(rareInheritedData, textStrokeColor, c) }
+ void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w) }
+ void setTextFillColor(const Color& c) { SET_VAR(rareInheritedData, textFillColor, c) }
+ void setColorSpace(ColorSpace space) { SET_VAR(rareInheritedData, colorSpace, space) }
+ void setOpacity(float f) { SET_VAR(rareNonInheritedData, opacity, f); }
+ void setAppearance(ControlPart a) { SET_VAR(rareNonInheritedData, m_appearance, a); }
+ void setBoxAlign(EBoxAlignment a) { SET_VAR(rareNonInheritedData.access()->flexibleBox, align, a); }
+ void setBoxDirection(EBoxDirection d) { inherited_flags._box_direction = d; }
+ void setBoxFlex(float f) { SET_VAR(rareNonInheritedData.access()->flexibleBox, flex, f); }
+ void setBoxFlexGroup(unsigned int fg) { SET_VAR(rareNonInheritedData.access()->flexibleBox, flex_group, fg); }
+ void setBoxLines(EBoxLines l) { SET_VAR(rareNonInheritedData.access()->flexibleBox, lines, l); }
+ void setBoxOrdinalGroup(unsigned int og) { SET_VAR(rareNonInheritedData.access()->flexibleBox, ordinal_group, og); }
+ void setBoxOrient(EBoxOrient o) { SET_VAR(rareNonInheritedData.access()->flexibleBox, orient, o); }
+ void setBoxPack(EBoxAlignment p) { SET_VAR(rareNonInheritedData.access()->flexibleBox, pack, p); }
+ void setBoxShadow(ShadowData* val, bool add=false);
+ void setBoxReflect(PassRefPtr<StyleReflection> reflect) { if (rareNonInheritedData->m_boxReflect != reflect) rareNonInheritedData.access()->m_boxReflect = reflect; }
+ void setBoxSizing(EBoxSizing s) { SET_VAR(m_box, m_boxSizing, s); }
+ void setMarqueeIncrement(const Length& f) { SET_VAR(rareNonInheritedData.access()->marquee, increment, f); }
+ void setMarqueeSpeed(int f) { SET_VAR(rareNonInheritedData.access()->marquee, speed, f); }
+ void setMarqueeDirection(EMarqueeDirection d) { SET_VAR(rareNonInheritedData.access()->marquee, direction, d); }
+ void setMarqueeBehavior(EMarqueeBehavior b) { SET_VAR(rareNonInheritedData.access()->marquee, behavior, b); }
+ void setMarqueeLoopCount(int i) { SET_VAR(rareNonInheritedData.access()->marquee, loops, i); }
+ void setUserModify(EUserModify u) { SET_VAR(rareInheritedData, userModify, u); }
+ void setUserDrag(EUserDrag d) { SET_VAR(rareNonInheritedData, userDrag, d); }
+ void setUserSelect(EUserSelect s) { SET_VAR(rareInheritedData, userSelect, s); }
+ void setTextOverflow(bool b) { SET_VAR(rareNonInheritedData, textOverflow, b); }
+ void setMarginBeforeCollapse(EMarginCollapse c) { SET_VAR(rareNonInheritedData, marginBeforeCollapse, c); }
+ void setMarginAfterCollapse(EMarginCollapse c) { SET_VAR(rareNonInheritedData, marginAfterCollapse, c); }
+ void setWordBreak(EWordBreak b) { SET_VAR(rareInheritedData, wordBreak, b); }
+ void setWordWrap(EWordWrap b) { SET_VAR(rareInheritedData, wordWrap, b); }
+ void setNBSPMode(ENBSPMode b) { SET_VAR(rareInheritedData, nbspMode, b); }
+ void setKHTMLLineBreak(EKHTMLLineBreak b) { SET_VAR(rareInheritedData, khtmlLineBreak, b); }
+ void setMatchNearestMailBlockquoteColor(EMatchNearestMailBlockquoteColor c) { SET_VAR(rareNonInheritedData, matchNearestMailBlockquoteColor, c); }
+ void setHighlight(const AtomicString& h) { SET_VAR(rareInheritedData, highlight, h); }
+ void setHyphens(Hyphens h) { SET_VAR(rareInheritedData, hyphens, h); }
+ void setHyphenationString(const AtomicString& h) { SET_VAR(rareInheritedData, hyphenationString, h); }
+ void setHyphenationLocale(const AtomicString& h) { SET_VAR(rareInheritedData, hyphenationLocale, h); }
+ void setBorderFit(EBorderFit b) { SET_VAR(rareNonInheritedData, m_borderFit, b); }
+ void setResize(EResize r) { SET_VAR(rareInheritedData, resize, r); }
+ void setColumnWidth(float f) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoWidth, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_width, f); }
+ void setHasAutoColumnWidth() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoWidth, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_width, 0); }
+ void setColumnCount(unsigned short c) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoCount, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_count, c); }
+ void setHasAutoColumnCount() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoCount, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_count, 0); }
+ void setColumnGap(float f) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_normalGap, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_gap, f); }
+ void setHasNormalColumnGap() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_normalGap, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_gap, 0); }
+ void setColumnRuleColor(const Color& c) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_color, c); }
+ void setColumnRuleStyle(EBorderStyle b) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_style, b); }
+ void setColumnRuleWidth(unsigned short w) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_width, w); }
+ void resetColumnRule() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule, BorderValue()) }
+ void setColumnSpan(bool b) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_columnSpan, b); }
+ void setColumnBreakBefore(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakBefore, p); }
+ void setColumnBreakInside(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakInside, p); }
+ void setColumnBreakAfter(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakAfter, p); }
+ void inheritColumnPropertiesFrom(RenderStyle* parent) { rareNonInheritedData.access()->m_multiCol = parent->rareNonInheritedData->m_multiCol; }
+ void setTransform(const TransformOperations& ops) { SET_VAR(rareNonInheritedData.access()->m_transform, m_operations, ops); }
+ void setTransformOriginX(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); }
+ void setTransformOriginY(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); }
+ void setTransformOriginZ(float f) { SET_VAR(rareNonInheritedData.access()->m_transform, m_z, f); }
+ void setSpeak(ESpeak s) { SET_VAR(rareInheritedData, speak, s); }
+ void setTextCombine(TextCombine v) { SET_VAR(rareNonInheritedData, m_textCombine, v); }
+ void setTextEmphasisColor(const Color& c) { SET_VAR(rareInheritedData, textEmphasisColor, c) }
+ void setTextEmphasisFill(TextEmphasisFill fill) { SET_VAR(rareInheritedData, textEmphasisFill, fill); }
+ void setTextEmphasisMark(TextEmphasisMark mark) { SET_VAR(rareInheritedData, textEmphasisMark, mark); }
+ void setTextEmphasisCustomMark(const AtomicString& mark) { SET_VAR(rareInheritedData, textEmphasisCustomMark, mark); }
+ void setTextEmphasisPosition(TextEmphasisPosition position) { SET_VAR(rareInheritedData, textEmphasisPosition, position); }
+ // End CSS3 Setters
+
+ // Apple-specific property setters
+ void setPointerEvents(EPointerEvents p) { inherited_flags._pointerEvents = p; }
+
+ void clearAnimations()
+ {
+ rareNonInheritedData.access()->m_animations.clear();
+ }
+
+ void clearTransitions()
+ {
+ rareNonInheritedData.access()->m_transitions.clear();
+ }
+
+ void inheritAnimations(const AnimationList* parent) { rareNonInheritedData.access()->m_animations = parent ? adoptPtr(new AnimationList(*parent)) : PassOwnPtr<AnimationList>(); }
+ void inheritTransitions(const AnimationList* parent) { rareNonInheritedData.access()->m_transitions = parent ? adoptPtr(new AnimationList(*parent)) : PassOwnPtr<AnimationList>(); }
+ void adjustAnimations();
+ void adjustTransitions();
+
+ void setTransformStyle3D(ETransformStyle3D b) { SET_VAR(rareNonInheritedData, m_transformStyle3D, b); }
+ void setBackfaceVisibility(EBackfaceVisibility b) { SET_VAR(rareNonInheritedData, m_backfaceVisibility, b); }
+ void setPerspective(float p) { SET_VAR(rareNonInheritedData, m_perspective, p); }
+ void setPerspectiveOriginX(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginX, l); }
+ void setPerspectiveOriginY(Length l) { SET_VAR(rareNonInheritedData, m_perspectiveOriginY, l); }
+ void setPageSize(LengthSize s) { SET_VAR(rareNonInheritedData, m_pageSize, s); }
+ void setPageSizeType(PageSizeType t) { SET_VAR(rareNonInheritedData, m_pageSizeType, t); }
+ void resetPageSizeType() { SET_VAR(rareNonInheritedData, m_pageSizeType, PAGE_SIZE_AUTO); }
+
+#if USE(ACCELERATED_COMPOSITING)
+ void setIsRunningAcceleratedAnimation(bool b = true) { SET_VAR(rareNonInheritedData, m_runningAcceleratedAnimation, b); }
+#endif
+
+ void setLineClamp(LineClampValue c) { SET_VAR(rareNonInheritedData, lineClamp, c); }
+ void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); }
+ void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); }
+
+#ifdef ANDROID_CSS_RING
+ void setRingFillColor(const Color& v) { SET_VAR(rareInheritedData, ringFillColor, v); }
+ void setRingInnerWidth(Length v) { SET_VAR(rareInheritedData, ringInnerWidth, v); }
+ void setRingOuterWidth(Length v) { SET_VAR(rareInheritedData, ringOuterWidth, v); }
+ void setRingOutset(Length v) { SET_VAR(rareInheritedData, ringOutset, v); }
+ void setRingPressedInnerColor(const Color& v) {
+ SET_VAR(rareInheritedData, ringPressedInnerColor, v); }
+ void setRingPressedOuterColor(const Color& v) {
+ SET_VAR(rareInheritedData, ringPressedOuterColor, v); }
+ void setRingRadius(Length v) { SET_VAR(rareInheritedData, ringRadius, v); }
+ void setRingSelectedInnerColor(const Color& v) {
+ SET_VAR(rareInheritedData, ringSelectedInnerColor, v); }
+ void setRingSelectedOuterColor(const Color& v) {
+ SET_VAR(rareInheritedData, ringSelectedOuterColor, v); }
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ void setTapHighlightColor(const Color& v) { SET_VAR(rareInheritedData, tapHighlightColor, v); }
+#endif
+
+#if ENABLE(SVG)
+ const SVGRenderStyle* svgStyle() const { return m_svgStyle.get(); }
+ SVGRenderStyle* accessSVGStyle() { return m_svgStyle.access(); }
+
+ float fillOpacity() const { return svgStyle()->fillOpacity(); }
+ void setFillOpacity(float f) { accessSVGStyle()->setFillOpacity(f); }
+
+ float strokeOpacity() const { return svgStyle()->strokeOpacity(); }
+ void setStrokeOpacity(float f) { accessSVGStyle()->setStrokeOpacity(f); }
+
+ float floodOpacity() const { return svgStyle()->floodOpacity(); }
+ void setFloodOpacity(float f) { accessSVGStyle()->setFloodOpacity(f); }
+#endif
+
+ const ContentData* contentData() const { return rareNonInheritedData->m_content.get(); }
+ bool contentDataEquivalent(const RenderStyle* otherStyle) const { return const_cast<RenderStyle*>(this)->rareNonInheritedData->contentDataEquivalent(*const_cast<RenderStyle*>(otherStyle)->rareNonInheritedData); }
+ void clearContent();
+ void setContent(PassRefPtr<StringImpl>, bool add = false);
+ void setContent(PassRefPtr<StyleImage>, bool add = false);
+ void setContent(PassOwnPtr<CounterContent>, bool add = false);
+
+ const CounterDirectiveMap* counterDirectives() const;
+ CounterDirectiveMap& accessCounterDirectives();
+
+ const AtomicString& hyphenString() const;
+
+ bool inheritedNotEqual(const RenderStyle*) const;
+
+ StyleDifference diff(const RenderStyle*, unsigned& changedContextSensitiveProperties) const;
+
+ bool isDisplayReplacedType() const
+ {
+ return display() == INLINE_BLOCK || display() == INLINE_BOX || display() == INLINE_TABLE;
+ }
+
+ bool isDisplayInlineType() const
+ {
+ return display() == INLINE || isDisplayReplacedType();
+ }
+
+ bool isOriginalDisplayInlineType() const
+ {
+ return originalDisplay() == INLINE || originalDisplay() == INLINE_BLOCK ||
+ originalDisplay() == INLINE_BOX || originalDisplay() == INLINE_TABLE;
+ }
+
+ void setWritingMode(WritingMode v) { inherited_flags.m_writingMode = v; }
+
+ // To tell if this style matched attribute selectors. This makes it impossible to share.
+ bool affectedByAttributeSelectors() const { return m_affectedByAttributeSelectors; }
+ void setAffectedByAttributeSelectors() { m_affectedByAttributeSelectors = true; }
+
+ bool unique() const { return m_unique; }
+ void setUnique() { m_unique = true; }
+
+ // Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.)
+ bool affectedByEmpty() const { return m_affectedByEmpty; }
+ bool emptyState() const { return m_emptyState; }
+ void setEmptyState(bool b) { m_affectedByEmpty = true; m_unique = true; m_emptyState = b; }
+ bool childrenAffectedByPositionalRules() const { return childrenAffectedByForwardPositionalRules() || childrenAffectedByBackwardPositionalRules(); }
+ bool childrenAffectedByFirstChildRules() const { return m_childrenAffectedByFirstChildRules; }
+ void setChildrenAffectedByFirstChildRules() { m_childrenAffectedByFirstChildRules = true; }
+ bool childrenAffectedByLastChildRules() const { return m_childrenAffectedByLastChildRules; }
+ void setChildrenAffectedByLastChildRules() { m_childrenAffectedByLastChildRules = true; }
+ bool childrenAffectedByDirectAdjacentRules() const { return m_childrenAffectedByDirectAdjacentRules; }
+ void setChildrenAffectedByDirectAdjacentRules() { m_childrenAffectedByDirectAdjacentRules = true; }
+ bool childrenAffectedByForwardPositionalRules() const { return m_childrenAffectedByForwardPositionalRules; }
+ void setChildrenAffectedByForwardPositionalRules() { m_childrenAffectedByForwardPositionalRules = true; }
+ bool childrenAffectedByBackwardPositionalRules() const { return m_childrenAffectedByBackwardPositionalRules; }
+ void setChildrenAffectedByBackwardPositionalRules() { m_childrenAffectedByBackwardPositionalRules = true; }
+ bool firstChildState() const { return m_firstChildState; }
+ void setFirstChildState() { m_firstChildState = true; }
+ bool lastChildState() const { return m_lastChildState; }
+ void setLastChildState() { m_lastChildState = true; }
+ unsigned childIndex() const { return m_childIndex; }
+ void setChildIndex(unsigned index) { m_childIndex = index; }
+
+ const Color visitedDependentColor(int colorProperty) const;
+
+ // Initial values for all the properties
+ static bool initialBorderCollapse() { return false; }
+ static EBorderStyle initialBorderStyle() { return BNONE; }
+ static NinePieceImage initialNinePieceImage() { return NinePieceImage(); }
+ static LengthSize initialBorderRadius() { return LengthSize(Length(0, Fixed), Length(0, Fixed)); }
+ static ECaptionSide initialCaptionSide() { return CAPTOP; }
+ static EClear initialClear() { return CNONE; }
+ static TextDirection initialDirection() { return LTR; }
+ static WritingMode initialWritingMode() { return TopToBottomWritingMode; }
+ static TextCombine initialTextCombine() { return TextCombineNone; }
+ static EDisplay initialDisplay() { return INLINE; }
+ static EEmptyCell initialEmptyCells() { return SHOW; }
+ static EFloat initialFloating() { return FNONE; }
+ static EListStylePosition initialListStylePosition() { return OUTSIDE; }
+ static EListStyleType initialListStyleType() { return Disc; }
+ static EOverflow initialOverflowX() { return OVISIBLE; }
+ static EOverflow initialOverflowY() { return OVISIBLE; }
+ static EPageBreak initialPageBreak() { return PBAUTO; }
+ static EPosition initialPosition() { return StaticPosition; }
+ static ETableLayout initialTableLayout() { return TAUTO; }
+ static EUnicodeBidi initialUnicodeBidi() { return UBNormal; }
+ static ETextTransform initialTextTransform() { return TTNONE; }
+ static EVisibility initialVisibility() { return VISIBLE; }
+ static EWhiteSpace initialWhiteSpace() { return NORMAL; }
+ static short initialHorizontalBorderSpacing() { return 0; }
+ static short initialVerticalBorderSpacing() { return 0; }
+ static ECursor initialCursor() { return CURSOR_AUTO; }
+ static Color initialColor() { return Color::black; }
+ static StyleImage* initialListStyleImage() { return 0; }
+ static unsigned short initialBorderWidth() { return 3; }
+ static int initialLetterWordSpacing() { return 0; }
+ static Length initialSize() { return Length(); }
+ static Length initialMinSize() { return Length(0, Fixed); }
+ static Length initialMaxSize() { return Length(undefinedLength, Fixed); }
+ static Length initialOffset() { return Length(); }
+ static Length initialMargin() { return Length(Fixed); }
+ static Length initialPadding() { return Length(Fixed); }
+ static Length initialTextIndent() { return Length(Fixed); }
+ static EVerticalAlign initialVerticalAlign() { return BASELINE; }
+ static int initialWidows() { return 2; }
+ static int initialOrphans() { return 2; }
+ static Length initialLineHeight() { return Length(-100.0, Percent); }
+ static ETextAlign initialTextAlign() { return TAAUTO; }
+ static ETextDecoration initialTextDecoration() { return TDNONE; }
+ static float initialZoom() { return 1.0f; }
+ static int initialOutlineOffset() { return 0; }
+ static float initialOpacity() { return 1.0f; }
+ static EBoxAlignment initialBoxAlign() { return BSTRETCH; }
+ static EBoxDirection initialBoxDirection() { return BNORMAL; }
+ static EBoxLines initialBoxLines() { return SINGLE; }
+ static EBoxOrient initialBoxOrient() { return HORIZONTAL; }
+ static EBoxAlignment initialBoxPack() { return BSTART; }
+ static float initialBoxFlex() { return 0.0f; }
+ static int initialBoxFlexGroup() { return 1; }
+ static int initialBoxOrdinalGroup() { return 1; }
+ static EBoxSizing initialBoxSizing() { return CONTENT_BOX; }
+ static StyleReflection* initialBoxReflect() { return 0; }
+ static int initialMarqueeLoopCount() { return -1; }
+ static int initialMarqueeSpeed() { return 85; }
+ static Length initialMarqueeIncrement() { return Length(6, Fixed); }
+ static EMarqueeBehavior initialMarqueeBehavior() { return MSCROLL; }
+ static EMarqueeDirection initialMarqueeDirection() { return MAUTO; }
+ static EUserModify initialUserModify() { return READ_ONLY; }
+ static EUserDrag initialUserDrag() { return DRAG_AUTO; }
+ static EUserSelect initialUserSelect() { return SELECT_TEXT; }
+ static bool initialTextOverflow() { return false; }
+ static EMarginCollapse initialMarginBeforeCollapse() { return MCOLLAPSE; }
+ static EMarginCollapse initialMarginAfterCollapse() { return MCOLLAPSE; }
+ static EWordBreak initialWordBreak() { return NormalWordBreak; }
+ static EWordWrap initialWordWrap() { return NormalWordWrap; }
+ static ENBSPMode initialNBSPMode() { return NBNORMAL; }
+ static EKHTMLLineBreak initialKHTMLLineBreak() { return LBNORMAL; }
+ static EMatchNearestMailBlockquoteColor initialMatchNearestMailBlockquoteColor() { return BCNORMAL; }
+ static const AtomicString& initialHighlight() { return nullAtom; }
+ static ESpeak initialSpeak() { return SpeakNormal; }
+ static Hyphens initialHyphens() { return HyphensManual; }
+ static const AtomicString& initialHyphenationString() { return nullAtom; }
+ static const AtomicString& initialHyphenationLocale() { return nullAtom; }
+ static EBorderFit initialBorderFit() { return BorderFitBorder; }
+ static EResize initialResize() { return RESIZE_NONE; }
+ static ControlPart initialAppearance() { return NoControlPart; }
+ static bool initialVisuallyOrdered() { return false; }
+ static float initialTextStrokeWidth() { return 0; }
+ static unsigned short initialColumnCount() { return 1; }
+ static bool initialColumnSpan() { return false; }
+ static const TransformOperations& initialTransform() { DEFINE_STATIC_LOCAL(TransformOperations, ops, ()); return ops; }
+ static Length initialTransformOriginX() { return Length(50.0, Percent); }
+ static Length initialTransformOriginY() { return Length(50.0, Percent); }
+ static EPointerEvents initialPointerEvents() { return PE_AUTO; }
+ static float initialTransformOriginZ() { return 0; }
+ static ETransformStyle3D initialTransformStyle3D() { return TransformStyle3DFlat; }
+ static EBackfaceVisibility initialBackfaceVisibility() { return BackfaceVisibilityVisible; }
+ static float initialPerspective() { return 0; }
+ static Length initialPerspectiveOriginX() { return Length(50.0, Percent); }
+ static Length initialPerspectiveOriginY() { return Length(50.0, Percent); }
+ static Color initialBackgroundColor() { return Color::transparent; }
+ static Color initialTextEmphasisColor() { return TextEmphasisFillFilled; }
+ static TextEmphasisFill initialTextEmphasisFill() { return TextEmphasisFillFilled; }
+ static TextEmphasisMark initialTextEmphasisMark() { return TextEmphasisMarkNone; }
+ static const AtomicString& initialTextEmphasisCustomMark() { return nullAtom; }
+ static TextEmphasisPosition initialTextEmphasisPosition() { return TextEmphasisPositionOver; }
+
+ // Keep these at the end.
+ static LineClampValue initialLineClamp() { return LineClampValue(); }
+ static bool initialTextSizeAdjust() { return true; }
+ static ETextSecurity initialTextSecurity() { return TSNONE; }
+#if ENABLE(DASHBOARD_SUPPORT)
+ static const Vector<StyleDashboardRegion>& initialDashboardRegions();
+ static const Vector<StyleDashboardRegion>& noneDashboardRegions();
+#endif
+
+#ifdef ANDROID_CSS_RING
+ static Color initialRingFillColor() { return Color::ringFill; }
+ static Length initialRingInnerWidth() { return Length(16, Fixed); } // 1.0
+ static Length initialRingOuterWidth() { return Length(40, Fixed); } // 2.5
+ static Length initialRingOutset() { return Length(3, Fixed); }
+ static Color initialRingSelectedInnerColor() { return Color::ringSelectedInner; }
+ static Color initialRingSelectedOuterColor() { return Color::ringSelectedOuter; }
+ static Color initialRingPressedInnerColor() { return Color::ringPressedInner; }
+ static Color initialRingPressedOuterColor() { return Color::ringPressedOuter; }
+ static Length initialRingRadius() { return Length(1, Fixed); }
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ static Color initialTapHighlightColor() { return Color::tap; }
+#endif
+
+private:
+ void getShadowExtent(const ShadowData*, int& top, int& right, int& bottom, int& left) const;
+ void getShadowHorizontalExtent(const ShadowData*, int& left, int& right) const;
+ void getShadowVerticalExtent(const ShadowData*, int& top, int& bottom) const;
+ void getShadowInlineDirectionExtent(const ShadowData* shadow, int& logicalLeft, int& logicalRight) const
+ {
+ return isHorizontalWritingMode() ? getShadowHorizontalExtent(shadow, logicalLeft, logicalRight) : getShadowVerticalExtent(shadow, logicalLeft, logicalRight);
+ }
+ void getShadowBlockDirectionExtent(const ShadowData* shadow, int& logicalTop, int& logicalBottom) const
+ {
+ return isHorizontalWritingMode() ? getShadowVerticalExtent(shadow, logicalTop, logicalBottom) : getShadowHorizontalExtent(shadow, logicalTop, logicalBottom);
+ }
+
+ // Color accessors are all private to make sure callers use visitedDependentColor instead to access them.
+ const Color& borderLeftColor() const { return surround->border.left().color(); }
+ const Color& borderRightColor() const { return surround->border.right().color(); }
+ const Color& borderTopColor() const { return surround->border.top().color(); }
+ const Color& borderBottomColor() const { return surround->border.bottom().color(); }
+ const Color& backgroundColor() const { return m_background->color(); }
+ const Color& color() const { return inherited->color; }
+ const Color& columnRuleColor() const { return rareNonInheritedData->m_multiCol->m_rule.color(); }
+ const Color& outlineColor() const { return m_background->outline().color(); }
+ const Color& textEmphasisColor() const { return rareInheritedData->textEmphasisColor; }
+ const Color& textFillColor() const { return rareInheritedData->textFillColor; }
+ const Color& textStrokeColor() const { return rareInheritedData->textStrokeColor; }
+
+ const Color colorIncludingFallback(int colorProperty, EBorderStyle borderStyle) const;
+
+ ContentData* prepareToSetContent(StringImpl*, bool add);
+};
+
+inline int adjustForAbsoluteZoom(int value, const RenderStyle* style)
+{
+ double zoomFactor = style->effectiveZoom();
+ if (zoomFactor == 1)
+ return value;
+ // Needed because computeLengthInt truncates (rather than rounds) when scaling up.
+ if (zoomFactor > 1) {
+ if (value < 0)
+ value--;
+ else
+ value++;
+ }
+
+ return roundForImpreciseConversion<int, INT_MAX, INT_MIN>(value / zoomFactor);
+}
+
+inline float adjustFloatForAbsoluteZoom(float value, const RenderStyle* style)
+{
+ return value / style->effectiveZoom();
+}
+
+} // namespace WebCore
+
+#endif // RenderStyle_h
diff --git a/Source/WebCore/rendering/style/RenderStyleConstants.h b/Source/WebCore/rendering/style/RenderStyleConstants.h
new file mode 100644
index 0000000..0839864
--- /dev/null
+++ b/Source/WebCore/rendering/style/RenderStyleConstants.h
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com)
+ * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (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 RenderStyleConstants_h
+#define RenderStyleConstants_h
+
+namespace WebCore {
+
+/*
+ * WARNING:
+ * --------
+ *
+ * The order of the values in the enums have to agree with the order specified
+ * in CSSValueKeywords.in, otherwise some optimizations in the parser will fail,
+ * and produce invalid results.
+ */
+
+// The difference between two styles. The following values are used:
+// (1) StyleDifferenceEqual - The two styles are identical
+// (2) StyleDifferenceRecompositeLayer - The layer needs its position and transform updated, but no repaint
+// (3) StyleDifferenceRepaint - The object just needs to be repainted.
+// (4) StyleDifferenceRepaintLayer - The layer and its descendant layers needs to be repainted.
+// (5) StyleDifferenceLayout - A layout is required.
+enum StyleDifference {
+ StyleDifferenceEqual,
+#if USE(ACCELERATED_COMPOSITING)
+ StyleDifferenceRecompositeLayer,
+#endif
+ StyleDifferenceRepaint,
+ StyleDifferenceRepaintLayer,
+ StyleDifferenceLayoutPositionedMovementOnly,
+ StyleDifferenceLayout
+};
+
+// When some style properties change, different amounts of work have to be done depending on
+// context (e.g. whether the property is changing on an element which has a compositing layer).
+// A simple StyleDifference does not provide enough information so we return a bit mask of
+// StyleDifferenceContextSensitiveProperties from RenderStyle::diff() too.
+enum StyleDifferenceContextSensitiveProperty {
+ ContextSensitivePropertyNone = 0,
+ ContextSensitivePropertyTransform = (1 << 0),
+ ContextSensitivePropertyOpacity = (1 << 1)
+};
+
+// Static pseudo styles. Dynamic ones are produced on the fly.
+enum PseudoId {
+ // The order must be NOP ID, public IDs, and then internal IDs.
+ NOPSEUDO, FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, SELECTION, FIRST_LINE_INHERITED, SCROLLBAR, FILE_UPLOAD_BUTTON, INPUT_PLACEHOLDER,
+ SLIDER_THUMB, SEARCH_CANCEL_BUTTON, SEARCH_DECORATION, SEARCH_RESULTS_DECORATION, SEARCH_RESULTS_BUTTON, MEDIA_CONTROLS_PANEL,
+ MEDIA_CONTROLS_PLAY_BUTTON, MEDIA_CONTROLS_MUTE_BUTTON, MEDIA_CONTROLS_TIMELINE, MEDIA_CONTROLS_TIMELINE_CONTAINER,
+ MEDIA_CONTROLS_VOLUME_SLIDER, MEDIA_CONTROLS_VOLUME_SLIDER_CONTAINER, MEDIA_CONTROLS_VOLUME_SLIDER_MUTE_BUTTON,
+ MEDIA_CONTROLS_CURRENT_TIME_DISPLAY, MEDIA_CONTROLS_TIME_REMAINING_DISPLAY,
+ MEDIA_CONTROLS_SEEK_BACK_BUTTON, MEDIA_CONTROLS_SEEK_FORWARD_BUTTON, MEDIA_CONTROLS_FULLSCREEN_BUTTON, MEDIA_CONTROLS_REWIND_BUTTON,
+ MEDIA_CONTROLS_RETURN_TO_REALTIME_BUTTON, MEDIA_CONTROLS_TOGGLE_CLOSED_CAPTIONS_BUTTON,
+ MEDIA_CONTROLS_STATUS_DISPLAY, SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER,
+ INPUT_LIST_BUTTON, INPUT_SPEECH_BUTTON, INNER_SPIN_BUTTON, OUTER_SPIN_BUTTON, VISITED_LINK, PROGRESS_BAR_VALUE,
+ METER_HORIZONTAL_BAR, METER_HORIZONTAL_OPTIMUM, METER_HORIZONTAL_SUBOPTIMAL, METER_HORIZONTAL_EVEN_LESS_GOOD,
+ METER_VERTICAL_BAR, METER_VERTICAL_OPTIMUM, METER_VERTICAL_SUBOPTIMAL, METER_VERTICAL_EVEN_LESS_GOOD,
+ AFTER_LAST_INTERNAL_PSEUDOID,
+ FULL_SCREEN, FULL_SCREEN_DOCUMENT,
+ FIRST_PUBLIC_PSEUDOID = FIRST_LINE,
+ FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON,
+ PUBLIC_PSEUDOID_MASK = ((1 << FIRST_INTERNAL_PSEUDOID) - 1) & ~((1 << FIRST_PUBLIC_PSEUDOID) - 1)
+};
+
+// These have been defined in the order of their precedence for border-collapsing. Do
+// not change this order!
+enum EBorderStyle { BNONE, BHIDDEN, INSET, GROOVE, RIDGE, OUTSET, DOTTED, DASHED, SOLID, DOUBLE };
+
+enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL };
+
+enum EPosition {
+ StaticPosition, RelativePosition, AbsolutePosition, FixedPosition
+};
+
+enum EFloat {
+ FNONE = 0, FLEFT, FRIGHT
+};
+
+enum EMarginCollapse { MCOLLAPSE, MSEPARATE, MDISCARD };
+
+// Box attributes. Not inherited.
+
+enum EBoxSizing { CONTENT_BOX, BORDER_BOX };
+
+// Random visual rendering model attributes. Not inherited.
+
+enum EOverflow {
+ OVISIBLE, OHIDDEN, OSCROLL, OAUTO, OOVERLAY, OMARQUEE
+};
+
+enum EVerticalAlign {
+ BASELINE, MIDDLE, SUB, SUPER, TEXT_TOP,
+ TEXT_BOTTOM, TOP, BOTTOM, BASELINE_MIDDLE, LENGTH
+};
+
+enum EClear {
+ CNONE = 0, CLEFT = 1, CRIGHT = 2, CBOTH = 3
+};
+
+enum ETableLayout {
+ TAUTO, TFIXED
+};
+
+enum EUnicodeBidi {
+ UBNormal, Embed, Override
+};
+
+// CSS Text Layout Module Level 3: Vertical writing support
+enum WritingMode {
+ TopToBottomWritingMode, RightToLeftWritingMode, LeftToRightWritingMode, BottomToTopWritingMode
+};
+
+enum TextCombine {
+ TextCombineNone, TextCombineHorizontal
+};
+
+enum EFillAttachment {
+ ScrollBackgroundAttachment, LocalBackgroundAttachment, FixedBackgroundAttachment
+};
+
+enum EFillBox {
+ BorderFillBox, PaddingFillBox, ContentFillBox, TextFillBox
+};
+
+enum EFillRepeat {
+ RepeatFill, NoRepeatFill, RoundFill, SpaceFill
+};
+
+enum EFillLayerType {
+ BackgroundFillLayer, MaskFillLayer
+};
+
+// CSS3 Background Values
+enum EFillSizeType { Contain, Cover, SizeLength, SizeNone };
+
+// CSS3 Marquee Properties
+
+enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE };
+enum EMarqueeDirection { MAUTO = 0, MLEFT = 1, MRIGHT = -1, MUP = 2, MDOWN = -2, MFORWARD = 3, MBACKWARD = -3 };
+
+// CSS3 Flexible Box Properties
+
+enum EBoxAlignment { BSTRETCH, BSTART, BCENTER, BEND, BJUSTIFY, BBASELINE };
+enum EBoxOrient { HORIZONTAL, VERTICAL };
+enum EBoxLines { SINGLE, MULTIPLE };
+enum EBoxDirection { BNORMAL, BREVERSE };
+
+enum ETextSecurity {
+ TSNONE, TSDISC, TSCIRCLE, TSSQUARE
+};
+
+// CSS3 User Modify Properties
+
+enum EUserModify {
+ READ_ONLY, READ_WRITE, READ_WRITE_PLAINTEXT_ONLY
+};
+
+// CSS3 User Drag Values
+
+enum EUserDrag {
+ DRAG_AUTO, DRAG_NONE, DRAG_ELEMENT
+};
+
+// CSS3 User Select Values
+
+enum EUserSelect {
+ SELECT_NONE, SELECT_TEXT
+};
+
+// Word Break Values. Matches WinIE, rather than CSS3
+
+enum EWordBreak {
+ NormalWordBreak, BreakAllWordBreak, BreakWordBreak
+};
+
+enum EWordWrap {
+ NormalWordWrap, BreakWordWrap
+};
+
+enum ENBSPMode {
+ NBNORMAL, SPACE
+};
+
+enum EKHTMLLineBreak {
+ LBNORMAL, AFTER_WHITE_SPACE
+};
+
+enum EMatchNearestMailBlockquoteColor {
+ BCNORMAL, MATCH
+};
+
+enum EResize {
+ RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL
+};
+
+// The order of this enum must match the order of the list style types in CSSValueKeywords.in.
+enum EListStyleType {
+ Disc,
+ Circle,
+ Square,
+ DecimalListStyle,
+ DecimalLeadingZero,
+ ArabicIndic,
+ BinaryListStyle,
+ Bengali,
+ Cambodian,
+ Khmer,
+ Devanagari,
+ Gujarati,
+ Gurmukhi,
+ Kannada,
+ LowerHexadecimal,
+ Lao,
+ Malayalam,
+ Mongolian,
+ Myanmar,
+ Octal,
+ Oriya,
+ Persian,
+ Urdu,
+ Telugu,
+ Tibetan,
+ Thai,
+ UpperHexadecimal,
+ LowerRoman,
+ UpperRoman,
+ LowerGreek,
+ LowerAlpha,
+ LowerLatin,
+ UpperAlpha,
+ UpperLatin,
+ Afar,
+ EthiopicHalehameAaEt,
+ EthiopicHalehameAaEr,
+ Amharic,
+ EthiopicHalehameAmEt,
+ AmharicAbegede,
+ EthiopicAbegedeAmEt,
+ CjkEarthlyBranch,
+ CjkHeavenlyStem,
+ Ethiopic,
+ EthiopicHalehameGez,
+ EthiopicAbegede,
+ EthiopicAbegedeGez,
+ HangulConsonant,
+ Hangul,
+ LowerNorwegian,
+ Oromo,
+ EthiopicHalehameOmEt,
+ Sidama,
+ EthiopicHalehameSidEt,
+ Somali,
+ EthiopicHalehameSoEt,
+ Tigre,
+ EthiopicHalehameTig,
+ TigrinyaEr,
+ EthiopicHalehameTiEr,
+ TigrinyaErAbegede,
+ EthiopicAbegedeTiEr,
+ TigrinyaEt,
+ EthiopicHalehameTiEt,
+ TigrinyaEtAbegede,
+ EthiopicAbegedeTiEt,
+ UpperGreek,
+ UpperNorwegian,
+ Asterisks,
+ Footnotes,
+ Hebrew,
+ Armenian,
+ LowerArmenian,
+ UpperArmenian,
+ Georgian,
+ CJKIdeographic,
+ Hiragana,
+ Katakana,
+ HiraganaIroha,
+ KatakanaIroha,
+ NoneListStyle
+};
+
+enum StyleContentType {
+ CONTENT_NONE, CONTENT_OBJECT, CONTENT_TEXT, CONTENT_COUNTER
+};
+
+enum EBorderFit { BorderFitBorder, BorderFitLines };
+
+enum EAnimationFillMode { AnimationFillModeNone, AnimationFillModeForwards, AnimationFillModeBackwards, AnimationFillModeBoth };
+
+enum EAnimPlayState {
+ AnimPlayStatePlaying = 0x0,
+ AnimPlayStatePaused = 0x1
+};
+
+enum EWhiteSpace {
+ NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP
+};
+
+enum ETextAlign {
+ TAAUTO, LEFT, RIGHT, CENTER, JUSTIFY, WEBKIT_LEFT, WEBKIT_RIGHT, WEBKIT_CENTER
+};
+
+enum ETextTransform {
+ CAPITALIZE, UPPERCASE, LOWERCASE, TTNONE
+};
+
+enum ETextDecoration {
+ TDNONE = 0x0 , UNDERLINE = 0x1, OVERLINE = 0x2, LINE_THROUGH= 0x4, BLINK = 0x8
+};
+
+enum EPageBreak {
+ PBAUTO, PBALWAYS, PBAVOID
+};
+
+enum EEmptyCell {
+ SHOW, HIDE
+};
+
+enum ECaptionSide {
+ CAPTOP, CAPBOTTOM, CAPLEFT, CAPRIGHT
+};
+
+enum EListStylePosition { OUTSIDE, INSIDE };
+
+enum EVisibility { VISIBLE, HIDDEN, COLLAPSE };
+
+enum ECursor {
+ // The following must match the order in CSSValueKeywords.in.
+ CURSOR_AUTO,
+ CURSOR_CROSS,
+ CURSOR_DEFAULT,
+ CURSOR_POINTER,
+ CURSOR_MOVE,
+ CURSOR_VERTICAL_TEXT,
+ CURSOR_CELL,
+ CURSOR_CONTEXT_MENU,
+ CURSOR_ALIAS,
+ CURSOR_PROGRESS,
+ CURSOR_NO_DROP,
+ CURSOR_NOT_ALLOWED,
+ CURSOR_WEBKIT_ZOOM_IN,
+ CURSOR_WEBKIT_ZOOM_OUT,
+ CURSOR_E_RESIZE,
+ CURSOR_NE_RESIZE,
+ CURSOR_NW_RESIZE,
+ CURSOR_N_RESIZE,
+ CURSOR_SE_RESIZE,
+ CURSOR_SW_RESIZE,
+ CURSOR_S_RESIZE,
+ CURSOR_W_RESIZE,
+ CURSOR_EW_RESIZE,
+ CURSOR_NS_RESIZE,
+ CURSOR_NESW_RESIZE,
+ CURSOR_NWSE_RESIZE,
+ CURSOR_COL_RESIZE,
+ CURSOR_ROW_RESIZE,
+ CURSOR_TEXT,
+ CURSOR_WAIT,
+ CURSOR_HELP,
+ CURSOR_ALL_SCROLL,
+ CURSOR_WEBKIT_GRAB,
+ CURSOR_WEBKIT_GRABBING,
+
+ // The following are handled as exceptions so don't need to match.
+ CURSOR_COPY,
+ CURSOR_NONE
+};
+
+enum EDisplay {
+ INLINE, BLOCK, LIST_ITEM, RUN_IN, COMPACT, INLINE_BLOCK,
+ TABLE, INLINE_TABLE, TABLE_ROW_GROUP,
+ TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_ROW,
+ TABLE_COLUMN_GROUP, TABLE_COLUMN, TABLE_CELL,
+ TABLE_CAPTION, BOX, INLINE_BOX,
+#if ENABLE(WCSS)
+ WAP_MARQUEE,
+#endif
+ NONE
+};
+
+enum EInsideLink {
+ NotInsideLink, InsideUnvisitedLink, InsideVisitedLink
+};
+
+enum EPointerEvents {
+ PE_NONE, PE_AUTO, PE_STROKE, PE_FILL, PE_PAINTED, PE_VISIBLE,
+ PE_VISIBLE_STROKE, PE_VISIBLE_FILL, PE_VISIBLE_PAINTED, PE_ALL
+};
+
+enum ETransformStyle3D {
+ TransformStyle3DFlat, TransformStyle3DPreserve3D
+};
+
+enum EBackfaceVisibility {
+ BackfaceVisibilityVisible, BackfaceVisibilityHidden
+};
+
+enum ELineClampType { LineClampLineCount, LineClampPercentage };
+
+enum Hyphens { HyphensNone, HyphensManual, HyphensAuto };
+
+enum ESpeak { SpeakNone, SpeakNormal, SpeakSpellOut, SpeakDigits, SpeakLiteralPunctuation, SpeakNoPunctuation };
+
+enum TextEmphasisFill { TextEmphasisFillFilled, TextEmphasisFillOpen };
+
+enum TextEmphasisMark { TextEmphasisMarkNone, TextEmphasisMarkAuto, TextEmphasisMarkDot, TextEmphasisMarkCircle, TextEmphasisMarkDoubleCircle, TextEmphasisMarkTriangle, TextEmphasisMarkSesame, TextEmphasisMarkCustom };
+
+enum TextEmphasisPosition { TextEmphasisPositionOver, TextEmphasisPositionUnder };
+
+} // namespace WebCore
+
+#endif // RenderStyleConstants_h
diff --git a/Source/WebCore/rendering/style/SVGRenderStyle.cpp b/Source/WebCore/rendering/style/SVGRenderStyle.cpp
new file mode 100644
index 0000000..28f80f2
--- /dev/null
+++ b/Source/WebCore/rendering/style/SVGRenderStyle.cpp
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2010 Rob Buis <buis@kde.org>
+ Copyright (C) Research In Motion Limited 2010. All rights reserved.
+
+ Based on khtml code by:
+ Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 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"
+
+#if ENABLE(SVG)
+#include "SVGRenderStyle.h"
+
+#include "CSSPrimitiveValue.h"
+#include "CSSValueList.h"
+#include "IntRect.h"
+#include "NodeRenderStyle.h"
+#include "SVGStyledElement.h"
+
+using namespace std;
+
+namespace WebCore {
+
+SVGRenderStyle::SVGRenderStyle()
+{
+ static SVGRenderStyle* defaultStyle = new SVGRenderStyle(CreateDefault);
+
+ fill = defaultStyle->fill;
+ stroke = defaultStyle->stroke;
+ text = defaultStyle->text;
+ stops = defaultStyle->stops;
+ misc = defaultStyle->misc;
+ shadowSVG = defaultStyle->shadowSVG;
+ inheritedResources = defaultStyle->inheritedResources;
+ resources = defaultStyle->resources;
+
+ setBitDefaults();
+}
+
+SVGRenderStyle::SVGRenderStyle(CreateDefaultType)
+{
+ setBitDefaults();
+
+ fill.init();
+ stroke.init();
+ text.init();
+ stops.init();
+ misc.init();
+ shadowSVG.init();
+ inheritedResources.init();
+ resources.init();
+}
+
+SVGRenderStyle::SVGRenderStyle(const SVGRenderStyle& other)
+ : RefCounted<SVGRenderStyle>()
+{
+ fill = other.fill;
+ stroke = other.stroke;
+ text = other.text;
+ stops = other.stops;
+ misc = other.misc;
+ shadowSVG = other.shadowSVG;
+ inheritedResources = other.inheritedResources;
+ resources = other.resources;
+
+ svg_inherited_flags = other.svg_inherited_flags;
+ svg_noninherited_flags = other.svg_noninherited_flags;
+}
+
+SVGRenderStyle::~SVGRenderStyle()
+{
+}
+
+bool SVGRenderStyle::operator==(const SVGRenderStyle& other) const
+{
+ return fill == other.fill
+ && stroke == other.stroke
+ && text == other.text
+ && stops == other.stops
+ && misc == other.misc
+ && shadowSVG == other.shadowSVG
+ && inheritedResources == other.inheritedResources
+ && resources == other.resources
+ && svg_inherited_flags == other.svg_inherited_flags
+ && svg_noninherited_flags == other.svg_noninherited_flags;
+}
+
+bool SVGRenderStyle::inheritedNotEqual(const SVGRenderStyle* other) const
+{
+ return fill != other->fill
+ || stroke != other->stroke
+ || text != other->text
+ || inheritedResources != other->inheritedResources
+ || svg_inherited_flags != other->svg_inherited_flags;
+}
+
+void SVGRenderStyle::inheritFrom(const SVGRenderStyle* svgInheritParent)
+{
+ if (!svgInheritParent)
+ return;
+
+ fill = svgInheritParent->fill;
+ stroke = svgInheritParent->stroke;
+ text = svgInheritParent->text;
+ inheritedResources = svgInheritParent->inheritedResources;
+
+ svg_inherited_flags = svgInheritParent->svg_inherited_flags;
+}
+
+StyleDifference SVGRenderStyle::diff(const SVGRenderStyle* other) const
+{
+ // NOTE: All comparisions that may return StyleDifferenceLayout have to go before those who return StyleDifferenceRepaint
+
+ // If kerning changes, we need a relayout, to force SVGCharacterData to be recalculated in the SVGRootInlineBox.
+ if (text != other->text)
+ return StyleDifferenceLayout;
+
+ // If resources change, we need a relayout, as the presence of resources influences the repaint rect.
+ if (resources != other->resources)
+ return StyleDifferenceLayout;
+
+ // If markers change, we need a relayout, as marker boundaries are cached in RenderSVGPath.
+ if (inheritedResources != other->inheritedResources)
+ return StyleDifferenceLayout;
+
+ // All text related properties influence layout.
+ if (svg_inherited_flags._textAnchor != other->svg_inherited_flags._textAnchor
+ || svg_inherited_flags._writingMode != other->svg_inherited_flags._writingMode
+ || svg_inherited_flags._glyphOrientationHorizontal != other->svg_inherited_flags._glyphOrientationHorizontal
+ || svg_inherited_flags._glyphOrientationVertical != other->svg_inherited_flags._glyphOrientationVertical
+ || svg_noninherited_flags.f._alignmentBaseline != other->svg_noninherited_flags.f._alignmentBaseline
+ || svg_noninherited_flags.f._dominantBaseline != other->svg_noninherited_flags.f._dominantBaseline
+ || svg_noninherited_flags.f._baselineShift != other->svg_noninherited_flags.f._baselineShift)
+ return StyleDifferenceLayout;
+
+ // Text related properties influence layout.
+ bool miscNotEqual = misc != other->misc;
+ if (miscNotEqual && misc->baselineShiftValue != other->misc->baselineShiftValue)
+ return StyleDifferenceLayout;
+
+ // These properties affect the cached stroke bounding box rects.
+ if (svg_inherited_flags._capStyle != other->svg_inherited_flags._capStyle
+ || svg_inherited_flags._joinStyle != other->svg_inherited_flags._joinStyle)
+ return StyleDifferenceLayout;
+
+ // Shadow changes require relayouts, as they affect the repaint rects.
+ if (shadowSVG != other->shadowSVG)
+ return StyleDifferenceLayout;
+
+ // Some stroke properties, requires relayouts, as the cached stroke boundaries need to be recalculated.
+ if (stroke != other->stroke) {
+ if (stroke->width != other->stroke->width
+ || stroke->paint != other->stroke->paint
+ || stroke->miterLimit != other->stroke->miterLimit
+ || stroke->dashArray != other->stroke->dashArray
+ || stroke->dashOffset != other->stroke->dashOffset)
+ return StyleDifferenceLayout;
+
+ // Only the stroke-opacity case remains, where we only need a repaint.
+ ASSERT(stroke->opacity != other->stroke->opacity);
+ return StyleDifferenceRepaint;
+ }
+
+ // NOTE: All comparisions below may only return StyleDifferenceRepaint
+
+ // Painting related properties only need repaints.
+ if (miscNotEqual) {
+ if (misc->floodColor != other->misc->floodColor
+ || misc->floodOpacity != other->misc->floodOpacity
+ || misc->lightingColor != other->misc->lightingColor)
+ return StyleDifferenceRepaint;
+ }
+
+ // If fill changes, we just need to repaint. Fill boundaries are not influenced by this, only by the Path, that RenderSVGPath contains.
+ if (fill != other->fill)
+ return StyleDifferenceRepaint;
+
+ // If gradient stops change, we just need to repaint. Style updates are already handled through RenderSVGGradientSTop.
+ if (stops != other->stops)
+ return StyleDifferenceRepaint;
+
+ // Changes of these flags only cause repaints.
+ if (svg_inherited_flags._colorRendering != other->svg_inherited_flags._colorRendering
+ || svg_inherited_flags._imageRendering != other->svg_inherited_flags._imageRendering
+ || svg_inherited_flags._shapeRendering != other->svg_inherited_flags._shapeRendering
+ || svg_inherited_flags._clipRule != other->svg_inherited_flags._clipRule
+ || svg_inherited_flags._fillRule != other->svg_inherited_flags._fillRule
+ || svg_inherited_flags._colorInterpolation != other->svg_inherited_flags._colorInterpolation
+ || svg_inherited_flags._colorInterpolationFilters != other->svg_inherited_flags._colorInterpolationFilters)
+ return StyleDifferenceRepaint;
+
+ // FIXME: vector-effect is not taken into account in the layout-phase. Once this is fixed, we should relayout here.
+ if (svg_noninherited_flags.f._vectorEffect != other->svg_noninherited_flags.f._vectorEffect)
+ return StyleDifferenceRepaint;
+
+ return StyleDifferenceEqual;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/style/SVGRenderStyle.h b/Source/WebCore/rendering/style/SVGRenderStyle.h
new file mode 100644
index 0000000..7f032e7
--- /dev/null
+++ b/Source/WebCore/rendering/style/SVGRenderStyle.h
@@ -0,0 +1,431 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005 Rob Buis <buis@kde.org>
+ Copyright (C) 2005, 2006 Apple Computer, 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.
+*/
+
+#ifndef SVGRenderStyle_h
+#define SVGRenderStyle_h
+
+#if ENABLE(SVG)
+#include "CSSValueList.h"
+#include "DataRef.h"
+#include "GraphicsTypes.h"
+#include "Path.h"
+#include "RenderStyleConstants.h"
+#include "SVGPaint.h"
+#include "SVGRenderStyleDefs.h"
+
+namespace WebCore {
+
+class FloatRect;
+class IntRect;
+class RenderObject;
+
+class SVGRenderStyle : public RefCounted<SVGRenderStyle> {
+public:
+ static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); }
+ PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));}
+ ~SVGRenderStyle();
+
+ bool inheritedNotEqual(const SVGRenderStyle*) const;
+ void inheritFrom(const SVGRenderStyle*);
+
+ StyleDifference diff(const SVGRenderStyle*) const;
+
+ bool operator==(const SVGRenderStyle&) const;
+ bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); }
+
+ // Initial values for all the properties
+ static EAlignmentBaseline initialAlignmentBaseline() { return AB_AUTO; }
+ static EDominantBaseline initialDominantBaseline() { return DB_AUTO; }
+ static EBaselineShift initialBaselineShift() { return BS_BASELINE; }
+ static EVectorEffect initialVectorEffect() { return VE_NONE; }
+ static LineCap initialCapStyle() { return ButtCap; }
+ static WindRule initialClipRule() { return RULE_NONZERO; }
+ static EColorInterpolation initialColorInterpolation() { return CI_SRGB; }
+ static EColorInterpolation initialColorInterpolationFilters() { return CI_LINEARRGB; }
+ static EColorRendering initialColorRendering() { return CR_AUTO; }
+ static WindRule initialFillRule() { return RULE_NONZERO; }
+ static EImageRendering initialImageRendering() { return IR_AUTO; }
+ static LineJoin initialJoinStyle() { return MiterJoin; }
+ static EShapeRendering initialShapeRendering() { return SR_AUTO; }
+ static ETextAnchor initialTextAnchor() { return TA_START; }
+ static SVGWritingMode initialWritingMode() { return WM_LRTB; }
+ static EGlyphOrientation initialGlyphOrientationHorizontal() { return GO_0DEG; }
+ static EGlyphOrientation initialGlyphOrientationVertical() { return GO_AUTO; }
+ static float initialFillOpacity() { return 1.0f; }
+ static SVGPaint* initialFillPaint() { return SVGPaint::defaultFill(); }
+ static float initialStrokeOpacity() { return 1.0f; }
+ static SVGPaint* initialStrokePaint() { return SVGPaint::defaultStroke(); }
+ static Vector<SVGLength> initialStrokeDashArray() { return Vector<SVGLength>(); }
+ static float initialStrokeMiterLimit() { return 4.0f; }
+ static float initialStopOpacity() { return 1.0f; }
+ static Color initialStopColor() { return Color(0, 0, 0); }
+ static float initialFloodOpacity() { return 1.0f; }
+ static Color initialFloodColor() { return Color(0, 0, 0); }
+ static Color initialLightingColor() { return Color(255, 255, 255); }
+ static ShadowData* initialShadow() { return 0; }
+ static String initialClipperResource() { return String(); }
+ static String initialFilterResource() { return String(); }
+ static String initialMaskerResource() { return String(); }
+ static String initialMarkerStartResource() { return String(); }
+ static String initialMarkerMidResource() { return String(); }
+ static String initialMarkerEndResource() { return String(); }
+
+ static SVGLength initialBaselineShiftValue()
+ {
+ SVGLength length;
+ ExceptionCode ec = 0;
+ length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec);
+ ASSERT(!ec);
+ return length;
+ }
+
+ static SVGLength initialKerning()
+ {
+ SVGLength length;
+ ExceptionCode ec = 0;
+ length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec);
+ ASSERT(!ec);
+ return length;
+ }
+
+ static SVGLength initialStrokeDashOffset()
+ {
+ SVGLength length;
+ ExceptionCode ec = 0;
+ length.newValueSpecifiedUnits(LengthTypeNumber, 0, ec);
+ ASSERT(!ec);
+ return length;
+ }
+
+ static SVGLength initialStrokeWidth()
+ {
+ SVGLength length;
+ ExceptionCode ec = 0;
+ length.newValueSpecifiedUnits(LengthTypeNumber, 1, ec);
+ ASSERT(!ec);
+ return length;
+ }
+
+ // SVG CSS Property setters
+ void setAlignmentBaseline(EAlignmentBaseline val) { svg_noninherited_flags.f._alignmentBaseline = val; }
+ void setDominantBaseline(EDominantBaseline val) { svg_noninherited_flags.f._dominantBaseline = val; }
+ void setBaselineShift(EBaselineShift val) { svg_noninherited_flags.f._baselineShift = val; }
+ void setVectorEffect(EVectorEffect val) { svg_noninherited_flags.f._vectorEffect = val; }
+ void setCapStyle(LineCap val) { svg_inherited_flags._capStyle = val; }
+ void setClipRule(WindRule val) { svg_inherited_flags._clipRule = val; }
+ void setColorInterpolation(EColorInterpolation val) { svg_inherited_flags._colorInterpolation = val; }
+ void setColorInterpolationFilters(EColorInterpolation val) { svg_inherited_flags._colorInterpolationFilters = val; }
+ void setColorRendering(EColorRendering val) { svg_inherited_flags._colorRendering = val; }
+ void setFillRule(WindRule val) { svg_inherited_flags._fillRule = val; }
+ void setImageRendering(EImageRendering val) { svg_inherited_flags._imageRendering = val; }
+ void setJoinStyle(LineJoin val) { svg_inherited_flags._joinStyle = val; }
+ void setShapeRendering(EShapeRendering val) { svg_inherited_flags._shapeRendering = val; }
+ void setTextAnchor(ETextAnchor val) { svg_inherited_flags._textAnchor = val; }
+ void setWritingMode(SVGWritingMode val) { svg_inherited_flags._writingMode = val; }
+ void setGlyphOrientationHorizontal(EGlyphOrientation val) { svg_inherited_flags._glyphOrientationHorizontal = val; }
+ void setGlyphOrientationVertical(EGlyphOrientation val) { svg_inherited_flags._glyphOrientationVertical = val; }
+
+ void setFillOpacity(float obj)
+ {
+ if (!(fill->opacity == obj))
+ fill.access()->opacity = obj;
+ }
+
+ void setFillPaint(PassRefPtr<SVGPaint> obj)
+ {
+ if (!(fill->paint == obj))
+ fill.access()->paint = obj;
+ }
+
+ void setStrokeOpacity(float obj)
+ {
+ if (!(stroke->opacity == obj))
+ stroke.access()->opacity = obj;
+ }
+
+ void setStrokePaint(PassRefPtr<SVGPaint> obj)
+ {
+ if (!(stroke->paint == obj))
+ stroke.access()->paint = obj;
+ }
+
+ void setStrokeDashArray(const Vector<SVGLength>& obj)
+ {
+ if (!(stroke->dashArray == obj))
+ stroke.access()->dashArray = obj;
+ }
+
+ void setStrokeMiterLimit(float obj)
+ {
+ if (!(stroke->miterLimit == obj))
+ stroke.access()->miterLimit = obj;
+ }
+
+ void setStrokeWidth(const SVGLength& obj)
+ {
+ if (!(stroke->width == obj))
+ stroke.access()->width = obj;
+ }
+
+ void setStrokeDashOffset(const SVGLength& obj)
+ {
+ if (!(stroke->dashOffset == obj))
+ stroke.access()->dashOffset = obj;
+ }
+
+ void setKerning(const SVGLength& obj)
+ {
+ if (!(text->kerning == obj))
+ text.access()->kerning = obj;
+ }
+
+ void setStopOpacity(float obj)
+ {
+ if (!(stops->opacity == obj))
+ stops.access()->opacity = obj;
+ }
+
+ void setStopColor(const Color& obj)
+ {
+ if (!(stops->color == obj))
+ stops.access()->color = obj;
+ }
+
+ void setFloodOpacity(float obj)
+ {
+ if (!(misc->floodOpacity == obj))
+ misc.access()->floodOpacity = obj;
+ }
+
+ void setFloodColor(const Color& obj)
+ {
+ if (!(misc->floodColor == obj))
+ misc.access()->floodColor = obj;
+ }
+
+ void setLightingColor(const Color& obj)
+ {
+ if (!(misc->lightingColor == obj))
+ misc.access()->lightingColor = obj;
+ }
+
+ void setBaselineShiftValue(const SVGLength& obj)
+ {
+ if (!(misc->baselineShiftValue == obj))
+ misc.access()->baselineShiftValue = obj;
+ }
+
+ void setShadow(PassOwnPtr<ShadowData> obj) { shadowSVG.access()->shadow = obj; }
+
+ // Setters for non-inherited resources
+ void setClipperResource(const String& obj)
+ {
+ if (!(resources->clipper == obj))
+ resources.access()->clipper = obj;
+ }
+
+ void setFilterResource(const String& obj)
+ {
+ if (!(resources->filter == obj))
+ resources.access()->filter = obj;
+ }
+
+ void setMaskerResource(const String& obj)
+ {
+ if (!(resources->masker == obj))
+ resources.access()->masker = obj;
+ }
+
+ // Setters for inherited resources
+ void setMarkerStartResource(const String& obj)
+ {
+ if (!(inheritedResources->markerStart == obj))
+ inheritedResources.access()->markerStart = obj;
+ }
+
+ void setMarkerMidResource(const String& obj)
+ {
+ if (!(inheritedResources->markerMid == obj))
+ inheritedResources.access()->markerMid = obj;
+ }
+
+ void setMarkerEndResource(const String& obj)
+ {
+ if (!(inheritedResources->markerEnd == obj))
+ inheritedResources.access()->markerEnd = obj;
+ }
+
+ // Read accessors for all the properties
+ EAlignmentBaseline alignmentBaseline() const { return (EAlignmentBaseline) svg_noninherited_flags.f._alignmentBaseline; }
+ EDominantBaseline dominantBaseline() const { return (EDominantBaseline) svg_noninherited_flags.f._dominantBaseline; }
+ EBaselineShift baselineShift() const { return (EBaselineShift) svg_noninherited_flags.f._baselineShift; }
+ EVectorEffect vectorEffect() const { return (EVectorEffect) svg_noninherited_flags.f._vectorEffect; }
+ LineCap capStyle() const { return (LineCap) svg_inherited_flags._capStyle; }
+ WindRule clipRule() const { return (WindRule) svg_inherited_flags._clipRule; }
+ EColorInterpolation colorInterpolation() const { return (EColorInterpolation) svg_inherited_flags._colorInterpolation; }
+ EColorInterpolation colorInterpolationFilters() const { return (EColorInterpolation) svg_inherited_flags._colorInterpolationFilters; }
+ EColorRendering colorRendering() const { return (EColorRendering) svg_inherited_flags._colorRendering; }
+ WindRule fillRule() const { return (WindRule) svg_inherited_flags._fillRule; }
+ EImageRendering imageRendering() const { return (EImageRendering) svg_inherited_flags._imageRendering; }
+ LineJoin joinStyle() const { return (LineJoin) svg_inherited_flags._joinStyle; }
+ EShapeRendering shapeRendering() const { return (EShapeRendering) svg_inherited_flags._shapeRendering; }
+ ETextAnchor textAnchor() const { return (ETextAnchor) svg_inherited_flags._textAnchor; }
+ SVGWritingMode writingMode() const { return (SVGWritingMode) svg_inherited_flags._writingMode; }
+ EGlyphOrientation glyphOrientationHorizontal() const { return (EGlyphOrientation) svg_inherited_flags._glyphOrientationHorizontal; }
+ EGlyphOrientation glyphOrientationVertical() const { return (EGlyphOrientation) svg_inherited_flags._glyphOrientationVertical; }
+ float fillOpacity() const { return fill->opacity; }
+ SVGPaint* fillPaint() const { return fill->paint.get(); }
+ float strokeOpacity() const { return stroke->opacity; }
+ SVGPaint* strokePaint() const { return stroke->paint.get(); }
+ Vector<SVGLength> strokeDashArray() const { return stroke->dashArray; }
+ float strokeMiterLimit() const { return stroke->miterLimit; }
+ SVGLength strokeWidth() const { return stroke->width; }
+ SVGLength strokeDashOffset() const { return stroke->dashOffset; }
+ SVGLength kerning() const { return text->kerning; }
+ float stopOpacity() const { return stops->opacity; }
+ Color stopColor() const { return stops->color; }
+ float floodOpacity() const { return misc->floodOpacity; }
+ Color floodColor() const { return misc->floodColor; }
+ Color lightingColor() const { return misc->lightingColor; }
+ SVGLength baselineShiftValue() const { return misc->baselineShiftValue; }
+ ShadowData* shadow() const { return shadowSVG->shadow.get(); }
+ String clipperResource() const { return resources->clipper; }
+ String filterResource() const { return resources->filter; }
+ String maskerResource() const { return resources->masker; }
+ String markerStartResource() const { return inheritedResources->markerStart; }
+ String markerMidResource() const { return inheritedResources->markerMid; }
+ String markerEndResource() const { return inheritedResources->markerEnd; }
+
+ // convenience
+ bool hasClipper() const { return !clipperResource().isEmpty(); }
+ bool hasMasker() const { return !maskerResource().isEmpty(); }
+ bool hasFilter() const { return !filterResource().isEmpty(); }
+ bool hasMarkers() const { return !markerStartResource().isEmpty() || !markerMidResource().isEmpty() || !markerEndResource().isEmpty(); }
+ bool hasStroke() const { return strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE; }
+ bool hasFill() const { return fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE; }
+ bool isVerticalWritingMode() const { return writingMode() == WM_TBRL || writingMode() == WM_TB; }
+
+protected:
+ // inherit
+ struct InheritedFlags {
+ bool operator==(const InheritedFlags& other) const
+ {
+ return (_colorRendering == other._colorRendering)
+ && (_imageRendering == other._imageRendering)
+ && (_shapeRendering == other._shapeRendering)
+ && (_clipRule == other._clipRule)
+ && (_fillRule == other._fillRule)
+ && (_capStyle == other._capStyle)
+ && (_joinStyle == other._joinStyle)
+ && (_textAnchor == other._textAnchor)
+ && (_colorInterpolation == other._colorInterpolation)
+ && (_colorInterpolationFilters == other._colorInterpolationFilters)
+ && (_writingMode == other._writingMode)
+ && (_glyphOrientationHorizontal == other._glyphOrientationHorizontal)
+ && (_glyphOrientationVertical == other._glyphOrientationVertical);
+ }
+
+ bool operator!=(const InheritedFlags& other) const
+ {
+ return !(*this == other);
+ }
+
+ unsigned _colorRendering : 2; // EColorRendering
+ unsigned _imageRendering : 2; // EImageRendering
+ unsigned _shapeRendering : 2; // EShapeRendering
+ unsigned _clipRule : 1; // WindRule
+ unsigned _fillRule : 1; // WindRule
+ unsigned _capStyle : 2; // LineCap
+ unsigned _joinStyle : 2; // LineJoin
+ unsigned _textAnchor : 2; // ETextAnchor
+ unsigned _colorInterpolation : 2; // EColorInterpolation
+ unsigned _colorInterpolationFilters : 2; // EColorInterpolation
+ unsigned _writingMode : 3; // SVGWritingMode
+ unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation
+ unsigned _glyphOrientationVertical : 3; // EGlyphOrientation
+ } svg_inherited_flags;
+
+ // don't inherit
+ struct NonInheritedFlags {
+ // 32 bit non-inherited, don't add to the struct, or the operator will break.
+ bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; }
+ bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; }
+
+ union {
+ struct {
+ unsigned _alignmentBaseline : 4; // EAlignmentBaseline
+ unsigned _dominantBaseline : 4; // EDominantBaseline
+ unsigned _baselineShift : 2; // EBaselineShift
+ unsigned _vectorEffect: 1; // EVectorEffect
+ // 21 bits unused
+ } f;
+ uint32_t _niflags;
+ };
+ } svg_noninherited_flags;
+
+ // inherited attributes
+ DataRef<StyleFillData> fill;
+ DataRef<StyleStrokeData> stroke;
+ DataRef<StyleTextData> text;
+ DataRef<StyleInheritedResourceData> inheritedResources;
+
+ // non-inherited attributes
+ DataRef<StyleStopData> stops;
+ DataRef<StyleMiscData> misc;
+ DataRef<StyleShadowSVGData> shadowSVG;
+ DataRef<StyleResourceData> resources;
+
+private:
+ enum CreateDefaultType { CreateDefault };
+
+ SVGRenderStyle();
+ SVGRenderStyle(const SVGRenderStyle&);
+ SVGRenderStyle(CreateDefaultType); // Used to create the default style.
+
+ void setBitDefaults()
+ {
+ svg_inherited_flags._clipRule = initialClipRule();
+ svg_inherited_flags._colorRendering = initialColorRendering();
+ svg_inherited_flags._fillRule = initialFillRule();
+ svg_inherited_flags._imageRendering = initialImageRendering();
+ svg_inherited_flags._shapeRendering = initialShapeRendering();
+ svg_inherited_flags._textAnchor = initialTextAnchor();
+ svg_inherited_flags._capStyle = initialCapStyle();
+ svg_inherited_flags._joinStyle = initialJoinStyle();
+ svg_inherited_flags._colorInterpolation = initialColorInterpolation();
+ svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters();
+ svg_inherited_flags._writingMode = initialWritingMode();
+ svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal();
+ svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical();
+
+ svg_noninherited_flags._niflags = 0;
+ svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline();
+ svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline();
+ svg_noninherited_flags.f._baselineShift = initialBaselineShift();
+ svg_noninherited_flags.f._vectorEffect = initialVectorEffect();
+ }
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // SVGRenderStyle_h
diff --git a/Source/WebCore/rendering/style/SVGRenderStyleDefs.cpp b/Source/WebCore/rendering/style/SVGRenderStyleDefs.cpp
new file mode 100644
index 0000000..c30ae6d
--- /dev/null
+++ b/Source/WebCore/rendering/style/SVGRenderStyleDefs.cpp
@@ -0,0 +1,227 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2007 Rob Buis <buis@kde.org>
+ Copyright (C) Research In Motion Limited 2010. All rights reserved.
+
+ Based on khtml code by:
+ Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org)
+ Copyright (C) 2002 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"
+
+#if ENABLE(SVG)
+#include "SVGRenderStyleDefs.h"
+
+#include "RenderStyle.h"
+#include "SVGRenderStyle.h"
+
+namespace WebCore {
+
+StyleFillData::StyleFillData()
+ : opacity(SVGRenderStyle::initialFillOpacity())
+ , paint(SVGRenderStyle::initialFillPaint())
+{
+}
+
+StyleFillData::StyleFillData(const StyleFillData& other)
+ : RefCounted<StyleFillData>()
+ , opacity(other.opacity)
+ , paint(other.paint)
+{
+}
+
+bool StyleFillData::operator==(const StyleFillData& other) const
+{
+ if (opacity != other.opacity)
+ return false;
+
+ if (!paint || !other.paint)
+ return paint == other.paint;
+
+ if (paint->paintType() != other.paint->paintType())
+ return false;
+
+ if (paint->paintType() == SVGPaint::SVG_PAINTTYPE_URI)
+ return paint->uri() == other.paint->uri();
+
+ if (paint->paintType() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR)
+ return paint->color() == other.paint->color();
+
+ return paint == other.paint;
+}
+
+StyleStrokeData::StyleStrokeData()
+ : opacity(SVGRenderStyle::initialStrokeOpacity())
+ , miterLimit(SVGRenderStyle::initialStrokeMiterLimit())
+ , width(SVGRenderStyle::initialStrokeWidth())
+ , dashOffset(SVGRenderStyle::initialStrokeDashOffset())
+ , dashArray(SVGRenderStyle::initialStrokeDashArray())
+ , paint(SVGRenderStyle::initialStrokePaint())
+{
+}
+
+StyleStrokeData::StyleStrokeData(const StyleStrokeData& other)
+ : RefCounted<StyleStrokeData>()
+ , opacity(other.opacity)
+ , miterLimit(other.miterLimit)
+ , width(other.width)
+ , dashOffset(other.dashOffset)
+ , dashArray(other.dashArray)
+ , paint(other.paint)
+{
+}
+
+bool StyleStrokeData::operator==(const StyleStrokeData& other) const
+{
+ return paint == other.paint
+ && width == other.width
+ && opacity == other.opacity
+ && miterLimit == other.miterLimit
+ && dashOffset == other.dashOffset
+ && dashArray == other.dashArray;
+}
+
+StyleStopData::StyleStopData()
+ : opacity(SVGRenderStyle::initialStopOpacity())
+ , color(SVGRenderStyle::initialStopColor())
+{
+}
+
+StyleStopData::StyleStopData(const StyleStopData& other)
+ : RefCounted<StyleStopData>()
+ , opacity(other.opacity)
+ , color(other.color)
+{
+}
+
+bool StyleStopData::operator==(const StyleStopData& other) const
+{
+ return color == other.color
+ && opacity == other.opacity;
+}
+
+StyleTextData::StyleTextData()
+ : kerning(SVGRenderStyle::initialKerning())
+{
+}
+
+StyleTextData::StyleTextData(const StyleTextData& other)
+ : RefCounted<StyleTextData>()
+ , kerning(other.kerning)
+{
+}
+
+bool StyleTextData::operator==(const StyleTextData& other) const
+{
+ return kerning == other.kerning;
+}
+
+StyleMiscData::StyleMiscData()
+ : floodColor(SVGRenderStyle::initialFloodColor())
+ , floodOpacity(SVGRenderStyle::initialFloodOpacity())
+ , lightingColor(SVGRenderStyle::initialLightingColor())
+ , baselineShiftValue(SVGRenderStyle::initialBaselineShiftValue())
+{
+}
+
+StyleMiscData::StyleMiscData(const StyleMiscData& other)
+ : RefCounted<StyleMiscData>()
+ , floodColor(other.floodColor)
+ , floodOpacity(other.floodOpacity)
+ , lightingColor(other.lightingColor)
+ , baselineShiftValue(other.baselineShiftValue)
+{
+}
+
+bool StyleMiscData::operator==(const StyleMiscData& other) const
+{
+ return floodOpacity == other.floodOpacity
+ && floodColor == other.floodColor
+ && lightingColor == other.lightingColor
+ && baselineShiftValue == other.baselineShiftValue;
+}
+
+StyleShadowSVGData::StyleShadowSVGData()
+{
+}
+
+StyleShadowSVGData::StyleShadowSVGData(const StyleShadowSVGData& other)
+ : RefCounted<StyleShadowSVGData>()
+ , shadow(other.shadow ? new ShadowData(*other.shadow) : 0)
+{
+}
+
+bool StyleShadowSVGData::operator==(const StyleShadowSVGData& other) const
+{
+ if ((!shadow && other.shadow) || (shadow && !other.shadow))
+ return false;
+ if (shadow && other.shadow && (*shadow != *other.shadow))
+ return false;
+ return true;
+}
+
+StyleResourceData::StyleResourceData()
+ : clipper(SVGRenderStyle::initialClipperResource())
+ , filter(SVGRenderStyle::initialFilterResource())
+ , masker(SVGRenderStyle::initialMaskerResource())
+{
+}
+
+StyleResourceData::StyleResourceData(const StyleResourceData& other)
+ : RefCounted<StyleResourceData>()
+ , clipper(other.clipper)
+ , filter(other.filter)
+ , masker(other.masker)
+{
+}
+
+bool StyleResourceData::operator==(const StyleResourceData& other) const
+{
+ return clipper == other.clipper
+ && filter == other.filter
+ && masker == other.masker;
+}
+
+StyleInheritedResourceData::StyleInheritedResourceData()
+ : markerStart(SVGRenderStyle::initialMarkerStartResource())
+ , markerMid(SVGRenderStyle::initialMarkerMidResource())
+ , markerEnd(SVGRenderStyle::initialMarkerEndResource())
+{
+}
+
+StyleInheritedResourceData::StyleInheritedResourceData(const StyleInheritedResourceData& other)
+ : RefCounted<StyleInheritedResourceData>()
+ , markerStart(other.markerStart)
+ , markerMid(other.markerMid)
+ , markerEnd(other.markerEnd)
+{
+}
+
+bool StyleInheritedResourceData::operator==(const StyleInheritedResourceData& other) const
+{
+ return markerStart == other.markerStart
+ && markerMid == other.markerMid
+ && markerEnd == other.markerEnd;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/style/SVGRenderStyleDefs.h b/Source/WebCore/rendering/style/SVGRenderStyleDefs.h
new file mode 100644
index 0000000..de058a2
--- /dev/null
+++ b/Source/WebCore/rendering/style/SVGRenderStyleDefs.h
@@ -0,0 +1,266 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005 Rob Buis <buis@kde.org>
+ Copyright (C) Research In Motion Limited 2010. All rights reserved.
+
+ Based on khtml code by:
+ Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org)
+ (C) 2000 Antti Koivisto (koivisto@kde.org)
+ (C) 2000-2003 Dirk Mueller (mueller@kde.org)
+ (C) 2002-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 SVGRenderStyleDefs_h
+#define SVGRenderStyleDefs_h
+
+#if ENABLE(SVG)
+#include "Color.h"
+#include "PlatformString.h"
+#include "SVGLength.h"
+#include "ShadowData.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+ enum EBaselineShift {
+ BS_BASELINE, BS_SUB, BS_SUPER, BS_LENGTH
+ };
+
+ enum ETextAnchor {
+ TA_START, TA_MIDDLE, TA_END
+ };
+
+ enum EColorInterpolation {
+ CI_AUTO, CI_SRGB, CI_LINEARRGB
+ };
+
+ enum EColorRendering {
+ CR_AUTO, CR_OPTIMIZESPEED, CR_OPTIMIZEQUALITY
+ };
+
+ enum EImageRendering {
+ IR_AUTO, IR_OPTIMIZESPEED, IR_OPTIMIZEQUALITY
+ };
+
+ enum EShapeRendering {
+ SR_AUTO, SR_OPTIMIZESPEED, SR_CRISPEDGES, SR_GEOMETRICPRECISION
+ };
+
+ enum SVGWritingMode {
+ WM_LRTB, WM_LR, WM_RLTB, WM_RL, WM_TBRL, WM_TB
+ };
+
+ enum EGlyphOrientation {
+ GO_0DEG, GO_90DEG, GO_180DEG, GO_270DEG, GO_AUTO
+ };
+
+ enum EAlignmentBaseline {
+ AB_AUTO, AB_BASELINE, AB_BEFORE_EDGE, AB_TEXT_BEFORE_EDGE,
+ AB_MIDDLE, AB_CENTRAL, AB_AFTER_EDGE, AB_TEXT_AFTER_EDGE,
+ AB_IDEOGRAPHIC, AB_ALPHABETIC, AB_HANGING, AB_MATHEMATICAL
+ };
+
+ enum EDominantBaseline {
+ DB_AUTO, DB_USE_SCRIPT, DB_NO_CHANGE, DB_RESET_SIZE,
+ DB_IDEOGRAPHIC, DB_ALPHABETIC, DB_HANGING, DB_MATHEMATICAL,
+ DB_CENTRAL, DB_MIDDLE, DB_TEXT_AFTER_EDGE, DB_TEXT_BEFORE_EDGE
+ };
+
+ enum EVectorEffect {
+ VE_NONE,
+ VE_NON_SCALING_STROKE
+ };
+
+ class CSSValue;
+ class CSSValueList;
+ class SVGPaint;
+
+ // Inherited/Non-Inherited Style Datastructures
+ class StyleFillData : public RefCounted<StyleFillData> {
+ public:
+ static PassRefPtr<StyleFillData> create() { return adoptRef(new StyleFillData); }
+ PassRefPtr<StyleFillData> copy() const { return adoptRef(new StyleFillData(*this)); }
+
+ bool operator==(const StyleFillData&) const;
+ bool operator!=(const StyleFillData& other) const
+ {
+ return !(*this == other);
+ }
+
+ float opacity;
+ RefPtr<SVGPaint> paint;
+
+ private:
+ StyleFillData();
+ StyleFillData(const StyleFillData&);
+ };
+
+ class StyleStrokeData : public RefCounted<StyleStrokeData> {
+ public:
+ static PassRefPtr<StyleStrokeData> create() { return adoptRef(new StyleStrokeData); }
+ PassRefPtr<StyleStrokeData> copy() const { return adoptRef(new StyleStrokeData(*this)); }
+
+ bool operator==(const StyleStrokeData&) const;
+ bool operator!=(const StyleStrokeData& other) const
+ {
+ return !(*this == other);
+ }
+
+ float opacity;
+ float miterLimit;
+
+ SVGLength width;
+ SVGLength dashOffset;
+ Vector<SVGLength> dashArray;
+
+ RefPtr<SVGPaint> paint;
+
+ private:
+ StyleStrokeData();
+ StyleStrokeData(const StyleStrokeData&);
+ };
+
+ class StyleStopData : public RefCounted<StyleStopData> {
+ public:
+ static PassRefPtr<StyleStopData> create() { return adoptRef(new StyleStopData); }
+ PassRefPtr<StyleStopData> copy() const { return adoptRef(new StyleStopData(*this)); }
+
+ bool operator==(const StyleStopData&) const;
+ bool operator!=(const StyleStopData& other) const
+ {
+ return !(*this == other);
+ }
+
+ float opacity;
+ Color color;
+
+ private:
+ StyleStopData();
+ StyleStopData(const StyleStopData&);
+ };
+
+ class StyleTextData : public RefCounted<StyleTextData> {
+ public:
+ static PassRefPtr<StyleTextData> create() { return adoptRef(new StyleTextData); }
+ PassRefPtr<StyleTextData> copy() const { return adoptRef(new StyleTextData(*this)); }
+
+ bool operator==(const StyleTextData& other) const;
+ bool operator!=(const StyleTextData& other) const
+ {
+ return !(*this == other);
+ }
+
+ SVGLength kerning;
+
+ private:
+ StyleTextData();
+ StyleTextData(const StyleTextData&);
+ };
+
+ // Note: the rule for this class is, *no inheritance* of these props
+ class StyleMiscData : public RefCounted<StyleMiscData> {
+ public:
+ static PassRefPtr<StyleMiscData> create() { return adoptRef(new StyleMiscData); }
+ PassRefPtr<StyleMiscData> copy() const { return adoptRef(new StyleMiscData(*this)); }
+
+ bool operator==(const StyleMiscData&) const;
+ bool operator!=(const StyleMiscData& other) const
+ {
+ return !(*this == other);
+ }
+
+ Color floodColor;
+ float floodOpacity;
+ Color lightingColor;
+
+ // non-inherited text stuff lives here not in StyleTextData.
+ SVGLength baselineShiftValue;
+
+ private:
+ StyleMiscData();
+ StyleMiscData(const StyleMiscData&);
+ };
+
+ class StyleShadowSVGData : public RefCounted<StyleShadowSVGData> {
+ public:
+ static PassRefPtr<StyleShadowSVGData> create() { return adoptRef(new StyleShadowSVGData); }
+ PassRefPtr<StyleShadowSVGData> copy() const { return adoptRef(new StyleShadowSVGData(*this)); }
+
+ bool operator==(const StyleShadowSVGData&) const;
+ bool operator!=(const StyleShadowSVGData& other) const
+ {
+ return !(*this == other);
+ }
+
+ OwnPtr<ShadowData> shadow;
+
+ private:
+ StyleShadowSVGData();
+ StyleShadowSVGData(const StyleShadowSVGData&);
+ };
+
+ // Non-inherited resources
+ class StyleResourceData : public RefCounted<StyleResourceData> {
+ public:
+ static PassRefPtr<StyleResourceData> create() { return adoptRef(new StyleResourceData); }
+ PassRefPtr<StyleResourceData> copy() const { return adoptRef(new StyleResourceData(*this)); }
+
+ bool operator==(const StyleResourceData&) const;
+ bool operator!=(const StyleResourceData& other) const
+ {
+ return !(*this == other);
+ }
+
+ String clipper;
+ String filter;
+ String masker;
+
+ private:
+ StyleResourceData();
+ StyleResourceData(const StyleResourceData&);
+ };
+
+ // Inherited resources
+ class StyleInheritedResourceData : public RefCounted<StyleInheritedResourceData> {
+ public:
+ static PassRefPtr<StyleInheritedResourceData> create() { return adoptRef(new StyleInheritedResourceData); }
+ PassRefPtr<StyleInheritedResourceData> copy() const { return adoptRef(new StyleInheritedResourceData(*this)); }
+
+ bool operator==(const StyleInheritedResourceData&) const;
+ bool operator!=(const StyleInheritedResourceData& other) const
+ {
+ return !(*this == other);
+ }
+
+ String markerStart;
+ String markerMid;
+ String markerEnd;
+
+ private:
+ StyleInheritedResourceData();
+ StyleInheritedResourceData(const StyleInheritedResourceData&);
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+
+#endif // SVGRenderStyleDefs_h
diff --git a/Source/WebCore/rendering/style/ShadowData.cpp b/Source/WebCore/rendering/style/ShadowData.cpp
new file mode 100644
index 0000000..e8d381c
--- /dev/null
+++ b/Source/WebCore/rendering/style/ShadowData.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 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 "ShadowData.h"
+
+#include "FloatRect.h"
+#include "IntRect.h"
+
+using namespace std;
+
+namespace WebCore {
+
+ShadowData::ShadowData(const ShadowData& o)
+ : m_x(o.m_x)
+ , m_y(o.m_y)
+ , m_blur(o.m_blur)
+ , m_spread(o.m_spread)
+ , m_color(o.m_color)
+ , m_style(o.m_style)
+ , m_isWebkitBoxShadow(o.m_isWebkitBoxShadow)
+{
+ m_next = o.m_next ? new ShadowData(*o.m_next) : 0;
+}
+
+bool ShadowData::operator==(const ShadowData& o) const
+{
+ if ((m_next && !o.m_next) || (!m_next && o.m_next)
+ || (m_next && o.m_next && *m_next != *o.m_next))
+ return false;
+
+ return m_x == o.m_x
+ && m_y == o.m_y
+ && m_blur == o.m_blur
+ && m_spread == o.m_spread
+ && m_style == o.m_style
+ && m_color == o.m_color
+ && m_isWebkitBoxShadow == o.m_isWebkitBoxShadow;
+}
+
+static inline void calculateShadowExtent(const ShadowData* shadow, int additionalOutlineSize, int& shadowLeft, int& shadowRight, int& shadowTop, int& shadowBottom)
+{
+ do {
+ int blurAndSpread = shadow->blur() + shadow->spread() + additionalOutlineSize;
+ if (shadow->style() == Normal) {
+ shadowLeft = min(shadow->x() - blurAndSpread, shadowLeft);
+ shadowRight = max(shadow->x() + blurAndSpread, shadowRight);
+ shadowTop = min(shadow->y() - blurAndSpread, shadowTop);
+ shadowBottom = max(shadow->y() + blurAndSpread, shadowBottom);
+ }
+
+ shadow = shadow->next();
+ } while (shadow);
+}
+
+void ShadowData::adjustRectForShadow(IntRect& rect, int additionalOutlineSize) const
+{
+ int shadowLeft = 0;
+ int shadowRight = 0;
+ int shadowTop = 0;
+ int shadowBottom = 0;
+ calculateShadowExtent(this, additionalOutlineSize, shadowLeft, shadowRight, shadowTop, shadowBottom);
+
+ rect.move(shadowLeft, shadowTop);
+ rect.setWidth(rect.width() - shadowLeft + shadowRight);
+ rect.setHeight(rect.height() - shadowTop + shadowBottom);
+}
+
+void ShadowData::adjustRectForShadow(FloatRect& rect, int additionalOutlineSize) const
+{
+ int shadowLeft = 0;
+ int shadowRight = 0;
+ int shadowTop = 0;
+ int shadowBottom = 0;
+ calculateShadowExtent(this, additionalOutlineSize, shadowLeft, shadowRight, shadowTop, shadowBottom);
+
+ rect.move(shadowLeft, shadowTop);
+ rect.setWidth(rect.width() - shadowLeft + shadowRight);
+ rect.setHeight(rect.height() - shadowTop + shadowBottom);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/ShadowData.h b/Source/WebCore/rendering/style/ShadowData.h
new file mode 100644
index 0000000..fb5926d
--- /dev/null
+++ b/Source/WebCore/rendering/style/ShadowData.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 ShadowData_h
+#define ShadowData_h
+
+#include "Color.h"
+#include <wtf/FastAllocBase.h>
+
+namespace WebCore {
+
+class FloatRect;
+class IntRect;
+
+enum ShadowStyle { Normal, Inset };
+
+// This struct holds information about shadows for the text-shadow and box-shadow properties.
+
+class ShadowData : public FastAllocBase {
+public:
+ ShadowData()
+ : m_x(0)
+ , m_y(0)
+ , m_blur(0)
+ , m_spread(0)
+ , m_style(Normal)
+ , m_isWebkitBoxShadow(false)
+ , m_next(0)
+ {
+ }
+
+ ShadowData(int x, int y, int blur, int spread, ShadowStyle style, bool isWebkitBoxShadow, const Color& color)
+ : m_x(x)
+ , m_y(y)
+ , m_blur(blur)
+ , m_spread(spread)
+ , m_color(color)
+ , m_style(style)
+ , m_isWebkitBoxShadow(isWebkitBoxShadow)
+ , m_next(0)
+ {
+ }
+
+ ShadowData(const ShadowData& o);
+ ~ShadowData() { delete m_next; }
+
+ bool operator==(const ShadowData& o) const;
+ bool operator!=(const ShadowData& o) const
+ {
+ return !(*this == o);
+ }
+
+ int x() const { return m_x; }
+ int y() const { return m_y; }
+ int blur() const { return m_blur; }
+ int spread() const { return m_spread; }
+ ShadowStyle style() const { return m_style; }
+ const Color& color() const { return m_color; }
+ bool isWebkitBoxShadow() const { return m_isWebkitBoxShadow; }
+
+ const ShadowData* next() const { return m_next; }
+ void setNext(ShadowData* shadow) { m_next = shadow; }
+
+ void adjustRectForShadow(IntRect&, int additionalOutlineSize = 0) const;
+ void adjustRectForShadow(FloatRect&, int additionalOutlineSize = 0) const;
+
+private:
+ int m_x;
+ int m_y;
+ int m_blur;
+ int m_spread;
+ Color m_color;
+ ShadowStyle m_style;
+ bool m_isWebkitBoxShadow;
+ ShadowData* m_next;
+};
+
+} // namespace WebCore
+
+#endif // ShadowData_h
diff --git a/Source/WebCore/rendering/style/StyleAllInOne.cpp b/Source/WebCore/rendering/style/StyleAllInOne.cpp
new file mode 100644
index 0000000..25b539f
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleAllInOne.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+
+#include "ContentData.cpp"
+#include "CounterDirectives.cpp"
+#include "FillLayer.cpp"
+#include "KeyframeList.cpp"
+#include "NinePieceImage.cpp"
+#include "RenderStyle.cpp"
+#include "SVGRenderStyle.cpp"
+#include "SVGRenderStyleDefs.cpp"
+#include "ShadowData.cpp"
+#include "StyleBackgroundData.cpp"
+#include "StyleBoxData.cpp"
+#include "StyleCachedImage.cpp"
+#include "StyleFlexibleBoxData.cpp"
+#include "StyleGeneratedImage.cpp"
+#include "StyleInheritedData.cpp"
+#include "StyleMarqueeData.cpp"
+#include "StyleMultiColData.cpp"
+#include "StyleRareInheritedData.cpp"
+#include "StyleRareNonInheritedData.cpp"
+#include "StyleSurroundData.cpp"
+#include "StyleTransformData.cpp"
+#include "StyleVisualData.cpp"
diff --git a/Source/WebCore/rendering/style/StyleBackgroundData.cpp b/Source/WebCore/rendering/style/StyleBackgroundData.cpp
new file mode 100644
index 0000000..08f5527
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleBackgroundData.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleBackgroundData.h"
+
+#include "RenderStyle.h"
+#include "RenderStyleConstants.h"
+
+namespace WebCore {
+
+StyleBackgroundData::StyleBackgroundData()
+ : m_background(BackgroundFillLayer)
+ , m_color(RenderStyle::initialBackgroundColor())
+{
+}
+
+StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o)
+ : RefCounted<StyleBackgroundData>()
+ , m_background(o.m_background)
+ , m_color(o.m_color)
+ , m_outline(o.m_outline)
+{
+}
+
+bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const
+{
+ return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleBackgroundData.h b/Source/WebCore/rendering/style/StyleBackgroundData.h
new file mode 100644
index 0000000..48a700e
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleBackgroundData.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleBackgroundData_h
+#define StyleBackgroundData_h
+
+#include "Color.h"
+#include "FillLayer.h"
+#include "OutlineValue.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StyleBackgroundData : public RefCounted<StyleBackgroundData> {
+public:
+ static PassRefPtr<StyleBackgroundData> create() { return adoptRef(new StyleBackgroundData); }
+ PassRefPtr<StyleBackgroundData> copy() const { return adoptRef(new StyleBackgroundData(*this)); }
+ ~StyleBackgroundData() { }
+
+ bool operator==(const StyleBackgroundData& o) const;
+ bool operator!=(const StyleBackgroundData& o) const
+ {
+ return !(*this == o);
+ }
+
+ const FillLayer& background() const { return m_background; }
+ const Color& color() const { return m_color; }
+ const OutlineValue& outline() const { return m_outline; }
+
+private:
+ friend class RenderStyle;
+
+ StyleBackgroundData();
+ StyleBackgroundData(const StyleBackgroundData&);
+
+ FillLayer m_background;
+ Color m_color;
+ OutlineValue m_outline;
+};
+
+} // namespace WebCore
+
+#endif // StyleBackgroundData_h
diff --git a/Source/WebCore/rendering/style/StyleBoxData.cpp b/Source/WebCore/rendering/style/StyleBoxData.cpp
new file mode 100644
index 0000000..2c523da
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleBoxData.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleBoxData.h"
+
+#include "RenderStyle.h"
+#include "RenderStyleConstants.h"
+
+namespace WebCore {
+
+StyleBoxData::StyleBoxData()
+ : m_minWidth(RenderStyle::initialMinSize())
+ , m_maxWidth(RenderStyle::initialMaxSize())
+ , m_minHeight(RenderStyle::initialMinSize())
+ , m_maxHeight(RenderStyle::initialMaxSize())
+ , m_zIndex(0)
+ , m_hasAutoZIndex(true)
+ , m_boxSizing(CONTENT_BOX)
+{
+}
+
+StyleBoxData::StyleBoxData(const StyleBoxData& o)
+ : RefCounted<StyleBoxData>()
+ , m_width(o.m_width)
+ , m_height(o.m_height)
+ , m_minWidth(o.m_minWidth)
+ , m_maxWidth(o.m_maxWidth)
+ , m_minHeight(o.m_minHeight)
+ , m_maxHeight(o.m_maxHeight)
+ , m_zIndex(o.m_zIndex)
+ , m_hasAutoZIndex(o.m_hasAutoZIndex)
+ , m_boxSizing(o.m_boxSizing)
+{
+}
+
+bool StyleBoxData::operator==(const StyleBoxData& o) const
+{
+ return m_width == o.m_width
+ && m_height == o.m_height
+ && m_minWidth == o.m_minWidth
+ && m_maxWidth == o.m_maxWidth
+ && m_minHeight == o.m_minHeight
+ && m_maxHeight == o.m_maxHeight
+ && m_zIndex == o.m_zIndex
+ && m_hasAutoZIndex == o.m_hasAutoZIndex
+ && m_boxSizing == o.m_boxSizing;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleBoxData.h b/Source/WebCore/rendering/style/StyleBoxData.h
new file mode 100644
index 0000000..00bce4e
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleBoxData.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleBoxData_h
+#define StyleBoxData_h
+
+#include "Length.h"
+#include "RenderStyleConstants.h"
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class StyleBoxData : public RefCounted<StyleBoxData> {
+public:
+ static PassRefPtr<StyleBoxData> create() { return adoptRef(new StyleBoxData); }
+ PassRefPtr<StyleBoxData> copy() const { return adoptRef(new StyleBoxData(*this)); }
+
+ bool operator==(const StyleBoxData& o) const;
+ bool operator!=(const StyleBoxData& o) const
+ {
+ return !(*this == o);
+ }
+
+ Length width() const { return m_width; }
+ Length height() const { return m_height; }
+
+ Length minWidth() const { return m_minWidth; }
+ Length minHeight() const { return m_minHeight; }
+
+ Length maxWidth() const { return m_maxWidth; }
+ Length maxHeight() const { return m_maxHeight; }
+
+ Length verticalAlign() const { return m_verticalAlign; }
+
+ int zIndex() const { return m_zIndex; }
+ bool hasAutoZIndex() const { return m_hasAutoZIndex; }
+
+ EBoxSizing boxSizing() const { return static_cast<EBoxSizing>(m_boxSizing); }
+
+private:
+ friend class RenderStyle;
+
+ StyleBoxData();
+ StyleBoxData(const StyleBoxData&);
+
+ Length m_width;
+ Length m_height;
+
+ Length m_minWidth;
+ Length m_maxWidth;
+
+ Length m_minHeight;
+ Length m_maxHeight;
+
+ Length m_verticalAlign;
+
+ int m_zIndex;
+ bool m_hasAutoZIndex : 1;
+ unsigned m_boxSizing : 1; // EBoxSizing
+};
+
+} // namespace WebCore
+
+#endif // StyleBoxData_h
diff --git a/Source/WebCore/rendering/style/StyleCachedImage.cpp b/Source/WebCore/rendering/style/StyleCachedImage.cpp
new file mode 100644
index 0000000..1d7aba8
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleCachedImage.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 "StyleCachedImage.h"
+
+#include "CachedImage.h"
+#include "RenderObject.h"
+
+namespace WebCore {
+
+PassRefPtr<CSSValue> StyleCachedImage::cssValue() const
+{
+ return CSSPrimitiveValue::create(m_image->url(), CSSPrimitiveValue::CSS_URI);
+}
+
+bool StyleCachedImage::canRender(float multiplier) const
+{
+ return m_image->canRender(multiplier);
+}
+
+bool StyleCachedImage::isLoaded() const
+{
+ return m_image->isLoaded();
+}
+
+bool StyleCachedImage::errorOccurred() const
+{
+ return m_image->errorOccurred();
+}
+
+IntSize StyleCachedImage::imageSize(const RenderObject* /*renderer*/, float multiplier) const
+{
+ return m_image->imageSize(multiplier);
+}
+
+bool StyleCachedImage::imageHasRelativeWidth() const
+{
+ return m_image->imageHasRelativeWidth();
+}
+
+bool StyleCachedImage::imageHasRelativeHeight() const
+{
+ return m_image->imageHasRelativeHeight();
+}
+
+bool StyleCachedImage::usesImageContainerSize() const
+{
+ return m_image->usesImageContainerSize();
+}
+
+void StyleCachedImage::setImageContainerSize(const IntSize& size)
+{
+ return m_image->setImageContainerSize(size);
+}
+
+void StyleCachedImage::addClient(RenderObject* renderer)
+{
+ return m_image->addClient(renderer);
+}
+
+void StyleCachedImage::removeClient(RenderObject* renderer)
+{
+ return m_image->removeClient(renderer);
+}
+
+Image* StyleCachedImage::image(RenderObject*, const IntSize&) const
+{
+ return m_image->image();
+}
+
+}
diff --git a/Source/WebCore/rendering/style/StyleCachedImage.h b/Source/WebCore/rendering/style/StyleCachedImage.h
new file mode 100644
index 0000000..3d6e1a2
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleCachedImage.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 StyleCachedImage_h
+#define StyleCachedImage_h
+
+#include "CachedResourceHandle.h"
+#include "StyleImage.h"
+
+namespace WebCore {
+
+class CachedImage;
+
+class StyleCachedImage : public StyleImage {
+public:
+ static PassRefPtr<StyleCachedImage> create(CachedImage* image) { return adoptRef(new StyleCachedImage(image)); }
+ virtual WrappedImagePtr data() const { return m_image.get(); }
+
+ virtual bool isCachedImage() const { return true; }
+
+ virtual PassRefPtr<CSSValue> cssValue() const;
+
+ CachedImage* cachedImage() const { return m_image.get(); }
+
+ virtual bool canRender(float multiplier) const;
+ virtual bool isLoaded() const;
+ virtual bool errorOccurred() const;
+ virtual IntSize imageSize(const RenderObject*, float multiplier) const;
+ virtual bool imageHasRelativeWidth() const;
+ virtual bool imageHasRelativeHeight() const;
+ virtual bool usesImageContainerSize() const;
+ virtual void setImageContainerSize(const IntSize&);
+ virtual void addClient(RenderObject*);
+ virtual void removeClient(RenderObject*);
+ virtual Image* image(RenderObject*, const IntSize&) const;
+
+private:
+ StyleCachedImage(CachedImage* image)
+ : m_image(image)
+ {
+ }
+
+ CachedResourceHandle<CachedImage> m_image;
+};
+
+}
+#endif
diff --git a/Source/WebCore/rendering/style/StyleDashboardRegion.h b/Source/WebCore/rendering/style/StyleDashboardRegion.h
new file mode 100644
index 0000000..bbb0cda
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleDashboardRegion.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleDashboardRegion_h
+#define StyleDashboardRegion_h
+#if ENABLE(DASHBOARD_SUPPORT)
+
+#include "LengthBox.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+// Dashboard region attributes. Not inherited.
+
+struct StyleDashboardRegion {
+ String label;
+ LengthBox offset;
+ int type;
+
+ enum {
+ None,
+ Circle,
+ Rectangle
+ };
+
+ bool operator==(const StyleDashboardRegion& o) const
+ {
+ return type == o.type && offset == o.offset && label == o.label;
+ }
+
+ bool operator!=(const StyleDashboardRegion& o) const
+ {
+ return !(*this == o);
+ }
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(DASHBOARD_SUPPORT)
+#endif // StyleDashboardRegion_h
diff --git a/Source/WebCore/rendering/style/StyleFlexibleBoxData.cpp b/Source/WebCore/rendering/style/StyleFlexibleBoxData.cpp
new file mode 100644
index 0000000..7c00080
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleFlexibleBoxData.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleFlexibleBoxData.h"
+
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+StyleFlexibleBoxData::StyleFlexibleBoxData()
+ : flex(RenderStyle::initialBoxFlex())
+ , flex_group(RenderStyle::initialBoxFlexGroup())
+ , ordinal_group(RenderStyle::initialBoxOrdinalGroup())
+ , align(RenderStyle::initialBoxAlign())
+ , pack(RenderStyle::initialBoxPack())
+ , orient(RenderStyle::initialBoxOrient())
+ , lines(RenderStyle::initialBoxLines())
+{
+}
+
+StyleFlexibleBoxData::StyleFlexibleBoxData(const StyleFlexibleBoxData& o)
+ : RefCounted<StyleFlexibleBoxData>()
+ , flex(o.flex)
+ , flex_group(o.flex_group)
+ , ordinal_group(o.ordinal_group)
+ , align(o.align)
+ , pack(o.pack)
+ , orient(o.orient)
+ , lines(o.lines)
+{
+}
+
+bool StyleFlexibleBoxData::operator==(const StyleFlexibleBoxData& o) const
+{
+ return flex == o.flex && flex_group == o.flex_group &&
+ ordinal_group == o.ordinal_group && align == o.align &&
+ pack == o.pack && orient == o.orient && lines == o.lines;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleFlexibleBoxData.h b/Source/WebCore/rendering/style/StyleFlexibleBoxData.h
new file mode 100644
index 0000000..f5d5e74
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleFlexibleBoxData.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleFlexibleBoxData_h
+#define StyleFlexibleBoxData_h
+
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class StyleFlexibleBoxData : public RefCounted<StyleFlexibleBoxData> {
+public:
+ static PassRefPtr<StyleFlexibleBoxData> create() { return adoptRef(new StyleFlexibleBoxData); }
+ PassRefPtr<StyleFlexibleBoxData> copy() const { return adoptRef(new StyleFlexibleBoxData(*this)); }
+
+ bool operator==(const StyleFlexibleBoxData& o) const;
+ bool operator!=(const StyleFlexibleBoxData& o) const
+ {
+ return !(*this == o);
+ }
+
+ float flex;
+ unsigned int flex_group;
+ unsigned int ordinal_group;
+
+ unsigned align : 3; // EBoxAlignment
+ unsigned pack: 3; // EBoxAlignment
+ unsigned orient: 1; // EBoxOrient
+ unsigned lines : 1; // EBoxLines
+
+private:
+ StyleFlexibleBoxData();
+ StyleFlexibleBoxData(const StyleFlexibleBoxData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleFlexibleBoxData_h
diff --git a/Source/WebCore/rendering/style/StyleGeneratedImage.cpp b/Source/WebCore/rendering/style/StyleGeneratedImage.cpp
new file mode 100644
index 0000000..2322f5f
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleGeneratedImage.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 "StyleGeneratedImage.h"
+
+#include "CSSImageGeneratorValue.h"
+#include "RenderObject.h"
+
+namespace WebCore {
+
+PassRefPtr<CSSValue> StyleGeneratedImage::cssValue() const
+{
+ return m_generator;
+}
+
+IntSize StyleGeneratedImage::imageSize(const RenderObject* renderer, float multiplier) const
+{
+ if (m_fixedSize) {
+ IntSize fixedSize = m_generator->fixedSize(renderer);
+ if (multiplier == 1.0f)
+ return fixedSize;
+
+ int width = fixedSize.width() * multiplier;
+ int height = fixedSize.height() * multiplier;
+
+ // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
+ if (fixedSize.width() > 0)
+ width = max(1, width);
+
+ if (fixedSize.height() > 0)
+ height = max(1, height);
+
+ return IntSize(width, height);
+ }
+
+ return m_containerSize;
+}
+
+void StyleGeneratedImage::setImageContainerSize(const IntSize& size)
+{
+ m_containerSize = size;
+}
+
+void StyleGeneratedImage::addClient(RenderObject* renderer)
+{
+ m_generator->addClient(renderer, IntSize());
+}
+
+void StyleGeneratedImage::removeClient(RenderObject* renderer)
+{
+ m_generator->removeClient(renderer);
+}
+
+Image* StyleGeneratedImage::image(RenderObject* renderer, const IntSize& size) const
+{
+ return m_generator->image(renderer, size);
+}
+
+}
diff --git a/Source/WebCore/rendering/style/StyleGeneratedImage.h b/Source/WebCore/rendering/style/StyleGeneratedImage.h
new file mode 100644
index 0000000..7be1f6a
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleGeneratedImage.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 StyleGeneratedImage_h
+#define StyleGeneratedImage_h
+
+#include "StyleImage.h"
+
+namespace WebCore {
+
+class CSSValue;
+class CSSImageGeneratorValue;
+
+class StyleGeneratedImage : public StyleImage {
+public:
+ static PassRefPtr<StyleGeneratedImage> create(CSSImageGeneratorValue* val, bool fixedSize)
+ {
+ return adoptRef(new StyleGeneratedImage(val, fixedSize));
+ }
+
+ virtual WrappedImagePtr data() const { return m_generator; }
+
+ virtual bool isGeneratedImage() const { return true; }
+
+ virtual PassRefPtr<CSSValue> cssValue() const;
+
+ virtual IntSize imageSize(const RenderObject*, float multiplier) const;
+ virtual bool imageHasRelativeWidth() const { return !m_fixedSize; }
+ virtual bool imageHasRelativeHeight() const { return !m_fixedSize; }
+ virtual bool usesImageContainerSize() const { return !m_fixedSize; }
+ virtual void setImageContainerSize(const IntSize&);
+ virtual void addClient(RenderObject*);
+ virtual void removeClient(RenderObject*);
+ virtual Image* image(RenderObject*, const IntSize&) const;
+
+private:
+ StyleGeneratedImage(CSSImageGeneratorValue* val, bool fixedSize)
+ : m_generator(val)
+ , m_fixedSize(fixedSize)
+ {
+ }
+
+ CSSImageGeneratorValue* m_generator; // The generator holds a reference to us.
+ IntSize m_containerSize;
+ bool m_fixedSize;
+};
+
+}
+#endif
diff --git a/Source/WebCore/rendering/style/StyleImage.h b/Source/WebCore/rendering/style/StyleImage.h
new file mode 100644
index 0000000..ead8d4a
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleImage.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 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 StyleImage_h
+#define StyleImage_h
+
+#include "CSSValue.h"
+#include "IntSize.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class CSSValue;
+class Image;
+class RenderObject;
+
+typedef void* WrappedImagePtr;
+
+class StyleImage : public RefCounted<StyleImage> {
+public:
+ virtual ~StyleImage() { }
+
+ bool operator==(const StyleImage& other) const
+ {
+ return data() == other.data();
+ }
+
+ virtual PassRefPtr<CSSValue> cssValue() const = 0;
+
+ virtual bool canRender(float /*multiplier*/) const { return true; }
+ virtual bool isLoaded() const { return true; }
+ virtual bool errorOccurred() const { return false; }
+ virtual IntSize imageSize(const RenderObject*, float multiplier) const = 0;
+ virtual bool imageHasRelativeWidth() const = 0;
+ virtual bool imageHasRelativeHeight() const = 0;
+ virtual bool usesImageContainerSize() const = 0;
+ virtual void setImageContainerSize(const IntSize&) = 0;
+ virtual void addClient(RenderObject*) = 0;
+ virtual void removeClient(RenderObject*) = 0;
+ virtual Image* image(RenderObject*, const IntSize&) const = 0;
+ virtual WrappedImagePtr data() const = 0;
+
+ virtual bool isCachedImage() const { return false; }
+ virtual bool isPendingImage() const { return false; }
+ virtual bool isGeneratedImage() const { return false; }
+
+ static bool imagesEquivalent(StyleImage* image1, StyleImage* image2)
+ {
+ if (image1 != image2) {
+ if (!image1 || !image2)
+ return false;
+ return *image1 == *image2;
+ }
+ return true;
+ }
+
+protected:
+ StyleImage() { }
+};
+
+}
+#endif
diff --git a/Source/WebCore/rendering/style/StyleInheritedData.cpp b/Source/WebCore/rendering/style/StyleInheritedData.cpp
new file mode 100644
index 0000000..874d053
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleInheritedData.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleRareInheritedData.h"
+
+#include "RenderStyle.h"
+#include "StyleImage.h"
+
+namespace WebCore {
+
+StyleInheritedData::StyleInheritedData()
+ : line_height(RenderStyle::initialLineHeight())
+ , list_style_image(RenderStyle::initialListStyleImage())
+ , color(RenderStyle::initialColor())
+ , horizontal_border_spacing(RenderStyle::initialHorizontalBorderSpacing())
+ , vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing())
+{
+}
+
+StyleInheritedData::~StyleInheritedData()
+{
+}
+
+StyleInheritedData::StyleInheritedData(const StyleInheritedData& o)
+ : RefCounted<StyleInheritedData>()
+ , line_height(o.line_height)
+ , list_style_image(o.list_style_image)
+ , font(o.font)
+ , color(o.color)
+ , horizontal_border_spacing(o.horizontal_border_spacing)
+ , vertical_border_spacing(o.vertical_border_spacing)
+{
+}
+
+bool StyleInheritedData::operator==(const StyleInheritedData& o) const
+{
+ return
+ line_height == o.line_height &&
+ StyleImage::imagesEquivalent(list_style_image.get(), o.list_style_image.get()) &&
+ font == o.font &&
+ color == o.color &&
+ horizontal_border_spacing == o.horizontal_border_spacing &&
+ vertical_border_spacing == o.vertical_border_spacing;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleInheritedData.h b/Source/WebCore/rendering/style/StyleInheritedData.h
new file mode 100644
index 0000000..ea398db
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleInheritedData.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleInheritedData_h
+#define StyleInheritedData_h
+
+#include "Color.h"
+#include "Font.h"
+#include "Length.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class StyleImage;
+
+class StyleInheritedData : public RefCounted<StyleInheritedData> {
+public:
+ static PassRefPtr<StyleInheritedData> create() { return adoptRef(new StyleInheritedData); }
+ PassRefPtr<StyleInheritedData> copy() const { return adoptRef(new StyleInheritedData(*this)); }
+ ~StyleInheritedData();
+
+ bool operator==(const StyleInheritedData& o) const;
+ bool operator!=(const StyleInheritedData& o) const
+ {
+ return !(*this == o);
+ }
+
+ // could be packed in a short but doesn't
+ // make a difference currently because of padding
+ Length line_height;
+
+ RefPtr<StyleImage> list_style_image;
+
+ Font font;
+ Color color;
+
+ short horizontal_border_spacing;
+ short vertical_border_spacing;
+private:
+ StyleInheritedData();
+ StyleInheritedData(const StyleInheritedData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleInheritedData_h
diff --git a/Source/WebCore/rendering/style/StyleMarqueeData.cpp b/Source/WebCore/rendering/style/StyleMarqueeData.cpp
new file mode 100644
index 0000000..f0e824d
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleMarqueeData.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleBackgroundData.h"
+
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+StyleMarqueeData::StyleMarqueeData()
+ : increment(RenderStyle::initialMarqueeIncrement())
+ , speed(RenderStyle::initialMarqueeSpeed())
+ , loops(RenderStyle::initialMarqueeLoopCount())
+ , behavior(RenderStyle::initialMarqueeBehavior())
+ , direction(RenderStyle::initialMarqueeDirection())
+{
+}
+
+StyleMarqueeData::StyleMarqueeData(const StyleMarqueeData& o)
+ : RefCounted<StyleMarqueeData>()
+ , increment(o.increment)
+ , speed(o.speed)
+ , loops(o.loops)
+ , behavior(o.behavior)
+ , direction(o.direction)
+{
+}
+
+bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const
+{
+ return increment == o.increment && speed == o.speed && direction == o.direction &&
+ behavior == o.behavior && loops == o.loops;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleMarqueeData.h b/Source/WebCore/rendering/style/StyleMarqueeData.h
new file mode 100644
index 0000000..5765f5d
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleMarqueeData.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleMarqueeData_h
+#define StyleMarqueeData_h
+
+#include "Length.h"
+#include "RenderStyleConstants.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StyleMarqueeData : public RefCounted<StyleMarqueeData> {
+public:
+ static PassRefPtr<StyleMarqueeData> create() { return adoptRef(new StyleMarqueeData); }
+ PassRefPtr<StyleMarqueeData> copy() const { return adoptRef(new StyleMarqueeData(*this)); }
+
+ bool operator==(const StyleMarqueeData& o) const;
+ bool operator!=(const StyleMarqueeData& o) const
+ {
+ return !(*this == o);
+ }
+
+ Length increment;
+ int speed;
+
+ int loops; // -1 means infinite.
+
+ unsigned behavior : 2; // EMarqueeBehavior
+ EMarqueeDirection direction : 3; // not unsigned because EMarqueeDirection has negative values
+
+private:
+ StyleMarqueeData();
+ StyleMarqueeData(const StyleMarqueeData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleMarqueeData_h
diff --git a/Source/WebCore/rendering/style/StyleMultiColData.cpp b/Source/WebCore/rendering/style/StyleMultiColData.cpp
new file mode 100644
index 0000000..3366e9f
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleMultiColData.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleMultiColData.h"
+
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+StyleMultiColData::StyleMultiColData()
+ : m_width(0)
+ , m_count(RenderStyle::initialColumnCount())
+ , m_gap(0)
+ , m_autoWidth(true)
+ , m_autoCount(true)
+ , m_normalGap(true)
+ , m_columnSpan(false)
+ , m_breakBefore(RenderStyle::initialPageBreak())
+ , m_breakAfter(RenderStyle::initialPageBreak())
+ , m_breakInside(RenderStyle::initialPageBreak())
+{
+}
+
+StyleMultiColData::StyleMultiColData(const StyleMultiColData& o)
+ : RefCounted<StyleMultiColData>()
+ , m_width(o.m_width)
+ , m_count(o.m_count)
+ , m_gap(o.m_gap)
+ , m_rule(o.m_rule)
+ , m_autoWidth(o.m_autoWidth)
+ , m_autoCount(o.m_autoCount)
+ , m_normalGap(o.m_normalGap)
+ , m_columnSpan(o.m_columnSpan)
+ , m_breakBefore(o.m_breakBefore)
+ , m_breakAfter(o.m_breakAfter)
+ , m_breakInside(o.m_breakInside)
+{
+}
+
+bool StyleMultiColData::operator==(const StyleMultiColData& o) const
+{
+ return m_width == o.m_width && m_count == o.m_count && m_gap == o.m_gap
+ && m_rule == o.m_rule && m_breakBefore == o.m_breakBefore
+ && m_autoWidth == o.m_autoWidth && m_autoCount == o.m_autoCount && m_normalGap == o.m_normalGap
+ && m_columnSpan == o.m_columnSpan && m_breakAfter == o.m_breakAfter && m_breakInside == o.m_breakInside;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleMultiColData.h b/Source/WebCore/rendering/style/StyleMultiColData.h
new file mode 100644
index 0000000..9948846
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleMultiColData.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleMultiColData_h
+#define StyleMultiColData_h
+
+#include "BorderValue.h"
+#include "Length.h"
+#include "RenderStyleConstants.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+// CSS3 Multi Column Layout
+
+class StyleMultiColData : public RefCounted<StyleMultiColData> {
+public:
+ static PassRefPtr<StyleMultiColData> create() { return adoptRef(new StyleMultiColData); }
+ PassRefPtr<StyleMultiColData> copy() const { return adoptRef(new StyleMultiColData(*this)); }
+
+ bool operator==(const StyleMultiColData& o) const;
+ bool operator!=(const StyleMultiColData &o) const
+ {
+ return !(*this == o);
+ }
+
+ unsigned short ruleWidth() const
+ {
+ if (m_rule.style() == BNONE || m_rule.style() == BHIDDEN)
+ return 0;
+ return m_rule.width();
+ }
+
+ float m_width;
+ unsigned short m_count;
+ float m_gap;
+ BorderValue m_rule;
+
+ bool m_autoWidth : 1;
+ bool m_autoCount : 1;
+ bool m_normalGap : 1;
+ bool m_columnSpan : 1;
+ unsigned m_breakBefore : 2; // EPageBreak
+ unsigned m_breakAfter : 2; // EPageBreak
+ unsigned m_breakInside : 2; // EPageBreak
+
+private:
+ StyleMultiColData();
+ StyleMultiColData(const StyleMultiColData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleMultiColData_h
diff --git a/Source/WebCore/rendering/style/StylePendingImage.h b/Source/WebCore/rendering/style/StylePendingImage.h
new file mode 100644
index 0000000..b0c9b01
--- /dev/null
+++ b/Source/WebCore/rendering/style/StylePendingImage.h
@@ -0,0 +1,71 @@
+/*
+ * 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 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 StylePendingImage_h
+#define StylePendingImage_h
+
+#include "StyleImage.h"
+
+namespace WebCore {
+
+// StylePendingImage is a placeholder StyleImage that is entered into the RenderStyle during
+// style resolution, in order to avoid loading images that are not referenced by the final style.
+// They should never exist in a RenderStyle after it has been returned from the style selector.
+
+class StylePendingImage : public StyleImage {
+public:
+ static PassRefPtr<StylePendingImage> create(CSSImageValue* value) { return adoptRef(new StylePendingImage(value)); }
+
+ virtual WrappedImagePtr data() const { return m_value; }
+
+ virtual bool isPendingImage() const { return true; }
+
+ virtual PassRefPtr<CSSValue> cssValue() const { return m_value; }
+ CSSImageValue* cssImageValue() const { return m_value; }
+
+ virtual IntSize imageSize(const RenderObject*, float /*multiplier*/) const { return IntSize(); }
+ virtual bool imageHasRelativeWidth() const { return false; }
+ virtual bool imageHasRelativeHeight() const { return false; }
+ virtual bool usesImageContainerSize() const { return false; }
+ virtual void setImageContainerSize(const IntSize&) { }
+ virtual void addClient(RenderObject*) { }
+ virtual void removeClient(RenderObject*) { }
+ virtual Image* image(RenderObject*, const IntSize&) const
+ {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+
+private:
+ StylePendingImage(CSSImageValue* value)
+ : m_value(value)
+ {
+ }
+
+ CSSImageValue* m_value; // Not retained; it owns us.
+};
+
+}
+#endif
diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp
new file mode 100644
index 0000000..6138df2
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleRareInheritedData.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@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 "StyleRareInheritedData.h"
+
+#include "RenderStyle.h"
+#include "RenderStyleConstants.h"
+
+namespace WebCore {
+
+StyleRareInheritedData::StyleRareInheritedData()
+ : textStrokeWidth(RenderStyle::initialTextStrokeWidth())
+#ifdef ANDROID_CSS_RING
+ , ringFillColor(RenderStyle::initialRingFillColor())
+ , ringInnerWidth(RenderStyle::initialRingInnerWidth())
+ , ringOuterWidth(RenderStyle::initialRingOuterWidth())
+ , ringOutset(RenderStyle::initialRingOutset())
+ , ringPressedInnerColor(RenderStyle::initialRingPressedInnerColor())
+ , ringPressedOuterColor(RenderStyle::initialRingPressedOuterColor())
+ , ringRadius(RenderStyle::initialRingRadius())
+ , ringSelectedInnerColor(RenderStyle::initialRingSelectedInnerColor())
+ , ringSelectedOuterColor(RenderStyle::initialRingSelectedOuterColor())
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ , tapHighlightColor(RenderStyle::initialTapHighlightColor())
+#endif
+ , textShadow(0)
+ , indent(RenderStyle::initialTextIndent())
+ , m_effectiveZoom(RenderStyle::initialZoom())
+ , widows(RenderStyle::initialWidows())
+ , orphans(RenderStyle::initialOrphans())
+ , textSecurity(RenderStyle::initialTextSecurity())
+ , userModify(READ_ONLY)
+ , wordBreak(RenderStyle::initialWordBreak())
+ , wordWrap(RenderStyle::initialWordWrap())
+ , nbspMode(NBNORMAL)
+ , khtmlLineBreak(LBNORMAL)
+ , textSizeAdjust(RenderStyle::initialTextSizeAdjust())
+ , resize(RenderStyle::initialResize())
+ , userSelect(RenderStyle::initialUserSelect())
+ , colorSpace(ColorSpaceDeviceRGB)
+ , speak(SpeakNormal)
+ , hyphens(HyphensManual)
+ , textEmphasisFill(TextEmphasisFillFilled)
+ , textEmphasisMark(TextEmphasisMarkNone)
+ , textEmphasisPosition(TextEmphasisPositionOver)
+{
+}
+
+StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o)
+ : RefCounted<StyleRareInheritedData>()
+ , textStrokeColor(o.textStrokeColor)
+ , textStrokeWidth(o.textStrokeWidth)
+ , textFillColor(o.textFillColor)
+ , textEmphasisColor(o.textEmphasisColor)
+#ifdef ANDROID_CSS_RING
+ , ringFillColor(o.ringFillColor)
+ , ringInnerWidth(o.ringInnerWidth)
+ , ringOuterWidth(o.ringOuterWidth)
+ , ringOutset(o.ringOutset)
+ , ringPressedInnerColor(o.ringPressedInnerColor)
+ , ringPressedOuterColor(o.ringPressedOuterColor)
+ , ringRadius(o.ringRadius)
+ , ringSelectedInnerColor(o.ringSelectedInnerColor)
+ , ringSelectedOuterColor(o.ringSelectedOuterColor)
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ , tapHighlightColor(o.tapHighlightColor)
+#endif
+ , textShadow(o.textShadow ? new ShadowData(*o.textShadow) : 0)
+ , highlight(o.highlight)
+ , cursorData(o.cursorData)
+ , indent(o.indent)
+ , m_effectiveZoom(o.m_effectiveZoom)
+ , widows(o.widows)
+ , orphans(o.orphans)
+ , textSecurity(o.textSecurity)
+ , userModify(o.userModify)
+ , wordBreak(o.wordBreak)
+ , wordWrap(o.wordWrap)
+ , nbspMode(o.nbspMode)
+ , khtmlLineBreak(o.khtmlLineBreak)
+ , textSizeAdjust(o.textSizeAdjust)
+ , resize(o.resize)
+ , userSelect(o.userSelect)
+ , colorSpace(o.colorSpace)
+ , speak(o.speak)
+ , hyphens(o.hyphens)
+ , textEmphasisFill(o.textEmphasisFill)
+ , textEmphasisMark(o.textEmphasisMark)
+ , textEmphasisPosition(o.textEmphasisPosition)
+ , hyphenationString(o.hyphenationString)
+ , hyphenationLocale(o.hyphenationLocale)
+ , textEmphasisCustomMark(o.textEmphasisCustomMark)
+{
+}
+
+StyleRareInheritedData::~StyleRareInheritedData()
+{
+ delete textShadow;
+}
+
+static bool cursorDataEquivalent(const CursorList* c1, const CursorList* c2)
+{
+ if (c1 == c2)
+ return true;
+ if ((!c1 && c2) || (c1 && !c2))
+ return false;
+ return (*c1 == *c2);
+}
+
+bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const
+{
+ return textStrokeColor == o.textStrokeColor
+ && textStrokeWidth == o.textStrokeWidth
+ && textFillColor == o.textFillColor
+ && textEmphasisColor == o.textEmphasisColor
+ && shadowDataEquivalent(o)
+ && highlight == o.highlight
+ && cursorDataEquivalent(cursorData.get(), o.cursorData.get())
+ && indent == o.indent
+ && m_effectiveZoom == o.m_effectiveZoom
+ && widows == o.widows
+ && orphans == o.orphans
+ && textSecurity == o.textSecurity
+ && userModify == o.userModify
+ && wordBreak == o.wordBreak
+ && wordWrap == o.wordWrap
+ && nbspMode == o.nbspMode
+ && khtmlLineBreak == o.khtmlLineBreak
+ && textSizeAdjust == o.textSizeAdjust
+#ifdef ANDROID_CSS_RING
+ && ringFillColor == o.ringFillColor
+ && ringInnerWidth == o.ringInnerWidth
+ && ringOuterWidth == o.ringOuterWidth
+ && ringOutset == o.ringOutset
+ && ringPressedInnerColor == o.ringPressedInnerColor
+ && ringPressedOuterColor == o.ringPressedOuterColor
+ && ringRadius == o.ringRadius
+ && ringSelectedInnerColor == o.ringSelectedInnerColor
+ && ringSelectedOuterColor == o.ringSelectedOuterColor
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ && tapHighlightColor == o.tapHighlightColor
+#endif
+ && resize == o.resize
+ && userSelect == o.userSelect
+ && colorSpace == o.colorSpace
+ && speak == o.speak
+ && hyphens == o.hyphens
+ && textEmphasisFill == o.textEmphasisFill
+ && textEmphasisMark == o.textEmphasisMark
+ && textEmphasisPosition == o.textEmphasisPosition
+ && hyphenationString == o.hyphenationString
+ && hyphenationLocale == o.hyphenationLocale
+ && textEmphasisCustomMark == o.textEmphasisCustomMark;
+}
+
+bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const
+{
+ if ((!textShadow && o.textShadow) || (textShadow && !o.textShadow))
+ return false;
+ if (textShadow && o.textShadow && (*textShadow != *o.textShadow))
+ return false;
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleRareInheritedData.h b/Source/WebCore/rendering/style/StyleRareInheritedData.h
new file mode 100644
index 0000000..a370934
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleRareInheritedData.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleRareInheritedData_h
+#define StyleRareInheritedData_h
+
+#include "Color.h"
+#include "Length.h"
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class CursorList;
+class ShadowData;
+
+// This struct is for rarely used inherited CSS3, CSS2, and WebKit-specific properties.
+// By grouping them together, we save space, and only allocate this object when someone
+// actually uses one of these properties.
+class StyleRareInheritedData : public RefCounted<StyleRareInheritedData> {
+public:
+ static PassRefPtr<StyleRareInheritedData> create() { return adoptRef(new StyleRareInheritedData); }
+ PassRefPtr<StyleRareInheritedData> copy() const { return adoptRef(new StyleRareInheritedData(*this)); }
+ ~StyleRareInheritedData();
+
+ bool operator==(const StyleRareInheritedData& o) const;
+ bool operator!=(const StyleRareInheritedData& o) const
+ {
+ return !(*this == o);
+ }
+ bool shadowDataEquivalent(const StyleRareInheritedData&) const;
+
+ Color textStrokeColor;
+ float textStrokeWidth;
+ Color textFillColor;
+ Color textEmphasisColor;
+
+#ifdef ANDROID_CSS_RING
+ Color ringFillColor;
+ Length ringInnerWidth;
+ Length ringOuterWidth;
+ Length ringOutset;
+ Color ringPressedInnerColor;
+ Color ringPressedOuterColor;
+ Length ringRadius;
+ Color ringSelectedInnerColor;
+ Color ringSelectedOuterColor;
+#endif
+#ifdef ANDROID_CSS_TAP_HIGHLIGHT_COLOR
+ Color tapHighlightColor;
+#endif
+
+ ShadowData* textShadow; // Our text shadow information for shadowed text drawing.
+ AtomicString highlight; // Apple-specific extension for custom highlight rendering.
+
+ RefPtr<CursorList> cursorData;
+ Length indent;
+ float m_effectiveZoom;
+
+ // Paged media properties.
+ short widows;
+ short orphans;
+
+ unsigned textSecurity : 2; // ETextSecurity
+ unsigned userModify : 2; // EUserModify (editing)
+ unsigned wordBreak : 2; // EWordBreak
+ unsigned wordWrap : 1; // EWordWrap
+ unsigned nbspMode : 1; // ENBSPMode
+ unsigned khtmlLineBreak : 1; // EKHTMLLineBreak
+ bool textSizeAdjust : 1; // An Apple extension.
+ unsigned resize : 2; // EResize
+ unsigned userSelect : 1; // EUserSelect
+ unsigned colorSpace : 1; // ColorSpace
+ unsigned speak : 3; // ESpeak
+ unsigned hyphens : 2; // Hyphens
+ unsigned textEmphasisFill : 1; // TextEmphasisFill
+ unsigned textEmphasisMark : 3; // TextEmphasisMark
+ unsigned textEmphasisPosition : 1; // TextEmphasisPosition
+
+ AtomicString hyphenationString;
+ AtomicString hyphenationLocale;
+
+ AtomicString textEmphasisCustomMark;
+
+private:
+ StyleRareInheritedData();
+ StyleRareInheritedData(const StyleRareInheritedData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleRareInheritedData_h
diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
new file mode 100644
index 0000000..42cf966
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleRareNonInheritedData.h"
+
+#include "CSSStyleSelector.h"
+#include "ContentData.h"
+#include "RenderCounter.h"
+#include "RenderStyle.h"
+#include "StyleImage.h"
+
+namespace WebCore {
+
+StyleRareNonInheritedData::StyleRareNonInheritedData()
+ : lineClamp(RenderStyle::initialLineClamp())
+ , opacity(RenderStyle::initialOpacity())
+ , m_content(0)
+ , m_counterDirectives(0)
+ , userDrag(RenderStyle::initialUserDrag())
+ , textOverflow(RenderStyle::initialTextOverflow())
+ , marginBeforeCollapse(MCOLLAPSE)
+ , marginAfterCollapse(MCOLLAPSE)
+ , matchNearestMailBlockquoteColor(RenderStyle::initialMatchNearestMailBlockquoteColor())
+ , m_appearance(RenderStyle::initialAppearance())
+ , m_borderFit(RenderStyle::initialBorderFit())
+ , m_textCombine(RenderStyle::initialTextCombine())
+ , m_counterIncrement(0)
+ , m_counterReset(0)
+#if USE(ACCELERATED_COMPOSITING)
+ , m_runningAcceleratedAnimation(false)
+#endif
+ , m_boxShadow(0)
+ , m_animations(0)
+ , m_transitions(0)
+ , m_mask(FillLayer(MaskFillLayer))
+ , m_transformStyle3D(RenderStyle::initialTransformStyle3D())
+ , m_backfaceVisibility(RenderStyle::initialBackfaceVisibility())
+ , m_perspective(RenderStyle::initialPerspective())
+ , m_perspectiveOriginX(RenderStyle::initialPerspectiveOriginX())
+ , m_perspectiveOriginY(RenderStyle::initialPerspectiveOriginY())
+ , m_pageSize()
+ , m_pageSizeType(PAGE_SIZE_AUTO)
+{
+}
+
+StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInheritedData& o)
+ : RefCounted<StyleRareNonInheritedData>()
+ , lineClamp(o.lineClamp)
+ , opacity(o.opacity)
+ , flexibleBox(o.flexibleBox)
+ , marquee(o.marquee)
+ , m_multiCol(o.m_multiCol)
+ , m_transform(o.m_transform)
+ , m_content(0)
+ , m_counterDirectives(0)
+ , userDrag(o.userDrag)
+ , textOverflow(o.textOverflow)
+ , marginBeforeCollapse(o.marginBeforeCollapse)
+ , marginAfterCollapse(o.marginAfterCollapse)
+ , matchNearestMailBlockquoteColor(o.matchNearestMailBlockquoteColor)
+ , m_appearance(o.m_appearance)
+ , m_borderFit(o.m_borderFit)
+ , m_textCombine(o.m_textCombine)
+ , m_counterIncrement(o.m_counterIncrement)
+ , m_counterReset(o.m_counterReset)
+#if USE(ACCELERATED_COMPOSITING)
+ , m_runningAcceleratedAnimation(o.m_runningAcceleratedAnimation)
+#endif
+ , m_boxShadow(o.m_boxShadow ? new ShadowData(*o.m_boxShadow) : 0)
+ , m_boxReflect(o.m_boxReflect)
+ , m_animations(o.m_animations ? new AnimationList(*o.m_animations) : 0)
+ , m_transitions(o.m_transitions ? new AnimationList(*o.m_transitions) : 0)
+ , m_mask(o.m_mask)
+ , m_maskBoxImage(o.m_maskBoxImage)
+ , m_transformStyle3D(o.m_transformStyle3D)
+ , m_backfaceVisibility(o.m_backfaceVisibility)
+ , m_perspective(o.m_perspective)
+ , m_perspectiveOriginX(o.m_perspectiveOriginX)
+ , m_perspectiveOriginY(o.m_perspectiveOriginY)
+ , m_pageSize(o.m_pageSize)
+ , m_pageSizeType(o.m_pageSizeType)
+{
+}
+
+StyleRareNonInheritedData::~StyleRareNonInheritedData()
+{
+}
+
+bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) const
+{
+ return lineClamp == o.lineClamp
+#if ENABLE(DASHBOARD_SUPPORT)
+ && m_dashboardRegions == o.m_dashboardRegions
+#endif
+ && opacity == o.opacity
+ && flexibleBox == o.flexibleBox
+ && marquee == o.marquee
+ && m_multiCol == o.m_multiCol
+ && m_transform == o.m_transform
+ && contentDataEquivalent(o)
+ && m_counterDirectives == o.m_counterDirectives
+ && userDrag == o.userDrag
+ && textOverflow == o.textOverflow
+ && marginBeforeCollapse == o.marginBeforeCollapse
+ && marginAfterCollapse == o.marginAfterCollapse
+ && matchNearestMailBlockquoteColor == o.matchNearestMailBlockquoteColor
+ && m_appearance == o.m_appearance
+ && m_borderFit == o.m_borderFit
+ && m_textCombine == o.m_textCombine
+ && m_counterIncrement == o.m_counterIncrement
+ && m_counterReset == o.m_counterReset
+#if USE(ACCELERATED_COMPOSITING)
+ && !m_runningAcceleratedAnimation && !o.m_runningAcceleratedAnimation
+#endif
+ && shadowDataEquivalent(o)
+ && reflectionDataEquivalent(o)
+ && animationDataEquivalent(o)
+ && transitionDataEquivalent(o)
+ && m_mask == o.m_mask
+ && m_maskBoxImage == o.m_maskBoxImage
+ && (m_transformStyle3D == o.m_transformStyle3D)
+ && (m_backfaceVisibility == o.m_backfaceVisibility)
+ && (m_perspective == o.m_perspective)
+ && (m_perspectiveOriginX == o.m_perspectiveOriginX)
+ && (m_perspectiveOriginY == o.m_perspectiveOriginY)
+ && (m_pageSize == o.m_pageSize)
+ && (m_pageSizeType == o.m_pageSizeType)
+ ;
+}
+
+bool StyleRareNonInheritedData::contentDataEquivalent(const StyleRareNonInheritedData& o) const
+{
+ ContentData* c1 = m_content.get();
+ ContentData* c2 = o.m_content.get();
+
+ while (c1 && c2) {
+ if (!c1->dataEquivalent(*c2))
+ return false;
+ c1 = c1->next();
+ c2 = c2->next();
+ }
+
+ return !c1 && !c2;
+}
+
+bool StyleRareNonInheritedData::shadowDataEquivalent(const StyleRareNonInheritedData& o) const
+{
+ if ((!m_boxShadow && o.m_boxShadow) || (m_boxShadow && !o.m_boxShadow))
+ return false;
+ if (m_boxShadow && o.m_boxShadow && (*m_boxShadow != *o.m_boxShadow))
+ return false;
+ return true;
+}
+
+bool StyleRareNonInheritedData::reflectionDataEquivalent(const StyleRareNonInheritedData& o) const
+{
+ if (m_boxReflect != o.m_boxReflect) {
+ if (!m_boxReflect || !o.m_boxReflect)
+ return false;
+ return *m_boxReflect == *o.m_boxReflect;
+ }
+ return true;
+
+}
+
+bool StyleRareNonInheritedData::animationDataEquivalent(const StyleRareNonInheritedData& o) const
+{
+ if ((!m_animations && o.m_animations) || (m_animations && !o.m_animations))
+ return false;
+ if (m_animations && o.m_animations && (*m_animations != *o.m_animations))
+ return false;
+ return true;
+}
+
+bool StyleRareNonInheritedData::transitionDataEquivalent(const StyleRareNonInheritedData& o) const
+{
+ if ((!m_transitions && o.m_transitions) || (m_transitions && !o.m_transitions))
+ return false;
+ if (m_transitions && o.m_transitions && (*m_transitions != *o.m_transitions))
+ return false;
+ return true;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleRareNonInheritedData.h b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h
new file mode 100644
index 0000000..89437f6
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleRareNonInheritedData.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleRareNonInheritedData_h
+#define StyleRareNonInheritedData_h
+
+#include "CounterDirectives.h"
+#include "CursorData.h"
+#include "DataRef.h"
+#include "FillLayer.h"
+#include "LineClampValue.h"
+#include "NinePieceImage.h"
+#include "StyleTransformData.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AnimationList;
+class CSSStyleSelector;
+class ShadowData;
+class StyleFlexibleBoxData;
+class StyleMarqueeData;
+class StyleMultiColData;
+class StyleReflection;
+class StyleTransformData;
+
+struct ContentData;
+struct LengthSize;
+
+#if ENABLE(DASHBOARD_SUPPORT)
+struct StyleDashboardRegion;
+#endif
+
+// Page size type.
+// StyleRareNonInheritedData::m_pageSize is meaningful only when
+// StyleRareNonInheritedData::m_pageSizeType is PAGE_SIZE_RESOLVED.
+enum PageSizeType {
+ PAGE_SIZE_AUTO, // size: auto
+ PAGE_SIZE_AUTO_LANDSCAPE, // size: landscape
+ PAGE_SIZE_AUTO_PORTRAIT, // size: portrait
+ PAGE_SIZE_RESOLVED // Size is fully resolved.
+};
+
+// This struct is for rarely used non-inherited CSS3, CSS2, and WebKit-specific properties.
+// By grouping them together, we save space, and only allocate this object when someone
+// actually uses one of these properties.
+class StyleRareNonInheritedData : public RefCounted<StyleRareNonInheritedData> {
+public:
+ static PassRefPtr<StyleRareNonInheritedData> create() { return adoptRef(new StyleRareNonInheritedData); }
+ PassRefPtr<StyleRareNonInheritedData> copy() const { return adoptRef(new StyleRareNonInheritedData(*this)); }
+ ~StyleRareNonInheritedData();
+
+ bool operator==(const StyleRareNonInheritedData&) const;
+ bool operator!=(const StyleRareNonInheritedData& o) const { return !(*this == o); }
+
+ bool contentDataEquivalent(const StyleRareNonInheritedData& o) const;
+ bool shadowDataEquivalent(const StyleRareNonInheritedData& o) const;
+ bool reflectionDataEquivalent(const StyleRareNonInheritedData& o) const;
+ bool animationDataEquivalent(const StyleRareNonInheritedData&) const;
+ bool transitionDataEquivalent(const StyleRareNonInheritedData&) const;
+
+ LineClampValue lineClamp; // An Apple extension.
+#if ENABLE(DASHBOARD_SUPPORT)
+ Vector<StyleDashboardRegion> m_dashboardRegions;
+#endif
+ float opacity; // Whether or not we're transparent.
+
+ DataRef<StyleFlexibleBoxData> flexibleBox; // Flexible box properties
+ DataRef<StyleMarqueeData> marquee; // Marquee properties
+ DataRef<StyleMultiColData> m_multiCol; // CSS3 multicol properties
+ DataRef<StyleTransformData> m_transform; // Transform properties (rotate, scale, skew, etc.)
+
+ OwnPtr<ContentData> m_content;
+ OwnPtr<CounterDirectiveMap> m_counterDirectives;
+
+ unsigned userDrag : 2; // EUserDrag
+ bool textOverflow : 1; // Whether or not lines that spill out should be truncated with "..."
+ unsigned marginBeforeCollapse : 2; // EMarginCollapse
+ unsigned marginAfterCollapse : 2; // EMarginCollapse
+ unsigned matchNearestMailBlockquoteColor : 1; // EMatchNearestMailBlockquoteColor, FIXME: This property needs to be eliminated. It should never have been added.
+ unsigned m_appearance : 6; // EAppearance
+ unsigned m_borderFit : 1; // EBorderFit
+ unsigned m_textCombine : 1; // CSS3 text-combine properties
+
+ short m_counterIncrement;
+ short m_counterReset;
+
+#if USE(ACCELERATED_COMPOSITING)
+ bool m_runningAcceleratedAnimation : 1;
+#endif
+ OwnPtr<ShadowData> m_boxShadow; // For box-shadow decorations.
+
+ RefPtr<StyleReflection> m_boxReflect;
+
+ OwnPtr<AnimationList> m_animations;
+ OwnPtr<AnimationList> m_transitions;
+
+ FillLayer m_mask;
+ NinePieceImage m_maskBoxImage;
+
+ ETransformStyle3D m_transformStyle3D;
+ EBackfaceVisibility m_backfaceVisibility;
+ float m_perspective;
+ Length m_perspectiveOriginX;
+ Length m_perspectiveOriginY;
+
+ LengthSize m_pageSize;
+ PageSizeType m_pageSizeType;
+
+private:
+ StyleRareNonInheritedData();
+ StyleRareNonInheritedData(const StyleRareNonInheritedData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleRareNonInheritedData_h
diff --git a/Source/WebCore/rendering/style/StyleReflection.h b/Source/WebCore/rendering/style/StyleReflection.h
new file mode 100644
index 0000000..455d1b7
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleReflection.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleReflection_h
+#define StyleReflection_h
+
+#include "CSSReflectionDirection.h"
+#include "Length.h"
+#include "NinePieceImage.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StyleReflection : public RefCounted<StyleReflection> {
+public:
+ static PassRefPtr<StyleReflection> create()
+ {
+ return adoptRef(new StyleReflection);
+ }
+
+ bool operator==(const StyleReflection& o) const
+ {
+ return m_direction == o.m_direction && m_offset == o.m_offset && m_mask == o.m_mask;
+ }
+ bool operator!=(const StyleReflection& o) const { return !(*this == o); }
+
+ CSSReflectionDirection direction() const { return m_direction; }
+ Length offset() const { return m_offset; }
+ const NinePieceImage& mask() const { return m_mask; }
+
+ void setDirection(CSSReflectionDirection dir) { m_direction = dir; }
+ void setOffset(const Length& l) { m_offset = l; }
+ void setMask(const NinePieceImage& image) { m_mask = image; }
+
+private:
+ StyleReflection()
+ : m_direction(ReflectionBelow)
+ , m_offset(0, Fixed)
+ {
+ }
+
+ CSSReflectionDirection m_direction;
+ Length m_offset;
+ NinePieceImage m_mask;
+};
+
+} // namespace WebCore
+
+#endif // StyleReflection_h
diff --git a/Source/WebCore/rendering/style/StyleSurroundData.cpp b/Source/WebCore/rendering/style/StyleSurroundData.cpp
new file mode 100644
index 0000000..8d5e79c
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleSurroundData.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleSurroundData.h"
+
+namespace WebCore {
+
+StyleSurroundData::StyleSurroundData()
+ : margin(Fixed)
+ , padding(Fixed)
+{
+}
+
+StyleSurroundData::StyleSurroundData(const StyleSurroundData& o)
+ : RefCounted<StyleSurroundData>()
+ , offset(o.offset)
+ , margin(o.margin)
+ , padding(o.padding)
+ , border(o.border)
+{
+}
+
+bool StyleSurroundData::operator==(const StyleSurroundData& o) const
+{
+ return offset == o.offset && margin == o.margin && padding == o.padding && border == o.border;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleSurroundData.h b/Source/WebCore/rendering/style/StyleSurroundData.h
new file mode 100644
index 0000000..b8f21e4
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleSurroundData.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleSurroundData_h
+#define StyleSurroundData_h
+
+#include "BorderData.h"
+#include "LengthBox.h"
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class StyleSurroundData : public RefCounted<StyleSurroundData> {
+public:
+ static PassRefPtr<StyleSurroundData> create() { return adoptRef(new StyleSurroundData); }
+ PassRefPtr<StyleSurroundData> copy() const { return adoptRef(new StyleSurroundData(*this)); }
+
+ bool operator==(const StyleSurroundData& o) const;
+ bool operator!=(const StyleSurroundData& o) const
+ {
+ return !(*this == o);
+ }
+
+ LengthBox offset;
+ LengthBox margin;
+ LengthBox padding;
+ BorderData border;
+
+private:
+ StyleSurroundData();
+ StyleSurroundData(const StyleSurroundData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleSurroundData_h
diff --git a/Source/WebCore/rendering/style/StyleTransformData.cpp b/Source/WebCore/rendering/style/StyleTransformData.cpp
new file mode 100644
index 0000000..2baebf9
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleTransformData.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleTransformData.h"
+
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+StyleTransformData::StyleTransformData()
+ : m_operations(RenderStyle::initialTransform())
+ , m_x(RenderStyle::initialTransformOriginX())
+ , m_y(RenderStyle::initialTransformOriginY())
+ , m_z(RenderStyle::initialTransformOriginZ())
+{
+}
+
+StyleTransformData::StyleTransformData(const StyleTransformData& o)
+ : RefCounted<StyleTransformData>()
+ , m_operations(o.m_operations)
+ , m_x(o.m_x)
+ , m_y(o.m_y)
+ , m_z(o.m_z)
+{
+}
+
+bool StyleTransformData::operator==(const StyleTransformData& o) const
+{
+ return m_x == o.m_x && m_y == o.m_y && m_z == o.m_z && m_operations == o.m_operations;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleTransformData.h b/Source/WebCore/rendering/style/StyleTransformData.h
new file mode 100644
index 0000000..6039824
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleTransformData.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleTransformData_h
+#define StyleTransformData_h
+
+#include "Length.h"
+#include "TransformOperations.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class StyleTransformData : public RefCounted<StyleTransformData> {
+public:
+ static PassRefPtr<StyleTransformData> create() { return adoptRef(new StyleTransformData); }
+ PassRefPtr<StyleTransformData> copy() const { return adoptRef(new StyleTransformData(*this)); }
+
+ bool operator==(const StyleTransformData& o) const;
+ bool operator!=(const StyleTransformData& o) const
+ {
+ return !(*this == o);
+ }
+
+ TransformOperations m_operations;
+ Length m_x;
+ Length m_y;
+ float m_z;
+
+private:
+ StyleTransformData();
+ StyleTransformData(const StyleTransformData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleTransformData_h
diff --git a/Source/WebCore/rendering/style/StyleVisualData.cpp b/Source/WebCore/rendering/style/StyleVisualData.cpp
new file mode 100644
index 0000000..14996c9
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleVisualData.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 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 "StyleVisualData.h"
+
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+StyleVisualData::StyleVisualData()
+ : hasClip(false)
+ , textDecoration(RenderStyle::initialTextDecoration())
+ , m_zoom(RenderStyle::initialZoom())
+{
+}
+
+StyleVisualData::~StyleVisualData()
+{
+}
+
+StyleVisualData::StyleVisualData(const StyleVisualData& o)
+ : RefCounted<StyleVisualData>()
+ , clip(o.clip)
+ , hasClip(o.hasClip)
+ , textDecoration(o.textDecoration)
+ , m_zoom(RenderStyle::initialZoom())
+{
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/rendering/style/StyleVisualData.h b/Source/WebCore/rendering/style/StyleVisualData.h
new file mode 100644
index 0000000..d1f0f83
--- /dev/null
+++ b/Source/WebCore/rendering/style/StyleVisualData.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
+ * (C) 2000 Antti Koivisto (koivisto@kde.org)
+ * (C) 2000 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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 StyleVisualData_h
+#define StyleVisualData_h
+
+#include "LengthBox.h"
+#include <wtf/RefCounted.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class StyleVisualData : public RefCounted<StyleVisualData> {
+public:
+ static PassRefPtr<StyleVisualData> create() { return adoptRef(new StyleVisualData); }
+ PassRefPtr<StyleVisualData> copy() const { return adoptRef(new StyleVisualData(*this)); }
+ ~StyleVisualData();
+
+ bool operator==(const StyleVisualData& o) const
+ {
+ return ( clip == o.clip &&
+ hasClip == o.hasClip &&
+ textDecoration == o.textDecoration &&
+ m_zoom == o.m_zoom);
+ }
+ bool operator!=(const StyleVisualData& o) const { return !(*this == o); }
+
+ LengthBox clip;
+ bool hasClip : 1;
+ unsigned textDecoration : 4; // Text decorations defined *only* by this element.
+
+ float m_zoom;
+
+private:
+ StyleVisualData();
+ StyleVisualData(const StyleVisualData&);
+};
+
+} // namespace WebCore
+
+#endif // StyleVisualData_h
diff --git a/Source/WebCore/rendering/svg/RenderSVGInline.cpp b/Source/WebCore/rendering/svg/RenderSVGInline.cpp
new file mode 100644
index 0000000..4d0c533
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGInline.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * Copyright (C) 2006 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGInline.h"
+
+#include "RenderSVGResource.h"
+#include "RenderSVGText.h"
+#include "SVGInlineFlowBox.h"
+
+namespace WebCore {
+
+RenderSVGInline::RenderSVGInline(Node* n)
+ : RenderInline(n)
+{
+}
+
+InlineFlowBox* RenderSVGInline::createInlineFlowBox()
+{
+ InlineFlowBox* box = new (renderArena()) SVGInlineFlowBox(this);
+ box->setHasVirtualLogicalHeight();
+ return box;
+}
+
+FloatRect RenderSVGInline::objectBoundingBox() const
+{
+ if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this))
+ return object->objectBoundingBox();
+
+ return FloatRect();
+}
+
+FloatRect RenderSVGInline::strokeBoundingBox() const
+{
+ if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this))
+ return object->strokeBoundingBox();
+
+ return FloatRect();
+}
+
+FloatRect RenderSVGInline::repaintRectInLocalCoordinates() const
+{
+ if (const RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this))
+ return object->repaintRectInLocalCoordinates();
+
+ return FloatRect();
+}
+
+IntRect RenderSVGInline::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
+}
+
+void RenderSVGInline::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+ SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
+}
+
+void RenderSVGInline::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState& transformState) const
+{
+ SVGRenderSupport::mapLocalToContainer(this, repaintContainer, useTransforms, fixed, transformState);
+}
+
+void RenderSVGInline::absoluteQuads(Vector<FloatQuad>& quads)
+{
+ RenderObject* object = RenderSVGText::locateRenderSVGTextAncestor(this);
+ if (!object)
+ return;
+
+ FloatRect textBoundingBox = object->strokeBoundingBox();
+ for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox())
+ quads.append(localToAbsoluteQuad(FloatRect(textBoundingBox.x() + box->x(), textBoundingBox.y() + box->y(), box->logicalWidth(), box->logicalHeight())));
+}
+
+void RenderSVGInline::destroy()
+{
+ SVGResourcesCache::clientDestroyed(this);
+ RenderInline::destroy();
+}
+
+void RenderSVGInline::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
+{
+ if (diff == StyleDifferenceLayout)
+ setNeedsBoundariesUpdate();
+ RenderInline::styleWillChange(diff, newStyle);
+}
+
+void RenderSVGInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderInline::styleDidChange(diff, oldStyle);
+ SVGResourcesCache::clientStyleChanged(this, diff, style());
+}
+
+void RenderSVGInline::updateFromElement()
+{
+ RenderInline::updateFromElement();
+ SVGResourcesCache::clientUpdatedFromElement(this, style());
+}
+
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGInline.h b/Source/WebCore/rendering/svg/RenderSVGInline.h
new file mode 100644
index 0000000..d7b7e66
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGInline.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (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 RenderSVGInline_h
+#define RenderSVGInline_h
+
+#if ENABLE(SVG)
+#include "RenderInline.h"
+
+#include "SVGRenderSupport.h"
+
+namespace WebCore {
+
+class RenderSVGInline : public RenderInline {
+public:
+ explicit RenderSVGInline(Node*);
+
+ virtual const char* renderName() const { return "RenderSVGInline"; }
+ virtual bool requiresLayer() const { return false; }
+ virtual bool isSVGInline() const { return true; }
+
+ // Chapter 10.4 of the SVG Specification say that we should use the
+ // object bounding box of the parent text element.
+ // We search for the root text element and take its bounding box.
+ // It is also necessary to take the stroke and repaint rect of
+ // this element, since we need it for filters.
+ virtual FloatRect objectBoundingBox() const;
+ virtual FloatRect strokeBoundingBox() const;
+ virtual FloatRect repaintRectInLocalCoordinates() const;
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+private:
+ virtual InlineFlowBox* createInlineFlowBox();
+
+ virtual void destroy();
+ virtual void styleWillChange(StyleDifference, const RenderStyle* newStyle);
+ virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);
+ virtual void updateFromElement();
+};
+
+}
+
+#endif // ENABLE(SVG)
+#endif // !RenderSVGTSpan_H
diff --git a/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp b/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp
new file mode 100644
index 0000000..b791f3e
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGInlineText.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * Copyright (C) 2006 Apple Computer Inc.
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2008 Rob Buis <buis@kde.org>
+ * 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)
+#include "RenderSVGInlineText.h"
+
+#include "FloatConversion.h"
+#include "FloatQuad.h"
+#include "RenderBlock.h"
+#include "RenderSVGRoot.h"
+#include "RenderSVGText.h"
+#include "SVGInlineTextBox.h"
+#include "SVGRootInlineBox.h"
+#include "VisiblePosition.h"
+
+namespace WebCore {
+
+static PassRefPtr<StringImpl> applySVGWhitespaceRules(PassRefPtr<StringImpl> string, bool preserveWhiteSpace)
+{
+ if (preserveWhiteSpace) {
+ // Spec: When xml:space="preserve", the SVG user agent will do the following using a
+ // copy of the original character data content. It will convert all newline and tab
+ // characters into space characters. Then, it will draw all space characters, including
+ // leading, trailing and multiple contiguous space characters.
+ RefPtr<StringImpl> newString = string->replace('\t', ' ');
+ newString = newString->replace('\n', ' ');
+ newString = newString->replace('\r', ' ');
+ return newString.release();
+ }
+
+ // Spec: When xml:space="default", the SVG user agent will do the following using a
+ // copy of the original character data content. First, it will remove all newline
+ // characters. Then it will convert all tab characters into space characters.
+ // Then, it will strip off all leading and trailing space characters.
+ // Then, all contiguous space characters will be consolidated.
+ RefPtr<StringImpl> newString = string->replace('\n', StringImpl::empty());
+ newString = newString->replace('\r', StringImpl::empty());
+ newString = newString->replace('\t', ' ');
+ return newString.release();
+}
+
+RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> string)
+ : RenderText(n, applySVGWhitespaceRules(string, false))
+{
+}
+
+void RenderSVGInlineText::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+ RenderText::styleDidChange(diff, oldStyle);
+
+ if (diff == StyleDifferenceLayout) {
+ // The text metrics may be influenced by style changes.
+ if (RenderSVGText* textRenderer = RenderSVGText::locateRenderSVGTextAncestor(this))
+ textRenderer->setNeedsPositioningValuesUpdate();
+ }
+
+ const RenderStyle* newStyle = style();
+ if (!newStyle || newStyle->whiteSpace() != PRE)
+ return;
+
+ if (!oldStyle || oldStyle->whiteSpace() != PRE)
+ setText(applySVGWhitespaceRules(originalText(), true), true);
+}
+
+InlineTextBox* RenderSVGInlineText::createTextBox()
+{
+ InlineTextBox* box = new (renderArena()) SVGInlineTextBox(this);
+ box->setHasVirtualLogicalHeight();
+ return box;
+}
+
+IntRect RenderSVGInlineText::localCaretRect(InlineBox* box, int caretOffset, int*)
+{
+ if (!box->isInlineTextBox())
+ return IntRect();
+
+ InlineTextBox* textBox = static_cast<InlineTextBox*>(box);
+ if (static_cast<unsigned>(caretOffset) < textBox->start() || static_cast<unsigned>(caretOffset) > textBox->start() + textBox->len())
+ return IntRect();
+
+ // Use the edge of the selection rect to determine the caret rect.
+ if (static_cast<unsigned>(caretOffset) < textBox->start() + textBox->len()) {
+ IntRect rect = textBox->selectionRect(0, 0, caretOffset, caretOffset + 1);
+ int x = box->isLeftToRightDirection() ? rect.x() : rect.right();
+ return IntRect(x, rect.y(), caretWidth, rect.height());
+ }
+
+ IntRect rect = textBox->selectionRect(0, 0, caretOffset - 1, caretOffset);
+ int x = box->isLeftToRightDirection() ? rect.right() : rect.x();
+ return IntRect(x, rect.y(), caretWidth, rect.height());
+}
+
+IntRect RenderSVGInlineText::linesBoundingBox() const
+{
+ IntRect boundingBox;
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+ boundingBox.unite(box->calculateBoundaries());
+ return boundingBox;
+}
+
+bool RenderSVGInlineText::characterStartsNewTextChunk(int position) const
+{
+ ASSERT(m_attributes.xValues().size() == textLength());
+ ASSERT(m_attributes.yValues().size() == textLength());
+ ASSERT(position >= 0);
+ ASSERT(position < static_cast<int>(textLength()));
+
+ // Each <textPath> element starts a new text chunk, regardless of any x/y values.
+ if (!position && parent()->isSVGTextPath() && !previousSibling())
+ return true;
+
+ int currentPosition = 0;
+ unsigned size = m_attributes.textMetricsValues().size();
+ for (unsigned i = 0; i < size; ++i) {
+ const SVGTextMetrics& metrics = m_attributes.textMetricsValues().at(i);
+
+ // We found the desired character.
+ if (currentPosition == position) {
+ return m_attributes.xValues().at(position) != SVGTextLayoutAttributes::emptyValue()
+ || m_attributes.yValues().at(position) != SVGTextLayoutAttributes::emptyValue();
+ }
+
+ currentPosition += metrics.length();
+ if (currentPosition > position)
+ break;
+ }
+
+ // The desired position is available in the x/y list, but not in the character data values list.
+ // That means the previous character data described a single glyph, consisting of multiple unicode characters.
+ // The consequence is that the desired character does not define a new absolute x/y position, even if present in the x/y test.
+ // This code is tested by svg/W3C-SVG-1.1/text-text-06-t.svg (and described in detail, why this influences chunk detection).
+ return false;
+}
+
+VisiblePosition RenderSVGInlineText::positionForPoint(const IntPoint& point)
+{
+ if (!firstTextBox() || !textLength())
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ RenderStyle* style = this->style();
+ ASSERT(style);
+ int baseline = style->font().ascent();
+
+ RenderBlock* containingBlock = this->containingBlock();
+ ASSERT(containingBlock);
+
+ // Map local point to absolute point, as the character origins stored in the text fragments use absolute coordinates.
+ FloatPoint absolutePoint(point);
+ absolutePoint.move(containingBlock->x(), containingBlock->y());
+
+ float closestDistance = std::numeric_limits<float>::max();
+ float closestDistancePosition = 0;
+ const SVGTextFragment* closestDistanceFragment = 0;
+ SVGInlineTextBox* closestDistanceBox = 0;
+
+ for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+ ASSERT(box->isSVGInlineTextBox());
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+
+ unsigned textFragmentsSize = fragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ const SVGTextFragment& fragment = fragments.at(i);
+ FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height);
+ if (!fragment.transform.isIdentity())
+ fragmentRect = fragment.transform.mapRect(fragmentRect);
+
+ float distance = powf(fragmentRect.x() - absolutePoint.x(), 2) +
+ powf(fragmentRect.y() + fragmentRect.height() / 2 - absolutePoint.y(), 2);
+
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestDistanceBox = textBox;
+ closestDistanceFragment = &fragment;
+ closestDistancePosition = fragmentRect.x();
+ }
+ }
+ }
+
+ if (!closestDistanceFragment)
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ int offset = closestDistanceBox->offsetForPositionInFragment(*closestDistanceFragment, absolutePoint.x() - closestDistancePosition, true);
+ return createVisiblePosition(offset + closestDistanceBox->start(), offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM);
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGInlineText.h b/Source/WebCore/rendering/svg/RenderSVGInlineText.h
new file mode 100644
index 0000000..926ec43
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGInlineText.h
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * (C) 2008 Rob Buis <buis@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 RenderSVGInlineText_h
+#define RenderSVGInlineText_h
+
+#if ENABLE(SVG)
+#include "RenderText.h"
+#include "SVGTextLayoutAttributes.h"
+
+namespace WebCore {
+
+class SVGInlineTextBox;
+
+class RenderSVGInlineText : public RenderText {
+public:
+ RenderSVGInlineText(Node*, PassRefPtr<StringImpl>);
+
+ bool characterStartsNewTextChunk(int position) const;
+
+ SVGTextLayoutAttributes& layoutAttributes() { return m_attributes; }
+ const SVGTextLayoutAttributes& layoutAttributes() const { return m_attributes; }
+ void storeLayoutAttributes(const SVGTextLayoutAttributes& attributes) { m_attributes = attributes; }
+
+private:
+ virtual const char* renderName() const { return "RenderSVGInlineText"; }
+
+ virtual void styleDidChange(StyleDifference, const RenderStyle*);
+
+ // FIXME: We need objectBoundingBox for DRT results and filters at the moment.
+ // This should be fixed to give back the objectBoundingBox of the text root.
+ virtual FloatRect objectBoundingBox() const { return FloatRect(); }
+
+ virtual bool requiresLayer() const { return false; }
+ virtual bool isSVGInlineText() const { return true; }
+
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+ virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
+ virtual IntRect linesBoundingBox() const;
+ virtual InlineTextBox* createTextBox();
+
+ SVGTextLayoutAttributes m_attributes;
+};
+
+inline RenderSVGInlineText* toRenderSVGInlineText(RenderObject* object)
+{
+ ASSERT(!object || object->isSVGInlineText());
+ return static_cast<RenderSVGInlineText*>(object);
+}
+
+inline const RenderSVGInlineText* toRenderSVGInlineText(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGInlineText());
+ return static_cast<const RenderSVGInlineText*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGInlineText(const RenderSVGInlineText*);
+
+}
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGInlineText_h
diff --git a/Source/WebCore/rendering/svg/RenderSVGPath.cpp b/Source/WebCore/rendering/svg/RenderSVGPath.cpp
new file mode 100644
index 0000000..0c8ac0c
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGPath.cpp
@@ -0,0 +1,338 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005, 2008 Rob Buis <buis@kde.org>
+ 2005, 2007 Eric Seidel <eric@webkit.org>
+ 2009 Google, Inc.
+ 2009 Dirk Schulze <krit@webkit.org>
+ Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ 2009 Jeff Schiller <codedread@gmail.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
+ 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"
+
+#if ENABLE(SVG)
+#include "RenderSVGPath.h"
+
+#include "FloatPoint.h"
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "HitTestRequest.h"
+#include "PointerEventsHitRules.h"
+#include "RenderSVGContainer.h"
+#include "RenderSVGResourceMarker.h"
+#include "RenderSVGResourceSolidColor.h"
+#include "SVGRenderSupport.h"
+#include "SVGResources.h"
+#include "SVGStyledTransformableElement.h"
+#include "SVGTransformList.h"
+#include "SVGURIReference.h"
+#include "StrokeStyleApplier.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+class BoundingRectStrokeStyleApplier : public StrokeStyleApplier {
+public:
+ BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style)
+ : m_object(object)
+ , m_style(style)
+ {
+ ASSERT(style);
+ ASSERT(object);
+ }
+
+ void strokeStyle(GraphicsContext* gc)
+ {
+ SVGRenderSupport::applyStrokeStyleToContext(gc, m_style, m_object);
+ }
+
+private:
+ const RenderObject* m_object;
+ RenderStyle* m_style;
+};
+
+RenderSVGPath::RenderSVGPath(SVGStyledTransformableElement* node)
+ : RenderSVGModelObject(node)
+ , m_needsBoundariesUpdate(false) // default is false, the cached rects are empty from the beginning
+ , m_needsPathUpdate(true) // default is true, so we grab a Path object once from SVGStyledTransformableElement
+ , m_needsTransformUpdate(true) // default is true, so we grab a AffineTransform object once from SVGStyledTransformableElement
+{
+}
+
+RenderSVGPath::~RenderSVGPath()
+{
+}
+
+bool RenderSVGPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule)
+{
+ if (!m_fillBoundingBox.contains(point))
+ return false;
+
+ Color fallbackColor;
+ if (requiresFill && !RenderSVGResource::fillPaintingResource(this, style(), fallbackColor))
+ return false;
+
+ return m_path.contains(point, fillRule);
+}
+
+bool RenderSVGPath::strokeContains(const FloatPoint& point, bool requiresStroke)
+{
+ if (!m_strokeAndMarkerBoundingBox.contains(point))
+ return false;
+
+ Color fallbackColor;
+ if (requiresStroke && !RenderSVGResource::strokePaintingResource(this, style(), fallbackColor))
+ return false;
+
+ BoundingRectStrokeStyleApplier strokeStyle(this, style());
+ return m_path.strokeContains(&strokeStyle, point);
+}
+
+void RenderSVGPath::layout()
+{
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
+ SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
+
+ bool updateCachedBoundariesInParents = false;
+
+ bool needsPathUpdate = m_needsPathUpdate;
+ if (needsPathUpdate) {
+ m_path.clear();
+ element->toPathData(m_path);
+ m_needsPathUpdate = false;
+ updateCachedBoundariesInParents = true;
+ }
+
+ if (m_needsTransformUpdate) {
+ m_localTransform = element->animatedLocalTransform();
+ m_needsTransformUpdate = false;
+ updateCachedBoundariesInParents = true;
+ }
+
+ if (m_needsBoundariesUpdate)
+ updateCachedBoundariesInParents = true;
+
+ // Invalidate all resources of this client if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ SVGResourcesCache::clientLayoutChanged(this);
+
+ // At this point LayoutRepainter already grabbed the old bounds,
+ // recalculate them now so repaintAfterLayout() uses the new bounds.
+ if (needsPathUpdate || m_needsBoundariesUpdate) {
+ updateCachedBoundaries();
+ m_needsBoundariesUpdate = false;
+ }
+
+ // If our bounds changed, notify the parents.
+ if (updateCachedBoundariesInParents)
+ RenderSVGModelObject::setNeedsBoundariesUpdate();
+
+ repainter.repaintAfterLayout();
+ setNeedsLayout(false);
+}
+
+void RenderSVGPath::fillAndStrokePath(GraphicsContext* context)
+{
+ RenderStyle* style = this->style();
+
+ Color fallbackColor;
+ if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
+ if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
+ fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
+ else if (fallbackColor.isValid()) {
+ RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
+ fallbackResource->setColor(fallbackColor);
+ if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
+ fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
+ }
+ }
+
+ fallbackColor = Color();
+ RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor);
+ if (!strokePaintingResource)
+ return;
+
+ Path path;
+
+ bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;
+ bool restoreContext = false;
+ if (nonScalingStroke) {
+ SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
+ AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
+ if (!nonScalingStrokeTransform.isInvertible())
+ return;
+
+ path = m_path;
+ path.transform(nonScalingStrokeTransform);
+
+ context->save();
+ context->concatCTM(nonScalingStrokeTransform.inverse());
+ restoreContext = true;
+ }
+
+ if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
+ strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
+ else if (fallbackColor.isValid()) {
+ RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
+ fallbackResource->setColor(fallbackColor);
+ if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
+ fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
+ }
+
+ if (restoreContext)
+ context->restore();
+}
+
+void RenderSVGPath::paint(PaintInfo& paintInfo, int, int)
+{
+ if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty())
+ return;
+
+ FloatRect boundingBox = repaintRectInLocalCoordinates();
+ if (!SVGRenderSupport::paintInfoIntersectsRepaintRect(boundingBox, m_localTransform, paintInfo))
+ return;
+
+ PaintInfo childPaintInfo(paintInfo);
+ bool drawsOutline = style()->outlineWidth() && (childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline);
+ if (drawsOutline || childPaintInfo.phase == PaintPhaseForeground) {
+ childPaintInfo.context->save();
+ childPaintInfo.applyTransform(m_localTransform);
+
+ if (childPaintInfo.phase == PaintPhaseForeground) {
+ PaintInfo savedInfo(childPaintInfo);
+
+ if (SVGRenderSupport::prepareToRenderSVGContent(this, childPaintInfo)) {
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ if (svgStyle->shapeRendering() == SR_CRISPEDGES)
+ childPaintInfo.context->setShouldAntialias(false);
+
+ fillAndStrokePath(childPaintInfo.context);
+
+ if (svgStyle->hasMarkers())
+ m_markerLayoutInfo.drawMarkers(childPaintInfo);
+ }
+
+ SVGRenderSupport::finishRenderSVGContent(this, childPaintInfo, savedInfo.context);
+ }
+
+ if (drawsOutline)
+ paintOutline(childPaintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()),
+ static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()));
+
+ childPaintInfo.context->restore();
+ }
+}
+
+// This method is called from inside paintOutline() since we call paintOutline()
+// while transformed to our coord system, return local coords
+void RenderSVGPath::addFocusRingRects(Vector<IntRect>& rects, int, int)
+{
+ IntRect rect = enclosingIntRect(repaintRectInLocalCoordinates());
+ if (!rect.isEmpty())
+ rects.append(rect);
+}
+
+bool RenderSVGPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
+{
+ // We only draw in the forground phase, so we only hit-test then.
+ if (hitTestAction != HitTestForeground)
+ return false;
+
+ FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
+
+ if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
+ return false;
+
+ PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
+ bool isVisible = (style()->visibility() == VISIBLE);
+ if (isVisible || !hitRules.requireVisible) {
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ WindRule fillRule = svgStyle->fillRule();
+ if (request.svgClipContent())
+ fillRule = svgStyle->clipRule();
+ if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
+ || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
+ updateHitTestResult(result, roundedIntPoint(localPoint));
+ return true;
+ }
+ }
+ return false;
+}
+
+FloatRect RenderSVGPath::calculateMarkerBoundsIfNeeded()
+{
+ SVGElement* svgElement = static_cast<SVGElement*>(node());
+ ASSERT(svgElement && svgElement->document());
+ if (!svgElement->isStyled())
+ return FloatRect();
+
+ SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement);
+ if (!styledElement->supportsMarkers())
+ return FloatRect();
+
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ ASSERT(svgStyle->hasMarkers());
+
+ SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this);
+ if (!resources)
+ return FloatRect();
+
+ RenderSVGResourceMarker* markerStart = resources->markerStart();
+ RenderSVGResourceMarker* markerMid = resources->markerMid();
+ RenderSVGResourceMarker* markerEnd = resources->markerEnd();
+ if (!markerStart && !markerMid && !markerEnd)
+ return FloatRect();
+
+ return m_markerLayoutInfo.calculateBoundaries(markerStart, markerMid, markerEnd, svgStyle->strokeWidth().value(svgElement), m_path);
+}
+
+void RenderSVGPath::updateCachedBoundaries()
+{
+ if (m_path.isEmpty()) {
+ m_fillBoundingBox = FloatRect();
+ m_strokeAndMarkerBoundingBox = FloatRect();
+ m_repaintBoundingBox = FloatRect();
+ return;
+ }
+
+ // Cache _unclipped_ fill bounding box, used for calculations in resources
+ m_fillBoundingBox = m_path.boundingRect();
+
+ // Cache _unclipped_ stroke bounding box, used for calculations in resources (includes marker boundaries)
+ m_strokeAndMarkerBoundingBox = m_fillBoundingBox;
+
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ if (svgStyle->hasStroke()) {
+ BoundingRectStrokeStyleApplier strokeStyle(this, style());
+ m_strokeAndMarkerBoundingBox.unite(m_path.strokeBoundingRect(&strokeStyle));
+ }
+
+ if (svgStyle->hasMarkers()) {
+ FloatRect markerBounds = calculateMarkerBoundsIfNeeded();
+ if (!markerBounds.isEmpty())
+ m_strokeAndMarkerBoundingBox.unite(markerBounds);
+ }
+
+ // Cache smallest possible repaint rectangle
+ m_repaintBoundingBox = m_strokeAndMarkerBoundingBox;
+ SVGRenderSupport::intersectRepaintRectWithResources(this, m_repaintBoundingBox);
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGPath.h b/Source/WebCore/rendering/svg/RenderSVGPath.h
new file mode 100644
index 0000000..41b0e51
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGPath.h
@@ -0,0 +1,105 @@
+/*
+ Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ 2004, 2005 Rob Buis <buis@kde.org>
+ 2005 Eric Seidel <eric@webkit.org>
+ 2006 Apple Computer, Inc
+ 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
+ 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 RenderSVGPath_h
+#define RenderSVGPath_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+#include "FloatRect.h"
+#include "RenderSVGModelObject.h"
+#include "SVGMarkerLayoutInfo.h"
+
+namespace WebCore {
+
+class FloatPoint;
+class RenderSVGContainer;
+class SVGStyledTransformableElement;
+
+class RenderSVGPath : public RenderSVGModelObject {
+public:
+ explicit RenderSVGPath(SVGStyledTransformableElement*);
+ virtual ~RenderSVGPath();
+
+ const Path& path() const { return m_path; }
+ void setNeedsPathUpdate() { m_needsPathUpdate = true; }
+ virtual void setNeedsBoundariesUpdate() { m_needsBoundariesUpdate = true; }
+ virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+
+private:
+ // Hit-detection seperated for the fill and the stroke
+ bool fillContains(const FloatPoint&, bool requiresFill = true, WindRule fillRule = RULE_NONZERO);
+ bool strokeContains(const FloatPoint&, bool requiresStroke = true);
+
+ virtual FloatRect objectBoundingBox() const { return m_fillBoundingBox; }
+ virtual FloatRect strokeBoundingBox() const { return m_strokeAndMarkerBoundingBox; }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return m_repaintBoundingBox; }
+ virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
+
+ virtual bool isSVGPath() const { return true; }
+ virtual const char* renderName() const { return "RenderSVGPath"; }
+
+ virtual void layout();
+ virtual void paint(PaintInfo&, int parentX, int parentY);
+ virtual void addFocusRingRects(Vector<IntRect>&, int tx, int ty);
+
+ virtual bool nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+
+ FloatRect calculateMarkerBoundsIfNeeded();
+ void updateCachedBoundaries();
+
+private:
+ virtual AffineTransform localTransform() const { return m_localTransform; }
+ void fillAndStrokePath(GraphicsContext*);
+
+ bool m_needsBoundariesUpdate : 1;
+ bool m_needsPathUpdate : 1;
+ bool m_needsTransformUpdate : 1;
+
+ mutable Path m_path;
+ FloatRect m_fillBoundingBox;
+ FloatRect m_strokeAndMarkerBoundingBox;
+ FloatRect m_repaintBoundingBox;
+ SVGMarkerLayoutInfo m_markerLayoutInfo;
+ AffineTransform m_localTransform;
+};
+
+inline RenderSVGPath* toRenderSVGPath(RenderObject* object)
+{
+ ASSERT(!object || object->isSVGPath());
+ return static_cast<RenderSVGPath*>(object);
+}
+
+inline const RenderSVGPath* toRenderSVGPath(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGPath());
+ return static_cast<const RenderSVGPath*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGPath(const RenderSVGPath*);
+
+}
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp b/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp
new file mode 100644
index 0000000..90ff36c
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGTSpan.cpp
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (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"
+
+#if ENABLE(SVG)
+#include "RenderSVGTSpan.h"
+
+namespace WebCore {
+
+RenderSVGTSpan::RenderSVGTSpan(Node* n)
+ : RenderSVGInline(n)
+{
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGTSpan.h b/Source/WebCore/rendering/svg/RenderSVGTSpan.h
new file mode 100644
index 0000000..97e0eb0
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGTSpan.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (C) 2006 Apple Computer Inc.
+ * (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 RenderSVGTSpan_h
+#define RenderSVGTSpan_h
+
+#if ENABLE(SVG)
+#include "RenderSVGInline.h"
+
+namespace WebCore {
+class RenderSVGTSpan : public RenderSVGInline {
+public:
+ explicit RenderSVGTSpan(Node*);
+ virtual const char* renderName() const { return "RenderSVGTSpan"; }
+};
+}
+
+#endif // ENABLE(SVG)
+#endif // !RenderSVGTSpan_h
diff --git a/Source/WebCore/rendering/svg/RenderSVGText.cpp b/Source/WebCore/rendering/svg/RenderSVGText.cpp
new file mode 100644
index 0000000..01a92b0
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGText.cpp
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2006 Alexander Kellett <lypanov@kde.org>
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 2008 Rob Buis <buis@kde.org>
+ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
+ * 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)
+#include "RenderSVGText.h"
+
+#include "FloatConversion.h"
+#include "FloatQuad.h"
+#include "GraphicsContext.h"
+#include "HitTestRequest.h"
+#include "PointerEventsHitRules.h"
+#include "RenderLayer.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGRoot.h"
+#include "SVGLengthList.h"
+#include "SVGRenderSupport.h"
+#include "SVGRootInlineBox.h"
+#include "SVGTextElement.h"
+#include "SVGTextLayoutAttributesBuilder.h"
+#include "SVGTransformList.h"
+#include "SVGURIReference.h"
+#include "SimpleFontData.h"
+#include "TransformState.h"
+#include "VisiblePosition.h"
+
+namespace WebCore {
+
+RenderSVGText::RenderSVGText(SVGTextElement* node)
+ : RenderSVGBlock(node)
+ , m_needsPositioningValuesUpdate(true)
+ , m_needsTransformUpdate(true)
+{
+}
+
+RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(RenderObject* start)
+{
+ ASSERT(start);
+ while (start && !start->isSVGText())
+ start = start->parent();
+ if (!start || !start->isSVGText())
+ return 0;
+ return toRenderSVGText(start);
+}
+
+const RenderSVGText* RenderSVGText::locateRenderSVGTextAncestor(const RenderObject* start)
+{
+ ASSERT(start);
+ while (start && !start->isSVGText())
+ start = start->parent();
+ if (!start || !start->isSVGText())
+ return 0;
+ return toRenderSVGText(start);
+}
+
+IntRect RenderSVGText::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
+{
+ return SVGRenderSupport::clippedOverflowRectForRepaint(this, repaintContainer);
+}
+
+void RenderSVGText::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& repaintRect, bool fixed)
+{
+ SVGRenderSupport::computeRectForRepaint(this, repaintContainer, repaintRect, fixed);
+}
+
+void RenderSVGText::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
+{
+ SVGRenderSupport::mapLocalToContainer(this, repaintContainer, fixed, useTransforms, transformState);
+}
+
+void RenderSVGText::layout()
+{
+ ASSERT(needsLayout());
+ LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
+
+ bool updateCachedBoundariesInParents = false;
+ if (m_needsTransformUpdate) {
+ SVGTextElement* text = static_cast<SVGTextElement*>(node());
+ m_localTransform = text->animatedLocalTransform();
+ m_needsTransformUpdate = false;
+ updateCachedBoundariesInParents = true;
+ }
+
+ if (m_needsPositioningValuesUpdate) {
+ // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details).
+ SVGTextLayoutAttributesBuilder layoutAttributesBuilder;
+ layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this);
+ m_needsPositioningValuesUpdate = false;
+ updateCachedBoundariesInParents = true;
+ }
+
+ // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text.
+ // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions.
+ ASSERT(!isInline());
+ ASSERT(!layoutOnlyPositionedObjects());
+ ASSERT(!scrollsOverflow());
+ ASSERT(!hasControlClip());
+ ASSERT(!hasColumns());
+ ASSERT(!positionedObjects());
+ ASSERT(!m_overflow);
+ ASSERT(!isAnonymousBlock());
+
+ if (!firstChild())
+ setChildrenInline(true);
+
+ // FIXME: We need to find a way to only layout the child boxes, if needed.
+ FloatRect oldBoundaries = objectBoundingBox();
+ ASSERT(childrenInline());
+ forceLayoutInlineChildren();
+
+ if (!updateCachedBoundariesInParents)
+ updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox();
+
+ // Invalidate all resources of this client if our layout changed.
+ if (m_everHadLayout && selfNeedsLayout())
+ SVGResourcesCache::clientLayoutChanged(this);
+
+ // If our bounds changed, notify the parents.
+ if (updateCachedBoundariesInParents)
+ RenderSVGBlock::setNeedsBoundariesUpdate();
+
+ repainter.repaintAfterLayout();
+ setNeedsLayout(false);
+}
+
+RootInlineBox* RenderSVGText::createRootInlineBox()
+{
+ RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this);
+ box->setHasVirtualLogicalHeight();
+ return box;
+}
+
+bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
+{
+ PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
+ bool isVisible = (style()->visibility() == VISIBLE);
+ if (isVisible || !hitRules.requireVisible) {
+ if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke))
+ || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
+ FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
+
+ if (!SVGRenderSupport::pointInClippingArea(this, localPoint))
+ return false;
+
+ return RenderBlock::nodeAtPoint(request, result, (int)localPoint.x(), (int)localPoint.y(), 0, 0, hitTestAction);
+ }
+ }
+
+ return false;
+}
+
+bool RenderSVGText::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction)
+{
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+VisiblePosition RenderSVGText::positionForPoint(const IntPoint& pointInContents)
+{
+ RootInlineBox* rootBox = firstRootBox();
+ if (!rootBox)
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ ASSERT(rootBox->isSVGRootInlineBox());
+ ASSERT(!rootBox->nextRootBox());
+ ASSERT(childrenInline());
+
+ InlineBox* closestBox = static_cast<SVGRootInlineBox*>(rootBox)->closestLeafChildForPosition(pointInContents);
+ if (!closestBox)
+ return createVisiblePosition(0, DOWNSTREAM);
+
+ return closestBox->renderer()->positionForPoint(IntPoint(pointInContents.x(), closestBox->m_y));
+}
+
+void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads)
+{
+ quads.append(localToAbsoluteQuad(strokeBoundingBox()));
+}
+
+void RenderSVGText::paint(PaintInfo& paintInfo, int, int)
+{
+ if (paintInfo.context->paintingDisabled())
+ return;
+
+ if (paintInfo.phase != PaintPhaseForeground
+ && paintInfo.phase != PaintPhaseSelfOutline
+ && paintInfo.phase != PaintPhaseSelection)
+ return;
+
+ PaintInfo blockInfo(paintInfo);
+ blockInfo.context->save();
+ blockInfo.applyTransform(localToParentTransform());
+ RenderBlock::paint(blockInfo, 0, 0);
+ blockInfo.context->restore();
+}
+
+FloatRect RenderSVGText::strokeBoundingBox() const
+{
+ FloatRect strokeBoundaries = objectBoundingBox();
+ const SVGRenderStyle* svgStyle = style()->svgStyle();
+ if (!svgStyle->hasStroke())
+ return strokeBoundaries;
+
+ ASSERT(node());
+ ASSERT(node()->isSVGElement());
+ strokeBoundaries.inflate(svgStyle->strokeWidth().value(static_cast<SVGElement*>(node())));
+ return strokeBoundaries;
+}
+
+FloatRect RenderSVGText::repaintRectInLocalCoordinates() const
+{
+ FloatRect repaintRect = strokeBoundingBox();
+ SVGRenderSupport::intersectRepaintRectWithResources(this, repaintRect);
+
+ if (const ShadowData* textShadow = style()->textShadow())
+ textShadow->adjustRectForShadow(repaintRect);
+
+ return repaintRect;
+}
+
+// Fix for <rdar://problem/8048875>. We should not render :first-line CSS Style
+// in a SVG text element context.
+RenderBlock* RenderSVGText::firstLineBlock() const
+{
+ return 0;
+}
+
+// Fix for <rdar://problem/8048875>. We should not render :first-letter CSS Style
+// in a SVG text element context.
+void RenderSVGText::updateFirstLetter()
+{
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGText.h b/Source/WebCore/rendering/svg/RenderSVGText.h
new file mode 100644
index 0000000..deae78c
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGText.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef RenderSVGText_h
+#define RenderSVGText_h
+
+#if ENABLE(SVG)
+
+#include "AffineTransform.h"
+#include "RenderSVGBlock.h"
+
+namespace WebCore {
+
+class SVGTextElement;
+
+class RenderSVGText : public RenderSVGBlock {
+public:
+ RenderSVGText(SVGTextElement* node);
+
+ void setNeedsPositioningValuesUpdate() { m_needsPositioningValuesUpdate = true; }
+ virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; }
+ virtual FloatRect repaintRectInLocalCoordinates() const;
+
+ static RenderSVGText* locateRenderSVGTextAncestor(RenderObject*);
+ static const RenderSVGText* locateRenderSVGTextAncestor(const RenderObject*);
+
+private:
+ virtual const char* renderName() const { return "RenderSVGText"; }
+ virtual bool isSVGText() const { return true; }
+
+ 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 nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint& pointInParent, HitTestAction);
+ virtual VisiblePosition positionForPoint(const IntPoint&);
+
+ virtual bool requiresLayer() const { return false; }
+ virtual void layout();
+
+ virtual void absoluteQuads(Vector<FloatQuad>&);
+
+ virtual IntRect clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer);
+ virtual void computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect&, bool fixed = false);
+
+ virtual void mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool useTransforms, bool fixed, TransformState&) const;
+
+ virtual FloatRect objectBoundingBox() const { return frameRect(); }
+ virtual FloatRect strokeBoundingBox() const;
+
+ virtual const AffineTransform& localToParentTransform() const { return m_localTransform; }
+ virtual AffineTransform localTransform() const { return m_localTransform; }
+ virtual RootInlineBox* createRootInlineBox();
+
+ virtual RenderBlock* firstLineBlock() const;
+ virtual void updateFirstLetter();
+
+ bool m_needsPositioningValuesUpdate : 1;
+ bool m_needsTransformUpdate : 1;
+ AffineTransform m_localTransform;
+};
+
+inline RenderSVGText* toRenderSVGText(RenderObject* object)
+{
+ ASSERT(!object || object->isSVGText());
+ return static_cast<RenderSVGText*>(object);
+}
+
+inline const RenderSVGText* toRenderSVGText(const RenderObject* object)
+{
+ ASSERT(!object || object->isSVGText());
+ return static_cast<const RenderSVGText*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGText(const RenderSVGText*);
+
+}
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp b/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp
new file mode 100644
index 0000000..4ba2eeb
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGTextPath.cpp
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@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.
+ *
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "RenderSVGTextPath.h"
+
+#include "FloatQuad.h"
+#include "RenderBlock.h"
+#include "SVGInlineTextBox.h"
+#include "SVGNames.h"
+#include "SVGPathElement.h"
+#include "SVGRootInlineBox.h"
+#include "SVGTextPathElement.h"
+#include "SVGTransformList.h"
+
+namespace WebCore {
+
+RenderSVGTextPath::RenderSVGTextPath(Node* n)
+ : RenderSVGInline(n)
+ , m_startOffset(0.0f)
+ , m_exactAlignment(true)
+ , m_stretchMethod(false)
+{
+}
+
+Path RenderSVGTextPath::layoutPath() const
+{
+ SVGTextPathElement* textPathElement = static_cast<SVGTextPathElement*>(node());
+ String pathId = SVGURIReference::getTarget(textPathElement->href());
+ Element* targetElement = textPathElement->document()->getElementById(pathId);
+ if (!targetElement || !targetElement->hasTagName(SVGNames::pathTag))
+ return Path();
+
+ SVGPathElement* pathElement = static_cast<SVGPathElement*>(targetElement);
+
+ Path pathData;
+ pathElement->toPathData(pathData);
+ // Spec: The transform attribute on the referenced 'path' element represents a
+ // supplemental transformation relative to the current user coordinate system for
+ // the current 'text' element, including any adjustments to the current user coordinate
+ // system due to a possible transform attribute on the current 'text' element.
+ // http://www.w3.org/TR/SVG/text.html#TextPathElement
+ pathData.transform(pathElement->animatedLocalTransform());
+ return pathData;
+}
+
+float RenderSVGTextPath::startOffset() const
+{
+ return static_cast<SVGTextPathElement*>(node())->startOffset().valueAsPercentage();
+}
+
+bool RenderSVGTextPath::exactAlignment() const
+{
+ return static_cast<SVGTextPathElement*>(node())->spacing() == SVG_TEXTPATH_SPACINGTYPE_EXACT;
+}
+
+bool RenderSVGTextPath::stretchMethod() const
+{
+ return static_cast<SVGTextPathElement*>(node())->method() == SVG_TEXTPATH_METHODTYPE_STRETCH;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/RenderSVGTextPath.h b/Source/WebCore/rendering/svg/RenderSVGTextPath.h
new file mode 100644
index 0000000..a71edf5
--- /dev/null
+++ b/Source/WebCore/rendering/svg/RenderSVGTextPath.h
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * Copyright (C) 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 RenderSVGTextPath_h
+#define RenderSVGTextPath_h
+
+#if ENABLE(SVG)
+#include "RenderSVGInline.h"
+
+namespace WebCore {
+
+class RenderSVGTextPath : public RenderSVGInline {
+public:
+ RenderSVGTextPath(Node*);
+
+ Path layoutPath() const;
+ float startOffset() const;
+ bool exactAlignment() const;
+ bool stretchMethod() const;
+
+ virtual bool isSVGTextPath() const { return true; }
+
+private:
+ virtual const char* renderName() const { return "RenderSVGTextPath"; }
+
+ float m_startOffset;
+
+ bool m_exactAlignment : 1;
+ bool m_stretchMethod : 1;
+
+ Path m_layoutPath;
+};
+
+inline RenderSVGTextPath* toRenderSVGTextPath(RenderObject* object)
+{
+ ASSERT(!object || !strcmp(object->renderName(), "RenderSVGTextPath"));
+ return static_cast<RenderSVGTextPath*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderSVGTextPath(const RenderSVGTextPath*);
+
+}
+
+#endif // ENABLE(SVG)
+#endif // RenderSVGTextPath_h
diff --git a/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp b/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp
new file mode 100644
index 0000000..ea806c7
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGInlineFlowBox.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (C) 2006 Apple Computer Inc.
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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 "SVGInlineFlowBox.h"
+
+#if ENABLE(SVG)
+#include "DocumentMarkerController.h"
+#include "GraphicsContext.h"
+#include "RenderSVGInlineText.h"
+#include "SVGInlineTextBox.h"
+#include "SVGRenderSupport.h"
+
+using namespace std;
+
+namespace WebCore {
+
+void SVGInlineFlowBox::paintSelectionBackground(PaintInfo& paintInfo)
+{
+ ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+ ASSERT(!paintInfo.context->paintingDisabled());
+
+ PaintInfo childPaintInfo(paintInfo);
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox())
+ static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo);
+ else if (child->isSVGInlineFlowBox())
+ static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo);
+ }
+}
+
+void SVGInlineFlowBox::paint(PaintInfo& paintInfo, int, int)
+{
+ ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+ ASSERT(!paintInfo.context->paintingDisabled());
+
+ RenderObject* boxRenderer = renderer();
+ ASSERT(boxRenderer);
+
+ PaintInfo childPaintInfo(paintInfo);
+ childPaintInfo.context->save();
+
+ if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox())
+ computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer()));
+
+ child->paint(childPaintInfo, 0, 0);
+ }
+ }
+
+ SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
+ childPaintInfo.context->restore();
+}
+
+IntRect SVGInlineFlowBox::calculateBoundaries() const
+{
+ IntRect childRect;
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
+ childRect.unite(child->calculateBoundaries());
+ return childRect;
+}
+
+void SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(RenderSVGInlineText* textRenderer)
+{
+ ASSERT(textRenderer);
+
+ Node* node = textRenderer->node();
+ if (!node || !node->inDocument())
+ return;
+
+ RenderStyle* style = textRenderer->style();
+ ASSERT(style);
+
+ Document* document = textRenderer->document();
+ Vector<DocumentMarker> markers = document->markers()->markersForNode(textRenderer->node());
+
+ Vector<DocumentMarker>::iterator markerEnd = markers.end();
+ for (Vector<DocumentMarker>::iterator markerIt = markers.begin(); markerIt != markerEnd; ++markerIt) {
+ const DocumentMarker& marker = *markerIt;
+
+ // SVG is only interessted in the TextMatch marker, for now.
+ if (marker.type != DocumentMarker::TextMatch)
+ continue;
+
+ FloatRect markerRect;
+ for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ ASSERT(box->isSVGInlineTextBox());
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(box);
+
+ int markerStartPosition = max<int>(marker.startOffset - textBox->start(), 0);
+ int markerEndPosition = min<int>(marker.endOffset - textBox->start(), textBox->len());
+
+ if (markerStartPosition >= markerEndPosition)
+ continue;
+
+ int fragmentStartPosition = 0;
+ int fragmentEndPosition = 0;
+
+ const Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ unsigned textFragmentsSize = fragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ const SVGTextFragment& fragment = fragments.at(i);
+
+ fragmentStartPosition = markerStartPosition;
+ fragmentEndPosition = markerEndPosition;
+ if (!textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
+ continue;
+
+ FloatRect fragmentRect = textBox->selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
+ if (!fragment.transform.isIdentity())
+ fragmentRect = fragment.transform.mapRect(fragmentRect);
+
+ markerRect.unite(fragmentRect);
+ }
+ }
+
+ document->markers()->setRenderedRectForMarker(node, marker, textRenderer->localToAbsoluteQuad(markerRect).enclosingBoundingBox());
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGInlineFlowBox.h b/Source/WebCore/rendering/svg/SVGInlineFlowBox.h
new file mode 100644
index 0000000..2358f2d
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGInlineFlowBox.h
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the WebKit project.
+ *
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (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 SVGInlineFlowBox_h
+#define SVGInlineFlowBox_h
+
+#if ENABLE(SVG)
+#include "InlineFlowBox.h"
+
+namespace WebCore {
+
+class RenderSVGInlineText;
+
+class SVGInlineFlowBox : public InlineFlowBox {
+public:
+ SVGInlineFlowBox(RenderObject* obj)
+ : InlineFlowBox(obj)
+ , m_logicalHeight(0)
+ {
+ }
+
+ virtual bool isSVGInlineFlowBox() const { return true; }
+ virtual int virtualLogicalHeight() const { return m_logicalHeight; }
+ void setLogicalHeight(int h) { m_logicalHeight = h; }
+
+ void paintSelectionBackground(PaintInfo&);
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ virtual IntRect calculateBoundaries() const;
+
+ static void computeTextMatchMarkerRectForRenderer(RenderSVGInlineText*);
+
+private:
+ int m_logicalHeight;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+
+#endif // SVGInlineFlowBox_h
diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp
new file mode 100644
index 0000000..5d0278b
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp
@@ -0,0 +1,608 @@
+/**
+ * Copyright (C) 2007 Rob Buis <buis@kde.org>
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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 "SVGInlineTextBox.h"
+
+#if ENABLE(SVG)
+#include "FloatConversion.h"
+#include "GraphicsContext.h"
+#include "InlineFlowBox.h"
+#include "RenderBlock.h"
+#include "RenderSVGInlineText.h"
+#include "RenderSVGResource.h"
+#include "RenderSVGResourceSolidColor.h"
+#include "SVGRootInlineBox.h"
+
+using namespace std;
+
+namespace WebCore {
+
+SVGInlineTextBox::SVGInlineTextBox(RenderObject* object)
+ : InlineTextBox(object)
+ , m_logicalHeight(0)
+ , m_paintingResourceMode(ApplyToDefaultMode)
+ , m_startsNewTextChunk(false)
+ , m_paintingResource(0)
+{
+}
+
+int SVGInlineTextBox::offsetForPosition(int, bool) const
+{
+ // SVG doesn't use the standard offset <-> position selection system, as it's not suitable for SVGs complex needs.
+ // vertical text selection, inline boxes spanning multiple lines (contrary to HTML, etc.)
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const
+{
+ RenderText* textRenderer = this->textRenderer();
+ ASSERT(textRenderer);
+
+ RenderStyle* style = textRenderer->style();
+ ASSERT(style);
+
+ TextRun textRun(constructTextRun(style, fragment));
+
+ // Eventually handle lengthAdjust="spacingAndGlyphs".
+ // FIXME: Handle vertical text.
+ if (!fragment.transform.isIdentity())
+ textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale()));
+
+ return fragment.positionListOffset - start() + style->font().offsetForPosition(textRun, position, includePartialGlyphs);
+}
+
+int SVGInlineTextBox::positionForOffset(int) const
+{
+ // SVG doesn't use the offset <-> position selection system.
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style)
+{
+ ASSERT(startPosition < endPosition);
+
+ const Font& font = style->font();
+ FloatPoint textOrigin(fragment.x, fragment.y - font.ascent());
+ return font.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height, startPosition, endPosition);
+}
+
+IntRect SVGInlineTextBox::selectionRect(int, int, int startPosition, int endPosition)
+{
+ int boxStart = start();
+ startPosition = max(startPosition - boxStart, 0);
+ endPosition = min(endPosition - boxStart, static_cast<int>(len()));
+ if (startPosition >= endPosition)
+ return IntRect();
+
+ RenderText* text = textRenderer();
+ ASSERT(text);
+
+ RenderStyle* style = text->style();
+ ASSERT(style);
+
+ FloatRect selectionRect;
+ int fragmentStartPosition = 0;
+ int fragmentEndPosition = 0;
+
+ unsigned textFragmentsSize = m_textFragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ const SVGTextFragment& fragment = m_textFragments.at(i);
+
+ fragmentStartPosition = startPosition;
+ fragmentEndPosition = endPosition;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
+ continue;
+
+ FloatRect fragmentRect = selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style);
+ if (!fragment.transform.isIdentity())
+ fragmentRect = fragment.transform.mapRect(fragmentRect);
+
+ selectionRect.unite(fragmentRect);
+ }
+
+ return enclosingIntRect(selectionRect);
+}
+
+void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo)
+{
+ ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
+ ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+ ASSERT(truncation() == cNoTruncation);
+
+ if (renderer()->style()->visibility() != VISIBLE)
+ return;
+
+ RenderObject* parentRenderer = parent()->renderer();
+ ASSERT(parentRenderer);
+ ASSERT(!parentRenderer->document()->printing());
+
+ // Determine whether or not we're selected.
+ bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
+ bool hasSelection = selectionState() != RenderObject::SelectionNone;
+ if (!hasSelection || paintSelectedTextOnly)
+ return;
+
+ Color backgroundColor = renderer()->selectionBackgroundColor();
+ if (!backgroundColor.isValid() || !backgroundColor.alpha())
+ return;
+
+ RenderStyle* style = parentRenderer->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ bool hasFill = svgStyle->hasFill();
+ bool hasStroke = svgStyle->hasStroke();
+
+ RenderStyle* selectionStyle = style;
+ if (hasSelection) {
+ selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
+ if (selectionStyle) {
+ const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
+ ASSERT(svgSelectionStyle);
+
+ if (!hasFill)
+ hasFill = svgSelectionStyle->hasFill();
+ if (!hasStroke)
+ hasStroke = svgSelectionStyle->hasStroke();
+ } else
+ selectionStyle = style;
+ }
+
+ int startPosition, endPosition;
+ selectionStartEnd(startPosition, endPosition);
+
+ int fragmentStartPosition = 0;
+ int fragmentEndPosition = 0;
+ unsigned textFragmentsSize = m_textFragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ SVGTextFragment& fragment = m_textFragments.at(i);
+ ASSERT(!m_paintingResource);
+
+ fragmentStartPosition = startPosition;
+ fragmentEndPosition = endPosition;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
+ continue;
+
+ paintInfo.context->save();
+
+ if (!fragment.transform.isIdentity())
+ paintInfo.context->concatCTM(fragment.transform);
+
+ paintInfo.context->setFillColor(backgroundColor, style->colorSpace());
+ paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor, style->colorSpace());
+
+ m_paintingResourceMode = ApplyToDefaultMode;
+ paintInfo.context->restore();
+ }
+
+ ASSERT(!m_paintingResource);
+}
+
+void SVGInlineTextBox::paint(PaintInfo& paintInfo, int, int)
+{
+ ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
+ ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+ ASSERT(truncation() == cNoTruncation);
+
+ if (renderer()->style()->visibility() != VISIBLE)
+ return;
+
+ // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox.
+ // If we ever need that for SVG, it's very easy to refactor and reuse the code.
+
+ RenderObject* parentRenderer = parent()->renderer();
+ ASSERT(parentRenderer);
+
+ bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
+ bool hasSelection = !parentRenderer->document()->printing() && selectionState() != RenderObject::SelectionNone;
+ if (!hasSelection && paintSelectedTextOnly)
+ return;
+
+ RenderStyle* style = parentRenderer->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ bool hasFill = svgStyle->hasFill();
+ bool hasStroke = svgStyle->hasStroke();
+
+ RenderStyle* selectionStyle = style;
+ if (hasSelection) {
+ selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
+ if (selectionStyle) {
+ const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
+ ASSERT(svgSelectionStyle);
+
+ if (!hasFill)
+ hasFill = svgSelectionStyle->hasFill();
+ if (!hasStroke)
+ hasStroke = svgSelectionStyle->hasStroke();
+ } else
+ selectionStyle = style;
+ }
+
+ unsigned textFragmentsSize = m_textFragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ SVGTextFragment& fragment = m_textFragments.at(i);
+ ASSERT(!m_paintingResource);
+
+ paintInfo.context->save();
+
+ if (!fragment.transform.isIdentity())
+ paintInfo.context->concatCTM(fragment.transform);
+
+ // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations.
+ int decorations = style->textDecorationsInEffect();
+ if (decorations & UNDERLINE)
+ paintDecoration(paintInfo.context, UNDERLINE, fragment);
+ if (decorations & OVERLINE)
+ paintDecoration(paintInfo.context, OVERLINE, fragment);
+
+ // Fill text
+ if (hasFill) {
+ m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode;
+ paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
+ }
+
+ // Stroke text
+ if (hasStroke) {
+ m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode;
+ paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
+ }
+
+ // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
+ if (decorations & LINE_THROUGH)
+ paintDecoration(paintInfo.context, LINE_THROUGH, fragment);
+
+ m_paintingResourceMode = ApplyToDefaultMode;
+ paintInfo.context->restore();
+ }
+
+ ASSERT(!m_paintingResource);
+}
+
+bool SVGInlineTextBox::acquirePaintingResource(GraphicsContext*& context, RenderObject* renderer, RenderStyle* style)
+{
+ ASSERT(renderer);
+ ASSERT(style);
+ ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
+
+ Color fallbackColor;
+ if (m_paintingResourceMode & ApplyToFillMode)
+ m_paintingResource = RenderSVGResource::fillPaintingResource(renderer, style, fallbackColor);
+ else if (m_paintingResourceMode & ApplyToStrokeMode)
+ m_paintingResource = RenderSVGResource::strokePaintingResource(renderer, style, fallbackColor);
+ else {
+ // We're either called for stroking or filling.
+ ASSERT_NOT_REACHED();
+ }
+
+ if (!m_paintingResource)
+ return false;
+
+ if (!m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode)) {
+ if (fallbackColor.isValid()) {
+ RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
+ fallbackResource->setColor(fallbackColor);
+
+ m_paintingResource = fallbackResource;
+ m_paintingResource->applyResource(renderer, style, context, m_paintingResourceMode);
+ }
+ }
+
+ return true;
+}
+
+void SVGInlineTextBox::releasePaintingResource(GraphicsContext*& context, const Path* path)
+{
+ ASSERT(m_paintingResource);
+
+ RenderObject* parentRenderer = parent()->renderer();
+ ASSERT(parentRenderer);
+
+ m_paintingResource->postApplyResource(parentRenderer, context, m_paintingResourceMode, path);
+ m_paintingResource = 0;
+}
+
+bool SVGInlineTextBox::prepareGraphicsContextForTextPainting(GraphicsContext*& context, TextRun& textRun, RenderStyle* style)
+{
+ bool acquiredResource = acquirePaintingResource(context, parent()->renderer(), style);
+
+#if ENABLE(SVG_FONTS)
+ // SVG Fonts need access to the painting resource used to draw the current text chunk.
+ if (acquiredResource)
+ textRun.setActivePaintingResource(m_paintingResource);
+#endif
+
+ return acquiredResource;
+}
+
+void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun)
+{
+ releasePaintingResource(context, /* path */0);
+
+#if ENABLE(SVG_FONTS)
+ textRun.setActivePaintingResource(0);
+#endif
+}
+
+TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFragment& fragment) const
+{
+ ASSERT(style);
+ ASSERT(textRenderer());
+
+ RenderText* text = textRenderer();
+ ASSERT(text);
+
+ TextRun run(text->characters() + fragment.positionListOffset
+ , fragment.length
+ , false /* allowTabs */
+ , 0 /* xPos, only relevant with allowTabs=true */
+ , 0 /* padding, only relevant for justified text, not relevant for SVG */
+ , direction() == RTL
+ , m_dirOverride || style->visuallyOrdered() /* directionalOverride */);
+
+#if ENABLE(SVG_FONTS)
+ RenderObject* parentRenderer = parent()->renderer();
+ ASSERT(parentRenderer);
+
+ run.setReferencingRenderObject(parentRenderer);
+#endif
+
+ // Disable any word/character rounding.
+ run.disableRoundingHacks();
+
+ // We handle letter & word spacing ourselves.
+ run.disableSpacing();
+ return run;
+}
+
+bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment& fragment, int& startPosition, int& endPosition) const
+{
+ if (startPosition >= endPosition)
+ return false;
+
+ int offset = static_cast<int>(fragment.positionListOffset) - start();
+ int length = static_cast<int>(fragment.length);
+
+ if (startPosition >= offset + length || endPosition <= offset)
+ return false;
+
+ if (startPosition < offset)
+ startPosition = 0;
+ else
+ startPosition -= offset;
+
+ if (endPosition > offset + length)
+ endPosition = length;
+ else {
+ ASSERT(endPosition >= offset);
+ endPosition -= offset;
+ }
+
+ ASSERT(startPosition < endPosition);
+ return true;
+}
+
+static inline float positionOffsetForDecoration(ETextDecoration decoration, const Font& font, float thickness)
+{
+ // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
+ // Compatible with Batik/Opera.
+ if (decoration == UNDERLINE)
+ return font.ascent() + thickness * 1.5f;
+ if (decoration == OVERLINE)
+ return thickness;
+ if (decoration == LINE_THROUGH)
+ return font.ascent() * 5.0f / 8.0f;
+
+ ASSERT_NOT_REACHED();
+ return 0.0f;
+}
+
+static inline float thicknessForDecoration(ETextDecoration, const Font& font)
+{
+ // FIXME: For SVG Fonts we need to use the attributes defined in the <font-face> if specified.
+ // Compatible with Batik/Opera
+ return font.size() / 20.0f;
+}
+
+static inline RenderObject* findRenderObjectDefininingTextDecoration(InlineFlowBox* parentBox)
+{
+ // Lookup first render object in parent hierarchy which has text-decoration set.
+ RenderObject* renderer = 0;
+ while (parentBox) {
+ renderer = parentBox->renderer();
+
+ if (renderer->style() && renderer->style()->textDecoration() != TDNONE)
+ break;
+
+ parentBox = parentBox->parent();
+ }
+
+ ASSERT(renderer);
+ return renderer;
+}
+
+void SVGInlineTextBox::paintDecoration(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment)
+{
+ if (textRenderer()->style()->textDecorationsInEffect() == TDNONE)
+ return;
+
+ // Find out which render style defined the text-decoration, as its fill/stroke properties have to be used for drawing instead of ours.
+ RenderObject* decorationRenderer = findRenderObjectDefininingTextDecoration(parent());
+ RenderStyle* decorationStyle = decorationRenderer->style();
+ ASSERT(decorationStyle);
+
+ if (decorationStyle->visibility() == HIDDEN)
+ return;
+
+ const SVGRenderStyle* svgDecorationStyle = decorationStyle->svgStyle();
+ ASSERT(svgDecorationStyle);
+
+ bool hasDecorationFill = svgDecorationStyle->hasFill();
+ bool hasDecorationStroke = svgDecorationStyle->hasStroke();
+
+ if (hasDecorationFill) {
+ m_paintingResourceMode = ApplyToFillMode;
+ paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
+ }
+
+ if (hasDecorationStroke) {
+ m_paintingResourceMode = ApplyToStrokeMode;
+ paintDecorationWithStyle(context, decoration, fragment, decorationRenderer);
+ }
+}
+
+void SVGInlineTextBox::paintDecorationWithStyle(GraphicsContext* context, ETextDecoration decoration, const SVGTextFragment& fragment, RenderObject* decorationRenderer)
+{
+ ASSERT(!m_paintingResource);
+ ASSERT(m_paintingResourceMode != ApplyToDefaultMode);
+
+ RenderStyle* decorationStyle = decorationRenderer->style();
+ ASSERT(decorationStyle);
+
+ const Font& font = decorationStyle->font();
+
+ // The initial y value refers to overline position.
+ float thickness = thicknessForDecoration(decoration, font);
+
+ if (fragment.width <= 0 && thickness <= 0)
+ return;
+
+ float y = fragment.y - font.ascent() + positionOffsetForDecoration(decoration, font, thickness);
+
+ Path path;
+ path.addRect(FloatRect(fragment.x, y, fragment.width, thickness));
+
+ context->save();
+
+ if (acquirePaintingResource(context, decorationRenderer, decorationStyle))
+ releasePaintingResource(context, &path);
+
+ context->restore();
+}
+
+void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition)
+{
+ const Font& font = style->font();
+ const ShadowData* shadow = style->textShadow();
+
+ FloatPoint textOrigin(fragment.x, fragment.y);
+ FloatRect shadowRect(FloatPoint(textOrigin.x(), textOrigin.y() - font.ascent()), FloatSize(fragment.width, fragment.height));
+
+ do {
+ if (!prepareGraphicsContextForTextPainting(context, textRun, style))
+ break;
+
+ FloatSize extraOffset;
+ if (shadow)
+ extraOffset = applyShadowToGraphicsContext(context, shadow, shadowRect, false /* stroked */, true /* opaque */, true /* horizontal */);
+
+ font.drawText(context, textRun, textOrigin + extraOffset, startPosition, endPosition);
+ restoreGraphicsContextAfterTextPainting(context, textRun);
+
+ if (!shadow)
+ break;
+
+ if (shadow->next())
+ context->restore();
+ else
+ context->clearShadow();
+
+ shadow = shadow->next();
+ } while (shadow);
+}
+
+void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, bool hasSelection, bool paintSelectedTextOnly)
+{
+ ASSERT(style);
+ ASSERT(selectionStyle);
+
+ int startPosition = 0;
+ int endPosition = 0;
+ if (hasSelection) {
+ selectionStartEnd(startPosition, endPosition);
+ hasSelection = mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition);
+ }
+
+ // Fast path if there is no selection, just draw the whole chunk part using the regular style
+ TextRun textRun(constructTextRun(style, fragment));
+ if (!hasSelection || startPosition >= endPosition) {
+ paintTextWithShadows(context, style, textRun, fragment, 0, fragment.length);
+ return;
+ }
+
+ // Eventually draw text using regular style until the start position of the selection
+ if (startPosition > 0 && !paintSelectedTextOnly)
+ paintTextWithShadows(context, style, textRun, fragment, 0, startPosition);
+
+ // Draw text using selection style from the start to the end position of the selection
+ if (style != selectionStyle)
+ SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle);
+
+ TextRun selectionTextRun(constructTextRun(selectionStyle, fragment));
+ paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition);
+
+ if (style != selectionStyle)
+ SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style);
+
+ // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part
+ if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly)
+ paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length);
+}
+
+IntRect SVGInlineTextBox::calculateBoundaries() const
+{
+ FloatRect textRect;
+
+ RenderText* textRenderer = this->textRenderer();
+ ASSERT(textRenderer);
+
+ RenderStyle* style = textRenderer->style();
+ ASSERT(style);
+
+ int baseline = baselinePosition(AlphabeticBaseline);
+ int heightDifference = baseline - style->font().ascent();
+
+ unsigned textFragmentsSize = m_textFragments.size();
+ for (unsigned i = 0; i < textFragmentsSize; ++i) {
+ const SVGTextFragment& fragment = m_textFragments.at(i);
+ FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height + heightDifference);
+
+ if (!fragment.transform.isIdentity())
+ fragmentRect = fragment.transform.mapRect(fragmentRect);
+
+ textRect.unite(fragmentRect);
+ }
+
+ return enclosingIntRect(textRect);
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.h b/Source/WebCore/rendering/svg/SVGInlineTextBox.h
new file mode 100644
index 0000000..acc5e9f
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2007 Rob Buis <buis@kde.org>
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef SVGInlineTextBox_h
+#define SVGInlineTextBox_h
+
+#if ENABLE(SVG)
+#include "InlineTextBox.h"
+#include "SVGTextLayoutEngine.h"
+
+namespace WebCore {
+
+class RenderSVGResource;
+class SVGRootInlineBox;
+
+class SVGInlineTextBox : public InlineTextBox {
+public:
+ SVGInlineTextBox(RenderObject*);
+
+ virtual bool isSVGInlineTextBox() const { return true; }
+
+ virtual int virtualLogicalHeight() const { return m_logicalHeight; }
+ void setLogicalHeight(int height) { m_logicalHeight = height; }
+
+ virtual int selectionTop() { return m_y; }
+ virtual int selectionHeight() { return m_logicalHeight; }
+ virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const;
+ virtual int positionForOffset(int offset) const;
+
+ void paintSelectionBackground(PaintInfo&);
+ virtual void paint(PaintInfo&, int tx, int ty);
+ virtual IntRect selectionRect(int absx, int absy, int startPosition, int endPosition);
+
+ bool mapStartEndPositionsIntoFragmentCoordinates(const SVGTextFragment&, int& startPosition, int& endPosition) const;
+
+ virtual IntRect calculateBoundaries() const;
+
+ void clearTextFragments() { m_textFragments.clear(); }
+ Vector<SVGTextFragment>& textFragments() { return m_textFragments; }
+ const Vector<SVGTextFragment>& textFragments() const { return m_textFragments; }
+
+ bool startsNewTextChunk() const { return m_startsNewTextChunk; }
+ void setStartsNewTextChunk(bool newTextChunk) { m_startsNewTextChunk = newTextChunk; }
+
+ int offsetForPositionInFragment(const SVGTextFragment&, float position, bool includePartialGlyphs) const;
+ FloatRect selectionRectForTextFragment(const SVGTextFragment&, int fragmentStartPosition, int fragmentEndPosition, RenderStyle*);
+
+private:
+ TextRun constructTextRun(RenderStyle*, const SVGTextFragment&) const;
+
+ bool acquirePaintingResource(GraphicsContext*&, RenderObject*, RenderStyle*);
+ void releasePaintingResource(GraphicsContext*&, const Path*);
+
+ bool prepareGraphicsContextForTextPainting(GraphicsContext*&, TextRun&, RenderStyle*);
+ void restoreGraphicsContextAfterTextPainting(GraphicsContext*&, TextRun&);
+
+ void paintDecoration(GraphicsContext*, ETextDecoration, const SVGTextFragment&);
+ void paintDecorationWithStyle(GraphicsContext*, ETextDecoration, const SVGTextFragment&, RenderObject* decorationRenderer);
+ void paintTextWithShadows(GraphicsContext*, RenderStyle*, TextRun&, const SVGTextFragment&, int startPosition, int endPosition);
+ void paintText(GraphicsContext*, RenderStyle*, RenderStyle* selectionStyle, const SVGTextFragment&, bool hasSelection, bool paintSelectedTextOnly);
+
+private:
+ int m_logicalHeight;
+ int m_paintingResourceMode;
+ bool m_startsNewTextChunk : 1;
+ RenderSVGResource* m_paintingResource;
+ Vector<SVGTextFragment> m_textFragments;
+};
+
+} // namespace WebCore
+
+#endif
+#endif // SVGInlineTextBox_h
diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp
new file mode 100644
index 0000000..49c76de
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * Copyright (C) 2006 Apple Computer Inc.
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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 "SVGRootInlineBox.h"
+
+#if ENABLE(SVG)
+#include "GraphicsContext.h"
+#include "RenderBlock.h"
+#include "RenderSVGInlineText.h"
+#include "SVGInlineFlowBox.h"
+#include "SVGInlineTextBox.h"
+#include "SVGNames.h"
+#include "SVGRenderSupport.h"
+#include "SVGTextPositioningElement.h"
+
+namespace WebCore {
+
+void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int)
+{
+ ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
+ ASSERT(!paintInfo.context->paintingDisabled());
+
+ RenderObject* boxRenderer = renderer();
+ ASSERT(boxRenderer);
+
+ bool isPrinting = renderer()->document()->printing();
+ bool hasSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
+
+ PaintInfo childPaintInfo(paintInfo);
+ if (hasSelection) {
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox())
+ static_cast<SVGInlineTextBox*>(child)->paintSelectionBackground(childPaintInfo);
+ else if (child->isSVGInlineFlowBox())
+ static_cast<SVGInlineFlowBox*>(child)->paintSelectionBackground(childPaintInfo);
+ }
+ }
+
+ childPaintInfo.context->save();
+
+ if (SVGRenderSupport::prepareToRenderSVGContent(boxRenderer, childPaintInfo)) {
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox())
+ SVGInlineFlowBox::computeTextMatchMarkerRectForRenderer(toRenderSVGInlineText(static_cast<SVGInlineTextBox*>(child)->textRenderer()));
+
+ child->paint(childPaintInfo, 0, 0);
+ }
+ }
+
+ SVGRenderSupport::finishRenderSVGContent(boxRenderer, childPaintInfo, paintInfo.context);
+ childPaintInfo.context->restore();
+}
+
+void SVGRootInlineBox::computePerCharacterLayoutInformation()
+{
+ // Perform SVG text layout phase two (see SVGTextLayoutEngine for details).
+ SVGTextLayoutEngine characterLayout;
+ layoutCharactersInTextBoxes(this, characterLayout);
+
+ // Perform SVG text layout phase three (see SVGTextChunkBuilder for details).
+ characterLayout.finishLayout();
+
+ // Perform SVG text layout phase four
+ // Position & resize all SVGInlineText/FlowBoxes in the inline box tree, resize the root box as well as the RenderSVGText parent block.
+ layoutChildBoxes(this);
+ layoutRootBox();
+}
+
+void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGTextLayoutEngine& characterLayout)
+{
+ for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox()) {
+ ASSERT(child->renderer());
+ ASSERT(child->renderer()->isSVGInlineText());
+
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
+ characterLayout.layoutInlineTextBox(textBox);
+ } else {
+ ASSERT(child->isInlineFlowBox());
+
+ // Skip generated content.
+ Node* node = child->renderer()->node();
+ if (!node)
+ continue;
+
+ SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
+ bool isTextPath = node->hasTagName(SVGNames::textPathTag);
+ if (isTextPath) {
+ // Build text chunks for all <textPath> children, using the line layout algorithm.
+ // This is needeed as text-anchor is just an additional startOffset for text paths.
+ SVGTextLayoutEngine lineLayout;
+ layoutCharactersInTextBoxes(flowBox, lineLayout);
+ characterLayout.beginTextPathLayout(child->renderer(), lineLayout);
+ }
+
+ layoutCharactersInTextBoxes(flowBox, characterLayout);
+
+ if (isTextPath)
+ characterLayout.endTextPathLayout();
+ }
+ }
+}
+
+void SVGRootInlineBox::layoutChildBoxes(InlineFlowBox* start)
+{
+ for (InlineBox* child = start->firstChild(); child; child = child->nextOnLine()) {
+ if (child->isSVGInlineTextBox()) {
+ ASSERT(child->renderer());
+ ASSERT(child->renderer()->isSVGInlineText());
+
+ SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(child);
+ IntRect boxRect = textBox->calculateBoundaries();
+ textBox->setX(boxRect.x());
+ textBox->setY(boxRect.y());
+ textBox->setLogicalWidth(boxRect.width());
+ textBox->setLogicalHeight(boxRect.height());
+ } else {
+ ASSERT(child->isInlineFlowBox());
+
+ // Skip generated content.
+ if (!child->renderer()->node())
+ continue;
+
+ SVGInlineFlowBox* flowBox = static_cast<SVGInlineFlowBox*>(child);
+ layoutChildBoxes(flowBox);
+
+ IntRect boxRect = flowBox->calculateBoundaries();
+ flowBox->setX(boxRect.x());
+ flowBox->setY(boxRect.y());
+ flowBox->setLogicalWidth(boxRect.width());
+ flowBox->setLogicalHeight(boxRect.height());
+ }
+ }
+}
+
+void SVGRootInlineBox::layoutRootBox()
+{
+ RenderBlock* parentBlock = block();
+ ASSERT(parentBlock);
+
+ IntRect childRect;
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ // Skip generated content.
+ if (!child->renderer()->node())
+ continue;
+ childRect.unite(child->calculateBoundaries());
+ }
+
+ int xBlock = childRect.x();
+ int yBlock = childRect.y();
+ int widthBlock = childRect.width();
+ int heightBlock = childRect.height();
+
+ // Finally, assign the root block position, now that all content is laid out.
+ parentBlock->setLocation(xBlock, yBlock);
+ parentBlock->setWidth(widthBlock);
+ parentBlock->setHeight(heightBlock);
+
+ // Position all children relative to the parent block.
+ for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) {
+ // Skip generated content.
+ if (!child->renderer()->node())
+ continue;
+ child->adjustPosition(-xBlock, -yBlock);
+ }
+
+ // Position ourselves.
+ setX(0);
+ setY(0);
+ setLogicalWidth(widthBlock);
+ setLogicalHeight(heightBlock);
+ setBlockLogicalHeight(heightBlock);
+ setLineTopBottomPositions(0, heightBlock);
+}
+
+InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point)
+{
+ InlineBox* firstLeaf = firstLeafChild();
+ InlineBox* lastLeaf = lastLeafChild();
+ if (firstLeaf == lastLeaf)
+ return firstLeaf;
+
+ // FIXME: Check for vertical text!
+ InlineBox* closestLeaf = 0;
+ for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
+ if (point.y() < leaf->m_y)
+ continue;
+ if (point.y() > leaf->m_y + leaf->virtualLogicalHeight())
+ continue;
+
+ closestLeaf = leaf;
+ if (point.x() < leaf->m_x + leaf->m_logicalWidth)
+ return leaf;
+ }
+
+ return closestLeaf ? closestLeaf : lastLeaf;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.h b/Source/WebCore/rendering/svg/SVGRootInlineBox.h
new file mode 100644
index 0000000..418c289
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz>
+ * (C) 2006 Apple Computer Inc.
+ * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef SVGRootInlineBox_h
+#define SVGRootInlineBox_h
+
+#if ENABLE(SVG)
+#include "RootInlineBox.h"
+#include "SVGRenderSupport.h"
+#include "SVGTextLayoutEngine.h"
+
+namespace WebCore {
+
+class SVGInlineTextBox;
+
+class SVGRootInlineBox : public RootInlineBox {
+public:
+ SVGRootInlineBox(RenderBlock* block)
+ : RootInlineBox(block)
+ , m_logicalHeight(0)
+ {
+ }
+
+ virtual bool isSVGRootInlineBox() const { return true; }
+
+ virtual int virtualLogicalHeight() const { return m_logicalHeight; }
+ void setLogicalHeight(int height) { m_logicalHeight = height; }
+
+ virtual void paint(PaintInfo&, int tx, int ty);
+
+ void computePerCharacterLayoutInformation();
+
+ virtual FloatRect objectBoundingBox() const { return FloatRect(); }
+ virtual FloatRect repaintRectInLocalCoordinates() const { return FloatRect(); }
+
+ InlineBox* closestLeafChildForPosition(const IntPoint&);
+
+private:
+ void layoutCharactersInTextBoxes(InlineFlowBox*, SVGTextLayoutEngine&);
+ void layoutChildBoxes(InlineFlowBox*);
+ void layoutRootBox();
+
+private:
+ int m_logicalHeight;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+
+#endif // SVGRootInlineBox_h
diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.cpp b/Source/WebCore/rendering/svg/SVGTextChunk.cpp
new file mode 100644
index 0000000..5dea6ad
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextChunk.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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)
+#include "SVGTextChunk.h"
+
+#include "SVGInlineTextBox.h"
+#include "SVGTextFragment.h"
+
+namespace WebCore {
+
+SVGTextChunk::SVGTextChunk(bool isVerticalText, ETextAnchor textAnchor, SVGTextContentElement::SVGLengthAdjustType lengthAdjust, float desiredTextLength)
+ : m_isVerticalText(isVerticalText)
+ , m_textAnchor(textAnchor)
+ , m_lengthAdjust(lengthAdjust)
+ , m_desiredTextLength(desiredTextLength)
+{
+}
+
+void SVGTextChunk::calculateLength(float& length, unsigned& characters) const
+{
+ SVGTextFragment* lastFragment = 0;
+
+ unsigned boxCount = m_boxes.size();
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = m_boxes.at(boxPosition);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+
+ unsigned size = fragments.size();
+ if (!size)
+ continue;
+
+ for (unsigned i = 0; i < size; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+ characters += fragment.length;
+
+ if (m_isVerticalText)
+ length += fragment.height;
+ else
+ length += fragment.width;
+
+ if (!lastFragment) {
+ lastFragment = &fragment;
+ continue;
+ }
+
+ // Resepect gap between chunks.
+ if (m_isVerticalText)
+ length += fragment.y - (lastFragment->y + lastFragment->height);
+ else
+ length += fragment.x - (lastFragment->x + lastFragment->width);
+
+ lastFragment = &fragment;
+ }
+ }
+}
+
+float SVGTextChunk::calculateTextAnchorShift(float length) const
+{
+ switch (m_textAnchor) {
+ case TA_START:
+ return 0;
+ case TA_MIDDLE:
+ return -length / 2;
+ case TA_END:
+ return -length;
+ };
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.h b/Source/WebCore/rendering/svg/SVGTextChunk.h
new file mode 100644
index 0000000..ebe6d81
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextChunk.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextChunk_h
+#define SVGTextChunk_h
+
+#if ENABLE(SVG)
+#include "SVGRenderStyleDefs.h"
+#include "SVGTextContentElement.h"
+
+namespace WebCore {
+
+class SVGInlineTextBox;
+
+// A SVGTextChunk describes a range of SVGTextFragments, see the SVG spec definition of a "text chunk".
+class SVGTextChunk {
+public:
+ SVGTextChunk(bool isVerticalText, ETextAnchor, SVGTextContentElement::SVGLengthAdjustType, float desiredTextLength);
+
+ void calculateLength(float& length, unsigned& characters) const;
+ float calculateTextAnchorShift(float length) const;
+
+ bool isVerticalText() const { return m_isVerticalText; }
+ ETextAnchor textAnchor() const { return m_textAnchor; }
+ SVGTextContentElement::SVGLengthAdjustType lengthAdjust() const { return m_lengthAdjust; }
+ float desiredTextLength() const { return m_desiredTextLength; }
+
+ Vector<SVGInlineTextBox*>& boxes() { return m_boxes; }
+ const Vector<SVGInlineTextBox*>& boxes() const { return m_boxes; }
+
+ bool hasDesiredTextLength() const { return m_lengthAdjust != SVGTextContentElement::LENGTHADJUST_UNKNOWN && m_desiredTextLength > 0; }
+ bool hasTextAnchor() const { return m_textAnchor != TA_START; }
+
+private:
+ // Contains all SVGInlineTextBoxes this chunk spans.
+ Vector<SVGInlineTextBox*> m_boxes;
+
+ // writing-mode specific property.
+ bool m_isVerticalText;
+
+ // text-anchor specific properties.
+ ETextAnchor m_textAnchor;
+
+ // textLength/lengthAdjust specific properties.
+ SVGTextContentElement::SVGLengthAdjustType m_lengthAdjust;
+ float m_desiredTextLength;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
new file mode 100644
index 0000000..bbbae6c
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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)
+#include "SVGTextChunkBuilder.h"
+
+#include "RenderSVGInlineText.h"
+#include "SVGElement.h"
+#include "SVGInlineTextBox.h"
+
+namespace WebCore {
+
+SVGTextChunkBuilder::SVGTextChunkBuilder()
+{
+}
+
+void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const
+{
+ DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ());
+ if (!m_textBoxTransformations.contains(textBox)) {
+ transform = s_identityTransform;
+ return;
+ }
+
+ transform = m_textBoxTransformations.get(textBox);
+}
+
+void SVGTextChunkBuilder::buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
+{
+ if (lineLayoutBoxes.isEmpty())
+ return;
+
+ bool foundStart = false;
+ unsigned lastChunkStartPosition = 0;
+ unsigned boxPosition = 0;
+ unsigned boxCount = lineLayoutBoxes.size();
+ for (; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = lineLayoutBoxes.at(boxPosition);
+ if (!textBox->startsNewTextChunk())
+ continue;
+
+ if (!foundStart) {
+ lastChunkStartPosition = boxPosition;
+ foundStart = true;
+ } else {
+ ASSERT(boxPosition > lastChunkStartPosition);
+ addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
+ lastChunkStartPosition = boxPosition;
+ }
+ }
+
+ if (!foundStart)
+ return;
+
+ if (boxPosition - lastChunkStartPosition > 0)
+ addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
+}
+
+void SVGTextChunkBuilder::layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
+{
+ buildTextChunks(lineLayoutBoxes);
+ if (m_textChunks.isEmpty())
+ return;
+
+ unsigned chunkCount = m_textChunks.size();
+ for (unsigned i = 0; i < chunkCount; ++i)
+ processTextChunk(m_textChunks.at(i));
+
+ m_textChunks.clear();
+}
+
+void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
+{
+ SVGInlineTextBox* textBox = lineLayoutBoxes.at(boxStart);
+ ASSERT(textBox);
+
+ RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
+ ASSERT(textRenderer);
+
+ const RenderStyle* style = textRenderer->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN;
+ float desiredTextLength = 0;
+
+ if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) {
+ lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust());
+ desiredTextLength = textContentElement->textLength().value(textContentElement);
+ }
+
+ SVGTextChunk chunk(svgStyle->isVerticalWritingMode(), svgStyle->textAnchor(), lengthAdjust, desiredTextLength);
+
+ Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
+ for (unsigned i = boxStart; i < boxStart + boxCount; ++i)
+ boxes.append(lineLayoutBoxes.at(i));
+
+ m_textChunks.append(chunk);
+}
+
+void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)
+{
+ bool processTextLength = chunk.hasDesiredTextLength();
+ bool processTextAnchor = chunk.hasTextAnchor();
+ if (!processTextAnchor && !processTextLength)
+ return;
+
+ const Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
+ unsigned boxCount = boxes.size();
+ if (!boxCount)
+ return;
+
+ // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).
+ float chunkLength = 0;
+ unsigned chunkCharacters = 0;
+ chunk.calculateLength(chunkLength, chunkCharacters);
+
+ bool isVerticalText = chunk.isVerticalText();
+ if (processTextLength) {
+ if (chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) {
+ float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
+ unsigned atCharacter = 0;
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments();
+ if (fragments.isEmpty())
+ continue;
+ processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
+ }
+ } else {
+ ASSERT(chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS);
+ float scale = chunk.desiredTextLength() / chunkLength;
+ AffineTransform spacingAndGlyphsTransform;
+
+ bool foundFirstFragment = false;
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = boxes.at(boxPosition);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ if (fragments.isEmpty())
+ continue;
+
+ if (!foundFirstFragment) {
+ foundFirstFragment = true;
+ buildSpacingAndGlyphsTransform(isVerticalText, scale, fragments.first(), spacingAndGlyphsTransform);
+ }
+
+ m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
+ }
+ }
+ }
+
+ if (!processTextAnchor)
+ return;
+
+ // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.
+ if (processTextLength && chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) {
+ chunkLength = 0;
+ chunkCharacters = 0;
+ chunk.calculateLength(chunkLength, chunkCharacters);
+ }
+
+ float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments();
+ if (fragments.isEmpty())
+ continue;
+ processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
+ }
+}
+
+void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)
+{
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+
+ if (isVerticalText)
+ fragment.y += textLengthShift * atCharacter;
+ else
+ fragment.x += textLengthShift * atCharacter;
+
+ atCharacter += fragment.length;
+ }
+}
+
+void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments)
+{
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+
+ if (isVerticalText)
+ fragment.y += textAnchorShift;
+ else
+ fragment.x += textAnchorShift;
+ }
+}
+
+void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)
+{
+ spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+
+ if (isVerticalText)
+ spacingAndGlyphsTransform.scaleNonUniform(1, scale);
+ else
+ spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+
+ spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h
new file mode 100644
index 0000000..36342e7
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextChunkBuilder_h
+#define SVGTextChunkBuilder_h
+
+#if ENABLE(SVG)
+#include "SVGTextChunk.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class SVGInlineTextBox;
+struct SVGTextFragment;
+
+// SVGTextChunkBuilder performs the third layout phase for SVG text.
+//
+// Phase one built the layout information from the SVG DOM stored in the RenderSVGInlineText objects (SVGTextLayoutAttributes).
+// Phase two performed the actual per-character layout, computing the final positions for each character, stored in the SVGInlineTextBox objects (SVGTextFragment).
+// Phase three performs all modifications that have to be applied to each individual text chunk (text-anchor & textLength).
+
+class SVGTextChunkBuilder : public Noncopyable {
+public:
+ SVGTextChunkBuilder();
+
+ const Vector<SVGTextChunk>& textChunks() const { return m_textChunks; }
+ void transformationForTextBox(SVGInlineTextBox*, AffineTransform&) const;
+
+ void buildTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes);
+ void layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes);
+
+private:
+ void addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxPosition, unsigned boxCount);
+ void processTextChunk(const SVGTextChunk&);
+
+ void processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>&, unsigned& atCharacter);
+ void processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>&);
+ void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&, AffineTransform&);
+
+private:
+ Vector<SVGTextChunk> m_textChunks;
+ HashMap<SVGInlineTextBox*, AffineTransform> m_textBoxTransformations;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextFragment.h b/Source/WebCore/rendering/svg/SVGTextFragment.h
new file mode 100644
index 0000000..2e520da
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextFragment.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextFragment_h
+#define SVGTextFragment_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+
+namespace WebCore {
+
+// A SVGTextFragment describes a text fragment of a RenderSVGInlineText which can be rendered at once.
+struct SVGTextFragment {
+ SVGTextFragment()
+ : positionListOffset(0)
+ , length(0)
+ , x(0)
+ , y(0)
+ , width(0)
+ , height(0)
+ {
+ }
+
+ // The first rendered character starts at RenderSVGInlineText::characters() + positionListOffset.
+ unsigned positionListOffset;
+ unsigned length;
+
+ float x;
+ float y;
+ float width;
+ float height;
+
+ // Includes rotation/glyph-orientation-(horizontal|vertical) transforms, lengthAdjust="spacingAndGlyphs" (for textPath only),
+ // as well as orientation related shifts (see SVGTextLayoutEngine, which builds this transformation).
+ AffineTransform transform;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp
new file mode 100644
index 0000000..3037b77
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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)
+#include "SVGTextLayoutAttributes.h"
+
+#include <stdio.h>
+#include <wtf/text/CString.h>
+
+namespace WebCore {
+
+SVGTextLayoutAttributes::SVGTextLayoutAttributes()
+{
+}
+
+void SVGTextLayoutAttributes::reserveCapacity(unsigned length)
+{
+ m_xValues.reserveCapacity(length);
+ m_yValues.reserveCapacity(length);
+ m_dxValues.reserveCapacity(length);
+ m_dyValues.reserveCapacity(length);
+ m_rotateValues.reserveCapacity(length);
+}
+
+float SVGTextLayoutAttributes::emptyValue()
+{
+ static float s_emptyValue = std::numeric_limits<float>::max() - 1;
+ return s_emptyValue;
+}
+
+static inline void dumpLayoutVector(const Vector<float>& values)
+{
+ if (values.isEmpty()) {
+ fprintf(stderr, "empty");
+ return;
+ }
+
+ unsigned size = values.size();
+ for (unsigned i = 0; i < size; ++i) {
+ float value = values.at(i);
+ if (value == SVGTextLayoutAttributes::emptyValue())
+ fprintf(stderr, "x ");
+ else
+ fprintf(stderr, "%lf ", value);
+ }
+}
+
+void SVGTextLayoutAttributes::dump() const
+{
+ fprintf(stderr, "x values: ");
+ dumpLayoutVector(m_xValues);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "y values: ");
+ dumpLayoutVector(m_yValues);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "dx values: ");
+ dumpLayoutVector(m_dxValues);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "dy values: ");
+ dumpLayoutVector(m_dyValues);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "rotate values: ");
+ dumpLayoutVector(m_rotateValues);
+ fprintf(stderr, "\n");
+
+ fprintf(stderr, "character data values:\n");
+ unsigned textMetricsSize = m_textMetricsValues.size();
+ for (unsigned i = 0; i < textMetricsSize; ++i) {
+ const SVGTextMetrics& metrics = m_textMetricsValues.at(i);
+ fprintf(stderr, "| {length=%i, glyphName='%s', unicodeString='%s', width=%lf, height=%lf}\n",
+ metrics.length(), metrics.glyph().name.utf8().data(), metrics.glyph().unicodeString.utf8().data(), metrics.width(), metrics.height());
+ }
+ fprintf(stderr, "\n");
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h
new file mode 100644
index 0000000..d08d5b7
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextLayoutAttributes_h
+#define SVGTextLayoutAttributes_h
+
+#if ENABLE(SVG)
+#include "SVGTextMetrics.h"
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class SVGTextLayoutAttributes {
+public:
+ SVGTextLayoutAttributes();
+
+ void reserveCapacity(unsigned length);
+ void dump() const;
+
+ static float emptyValue();
+
+ Vector<float>& xValues() { return m_xValues; }
+ const Vector<float>& xValues() const { return m_xValues; }
+
+ Vector<float>& yValues() { return m_yValues; }
+ const Vector<float>& yValues() const { return m_yValues; }
+
+ Vector<float>& dxValues() { return m_dxValues; }
+ const Vector<float>& dxValues() const { return m_dxValues; }
+
+ Vector<float>& dyValues() { return m_dyValues; }
+ const Vector<float>& dyValues() const { return m_dyValues; }
+
+ Vector<float>& rotateValues() { return m_rotateValues; }
+ const Vector<float>& rotateValues() const { return m_rotateValues; }
+
+ Vector<SVGTextMetrics>& textMetricsValues() { return m_textMetricsValues; }
+ const Vector<SVGTextMetrics>& textMetricsValues() const { return m_textMetricsValues; }
+
+private:
+ Vector<float> m_xValues;
+ Vector<float> m_yValues;
+ Vector<float> m_dxValues;
+ Vector<float> m_dyValues;
+ Vector<float> m_rotateValues;
+ Vector<SVGTextMetrics> m_textMetricsValues;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
new file mode 100644
index 0000000..3122912
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
@@ -0,0 +1,308 @@
+/*
+ * 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)
+#include "SVGTextLayoutAttributesBuilder.h"
+
+#include "RenderSVGInlineText.h"
+#include "RenderSVGText.h"
+#include "SVGTextPositioningElement.h"
+
+// Set to a value > 0 to dump the text layout attributes
+#define DUMP_TEXT_LAYOUT_ATTRIBUTES 0
+
+namespace WebCore {
+
+SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
+{
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot)
+{
+ ASSERT(textRoot);
+ m_scopes.clear();
+
+ // Build list of x/y/dx/dy/rotate values for each subtree element that may define these values (tspan/textPath etc).
+ unsigned atCharacter = 0;
+ UChar lastCharacter = '\0';
+ buildLayoutScopes(textRoot, atCharacter, lastCharacter);
+
+ if (!atCharacter)
+ return;
+
+ // Build list of x/y/dx/dy/rotate values for the outermost <text> element.
+ buildOutermostLayoutScope(textRoot, atCharacter);
+
+ // Propagate layout attributes to each RenderSVGInlineText object.
+ atCharacter = 0;
+ lastCharacter = '\0';
+ propagateLayoutAttributes(textRoot, atCharacter, lastCharacter);
+}
+
+static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength)
+{
+ ASSERT(lengthContext);
+
+ unsigned length = list.size();
+ if (length > textContentLength)
+ length = textContentLength;
+ floatValues.reserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i) {
+ const SVGLength& length = list.at(i);
+ floatValues.append(length.value(lengthContext));
+ }
+}
+
+static inline void extractFloatValuesFromSVGNumberList(const SVGNumberList& list, Vector<float>& floatValues, unsigned textContentLength)
+{
+ unsigned length = list.size();
+ if (length > textContentLength)
+ length = textContentLength;
+ floatValues.reserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i)
+ floatValues.append(list.at(i));
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutScope(LayoutScope& scope, RenderObject* renderer, unsigned textContentStart, unsigned textContentLength) const
+{
+ ASSERT(renderer);
+ ASSERT(renderer->style());
+
+ scope.textContentStart = textContentStart;
+ scope.textContentLength = textContentLength;
+
+ SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(renderer);
+ if (!element)
+ return;
+
+ SVGTextLayoutAttributes& attributes = scope.attributes;
+ extractFloatValuesFromSVGLengthList(element, element->x(), attributes.xValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->y(), attributes.yValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->dx(), attributes.dxValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->dy(), attributes.dyValues(), textContentLength);
+ extractFloatValuesFromSVGNumberList(element->rotate(), attributes.rotateValues(), textContentLength);
+
+ // The last rotation value spans the whole scope.
+ Vector<float>& rotateValues = attributes.rotateValues();
+ if (rotateValues.isEmpty())
+ return;
+
+ unsigned rotateValuesSize = rotateValues.size();
+ if (rotateValuesSize == textContentLength)
+ return;
+
+ float lastRotation = rotateValues.last();
+
+ rotateValues.resize(textContentLength);
+ for (unsigned i = rotateValuesSize; i < textContentLength; ++i)
+ rotateValues.at(i) = lastRotation;
+}
+
+static inline bool characterIsSpace(const UChar& character)
+{
+ return character == ' ';
+}
+
+static inline bool characterIsSpaceOrNull(const UChar& character)
+{
+ return character == ' ' || character == '\0';
+}
+
+static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style)
+{
+ ASSERT(style);
+ return style->whiteSpace() == PRE;
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutScopes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter)
+{
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ if (child->isSVGInlineText()) {
+ RenderSVGInlineText* text = toRenderSVGInlineText(child);
+
+ if (!shouldPreserveAllWhiteSpace(text->style())) {
+ const UChar* characters = text->characters();
+ unsigned textLength = text->textLength();
+ for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
+ const UChar& currentCharacter = characters[textPosition];
+ if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter))
+ continue;
+
+ lastCharacter = currentCharacter;
+ ++atCharacter;
+ }
+ } else
+ atCharacter += text->textLength();
+
+ continue;
+ }
+
+ if (!child->isSVGInline())
+ continue;
+
+ unsigned textContentStart = atCharacter;
+ buildLayoutScopes(child, atCharacter, lastCharacter);
+
+ LayoutScope scope;
+ buildLayoutScope(scope, child, textContentStart, atCharacter - textContentStart);
+ m_scopes.append(scope);
+ }
+}
+
+void SVGTextLayoutAttributesBuilder::buildOutermostLayoutScope(RenderSVGText* textRoot, unsigned textLength)
+{
+ LayoutScope scope;
+ buildLayoutScope(scope, textRoot, 0, textLength);
+
+ // Handle <text> x/y default attributes.
+ Vector<float>& xValues = scope.attributes.xValues();
+ if (xValues.isEmpty())
+ xValues.append(0);
+
+ Vector<float>& yValues = scope.attributes.yValues();
+ if (yValues.isEmpty())
+ yValues.append(0);
+
+ m_scopes.prepend(scope);
+}
+
+void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const
+{
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ if (child->isSVGInlineText()) {
+ RenderSVGInlineText* text = toRenderSVGInlineText(child);
+ const UChar* characters = text->characters();
+ unsigned textLength = text->textLength();
+ bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());
+
+ SVGTextLayoutAttributes attributes;
+ attributes.reserveCapacity(textLength);
+
+ unsigned valueListPosition = atCharacter;
+ unsigned metricsLength = 1;
+ for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
+ const UChar& currentCharacter = characters[textPosition];
+
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
+ metricsLength = metrics.length();
+
+ if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
+ assignEmptyLayoutAttributesForCharacter(attributes);
+ attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics());
+ continue;
+ }
+
+ assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition);
+
+ if (metricsLength > 1) {
+ for (unsigned i = 0; i < metricsLength - 1; ++i)
+ assignEmptyLayoutAttributesForCharacter(attributes);
+ }
+
+ lastCharacter = currentCharacter;
+ valueListPosition += metricsLength;
+ }
+
+#if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
+ fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
+ attributes.dump();
+#endif
+
+ text->storeLayoutAttributes(attributes);
+ atCharacter = valueListPosition;
+ continue;
+ }
+
+ if (!child->isSVGInline())
+ continue;
+
+ propagateLayoutAttributes(child, atCharacter, lastCharacter);
+ }
+}
+
+float SVGTextLayoutAttributesBuilder::nextLayoutValue(LayoutValueType type, unsigned atCharacter) const
+{
+ for (int i = m_scopes.size() - 1; i >= 0; --i) {
+ const LayoutScope& scope = m_scopes.at(i);
+ if (scope.textContentStart > atCharacter || scope.textContentStart + scope.textContentLength < atCharacter)
+ continue;
+
+ const Vector<float>* valuesPointer = 0;
+ switch (type) {
+ case XValueAttribute:
+ valuesPointer = &scope.attributes.xValues();
+ break;
+ case YValueAttribute:
+ valuesPointer = &scope.attributes.yValues();
+ break;
+ case DxValueAttribute:
+ valuesPointer = &scope.attributes.dxValues();
+ break;
+ case DyValueAttribute:
+ valuesPointer = &scope.attributes.dyValues();
+ break;
+ case RotateValueAttribute:
+ valuesPointer = &scope.attributes.rotateValues();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ ASSERT(valuesPointer);
+ const Vector<float>& values = *valuesPointer;
+ if (values.isEmpty())
+ continue;
+
+ unsigned position = atCharacter - scope.textContentStart;
+ if (position >= values.size())
+ continue;
+
+ return values.at(position);
+ }
+
+ return SVGTextLayoutAttributes::emptyValue();
+}
+
+void SVGTextLayoutAttributesBuilder::assignLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes, SVGTextMetrics& metrics, unsigned valueListPosition) const
+{
+ attributes.xValues().append(nextLayoutValue(XValueAttribute, valueListPosition));
+ attributes.yValues().append(nextLayoutValue(YValueAttribute, valueListPosition));
+ attributes.dxValues().append(nextLayoutValue(DxValueAttribute, valueListPosition));
+ attributes.dyValues().append(nextLayoutValue(DyValueAttribute, valueListPosition));
+ attributes.rotateValues().append(nextLayoutValue(RotateValueAttribute, valueListPosition));
+ attributes.textMetricsValues().append(metrics);
+}
+
+void SVGTextLayoutAttributesBuilder::assignEmptyLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes) const
+{
+ attributes.xValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.yValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.dxValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.dyValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.rotateValues().append(SVGTextLayoutAttributes::emptyValue());
+ // This doesn't add an empty value to textMetricsValues() on purpose!
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h
new file mode 100644
index 0000000..f29ac64
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextLayoutAttributesBuilder_h
+#define SVGTextLayoutAttributesBuilder_h
+
+#if ENABLE(SVG)
+#include "SVGTextLayoutAttributes.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class RenderObject;
+class RenderSVGText;
+
+// SVGTextLayoutAttributesBuilder performs the first layout phase for SVG text.
+//
+// It extracts the x/y/dx/dy/rotate values from the SVGTextPositioningElements in the DOM,
+// measures all characters in the RenderSVGText subtree and extracts kerning/ligature information.
+// These values are propagated to the corresponding RenderSVGInlineText renderers.
+// The first layout phase only extracts the relevant information needed in RenderBlockLineLayout
+// to create the InlineBox tree based on text chunk boundaries & BiDi information.
+// The second layout phase is carried out by SVGTextLayoutEngine.
+
+class SVGTextLayoutAttributesBuilder : public Noncopyable {
+public:
+ SVGTextLayoutAttributesBuilder();
+ void buildLayoutAttributesForTextSubtree(RenderSVGText*);
+
+private:
+ struct LayoutScope {
+ LayoutScope()
+ : textContentStart(0)
+ , textContentLength(0)
+ {
+ }
+
+ unsigned textContentStart;
+ unsigned textContentLength;
+ SVGTextLayoutAttributes attributes;
+ };
+
+ void buildLayoutScope(LayoutScope&, RenderObject*, unsigned textContentStart, unsigned textContentLength) const;
+ void buildLayoutScopes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter);
+ void buildOutermostLayoutScope(RenderSVGText*, unsigned textLength);
+ void propagateLayoutAttributes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter) const;
+
+ enum LayoutValueType {
+ XValueAttribute,
+ YValueAttribute,
+ DxValueAttribute,
+ DyValueAttribute,
+ RotateValueAttribute
+ };
+
+ float nextLayoutValue(LayoutValueType, unsigned atCharacter) const;
+ void assignLayoutAttributesForCharacter(SVGTextLayoutAttributes&, SVGTextMetrics&, unsigned valueListPosition) const;
+ void assignEmptyLayoutAttributesForCharacter(SVGTextLayoutAttributes&) const;
+
+private:
+ Vector<LayoutScope> m_scopes;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
new file mode 100644
index 0000000..7eefad6
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp
@@ -0,0 +1,579 @@
+/*
+ * 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)
+#include "SVGTextLayoutEngine.h"
+
+#include "RenderSVGInlineText.h"
+#include "RenderSVGTextPath.h"
+#include "SVGElement.h"
+#include "SVGInlineTextBox.h"
+#include "SVGTextLayoutEngineBaseline.h"
+#include "SVGTextLayoutEngineSpacing.h"
+
+// Set to a value > 0 to dump the text fragments
+#define DUMP_TEXT_FRAGMENTS 0
+
+namespace WebCore {
+
+SVGTextLayoutEngine::SVGTextLayoutEngine()
+ : m_x(0)
+ , m_y(0)
+ , m_dx(0)
+ , m_dy(0)
+ , m_isVerticalText(false)
+ , m_inPathLayout(false)
+ , m_textPathLength(0)
+ , m_textPathCurrentOffset(0)
+ , m_textPathSpacing(0)
+ , m_textPathScaling(1)
+{
+}
+
+void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y)
+{
+ if (m_inPathLayout)
+ return;
+
+ // Replace characters x/y position, with the current text position plus any
+ // relative adjustments, if it doesn't specify an absolute position itself.
+ if (x == SVGTextLayoutAttributes::emptyValue())
+ x = m_x + m_dx;
+
+ if (y == SVGTextLayoutAttributes::emptyValue())
+ y = m_y + m_dy;
+
+ m_dx = 0;
+ m_dy = 0;
+}
+
+void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyphAdvance)
+{
+ // Update current text position after processing the character.
+ if (m_isVerticalText) {
+ m_x = x;
+ m_y = y + glyphAdvance;
+ } else {
+ m_x = x + glyphAdvance;
+ m_y = y;
+ }
+}
+
+void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned positionListOffset)
+{
+ // Update relative positioning information.
+ if (dxValues.isEmpty() && dyValues.isEmpty())
+ return;
+
+ float dx = 0;
+ if (!dxValues.isEmpty()) {
+ float& dxCurrent = dxValues.at(positionListOffset);
+ if (dxCurrent != SVGTextLayoutAttributes::emptyValue())
+ dx = dxCurrent;
+ }
+
+ float dy = 0;
+ if (!dyValues.isEmpty()) {
+ float& dyCurrent = dyValues.at(positionListOffset);
+ if (dyCurrent != SVGTextLayoutAttributes::emptyValue())
+ dy = dyCurrent;
+ }
+
+ if (m_inPathLayout) {
+ if (m_isVerticalText) {
+ m_dx += dx;
+ m_dy = dy;
+ } else {
+ m_dx = dx;
+ m_dy += dy;
+ }
+
+ return;
+ }
+
+ m_dx = dx;
+ m_dy = dy;
+}
+
+void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, RenderSVGInlineText* text, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics)
+{
+ ASSERT(!m_currentTextFragment.length);
+
+ // Figure out length of fragment.
+ m_currentTextFragment.length = positionListOffset - m_currentTextFragment.positionListOffset;
+
+ // Figure out fragment metrics.
+ if (m_currentTextFragment.length == 1) {
+ // Fast path, can rely on already computed per-character metrics.
+ m_currentTextFragment.width = lastCharacterMetrics.width();
+ m_currentTextFragment.height = lastCharacterMetrics.height();
+ } else {
+ // Need to measure the whole range (range metrics != sum of character metrics)
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, m_currentTextFragment.positionListOffset, m_currentTextFragment.length);
+ m_currentTextFragment.width = metrics.width();
+ m_currentTextFragment.height = metrics.height();
+ }
+
+ textBox->textFragments().append(m_currentTextFragment);
+ m_currentTextFragment = SVGTextFragment();
+}
+
+bool SVGTextLayoutEngine::parentDefinesTextLength(RenderObject* parent) const
+{
+ RenderObject* currentParent = parent;
+ while (currentParent) {
+ SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(currentParent);
+ if (textContentElement) {
+ SVGTextContentElement::SVGLengthAdjustType lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust());
+ if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACING && textContentElement->textLength().value(textContentElement) > 0)
+ return true;
+ }
+
+ if (currentParent->isSVGText())
+ return false;
+
+ currentParent = currentParent->parent();
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+void SVGTextLayoutEngine::beginTextPathLayout(RenderObject* object, SVGTextLayoutEngine& lineLayout)
+{
+ ASSERT(object);
+
+ m_inPathLayout = true;
+ RenderSVGTextPath* textPath = toRenderSVGTextPath(object);
+
+ m_textPath = textPath->layoutPath();
+ m_textPathStartOffset = textPath->startOffset();
+ m_textPathLength = m_textPath.length();
+ if (m_textPathStartOffset > 0 && m_textPathStartOffset <= 1)
+ m_textPathStartOffset *= m_textPathLength;
+
+ float totalLength = 0;
+ unsigned totalCharacters = 0;
+
+ lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes);
+ const Vector<SVGTextChunk>& textChunks = lineLayout.m_chunkLayoutBuilder.textChunks();
+
+ unsigned size = textChunks.size();
+ for (unsigned i = 0; i < size; ++i) {
+ const SVGTextChunk& chunk = textChunks.at(i);
+
+ float length = 0;
+ unsigned characters = 0;
+ chunk.calculateLength(length, characters);
+
+ // Handle text-anchor as additional start offset for text paths.
+ m_textPathStartOffset += chunk.calculateTextAnchorShift(length);
+
+ totalLength += length;
+ totalCharacters += characters;
+ }
+
+ m_textPathCurrentOffset = m_textPathStartOffset;
+
+ // Eventually handle textLength adjustments.
+ SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN;
+ float desiredTextLength = 0;
+
+ if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textPath)) {
+ lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust());
+ desiredTextLength = textContentElement->textLength().value(textContentElement);
+ }
+
+ if (!desiredTextLength)
+ return;
+
+ if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACING)
+ m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters;
+ else
+ m_textPathScaling = desiredTextLength / totalLength;
+}
+
+void SVGTextLayoutEngine::endTextPathLayout()
+{
+ m_inPathLayout = false;
+ m_textPath = Path();
+ m_textPathLength = 0;
+ m_textPathStartOffset = 0;
+ m_textPathCurrentOffset = 0;
+ m_textPathSpacing = 0;
+ m_textPathScaling = 1;
+}
+
+void SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox)
+{
+ ASSERT(textBox);
+
+ RenderSVGInlineText* text = toRenderSVGInlineText(textBox->textRenderer());
+ ASSERT(text);
+ ASSERT(text->parent());
+ ASSERT(text->parent()->node());
+ ASSERT(text->parent()->node()->isSVGElement());
+
+ const RenderStyle* style = text->style();
+ ASSERT(style);
+
+ textBox->clearTextFragments();
+ m_isVerticalText = style->svgStyle()->isVerticalWritingMode();
+ layoutTextOnLineOrPath(textBox, text, style);
+
+ if (m_inPathLayout) {
+ m_pathLayoutBoxes.append(textBox);
+ return;
+ }
+
+ m_lineLayoutBoxes.append(textBox);
+}
+
+void SVGTextLayoutEngine::finishLayout()
+{
+ // After all text fragments are stored in their correpsonding SVGInlineTextBoxes, we can layout individual text chunks.
+ // Chunk layouting is only performed for line layout boxes, not for path layout, where it has already been done.
+ m_chunkLayoutBuilder.layoutTextChunks(m_lineLayoutBoxes);
+
+ // Finalize transform matrices, after the chunk layout corrections have been applied, and all fragment x/y positions are finalized.
+ if (!m_lineLayoutBoxes.isEmpty()) {
+#if DUMP_TEXT_FRAGMENTS > 0
+ fprintf(stderr, "Line layout: ");
+#endif
+
+ finalizeTransformMatrices(m_lineLayoutBoxes);
+ }
+
+ if (!m_pathLayoutBoxes.isEmpty()) {
+#if DUMP_TEXT_FRAGMENTS > 0
+ fprintf(stderr, "Path layout: ");
+#endif
+ finalizeTransformMatrices(m_pathLayoutBoxes);
+ }
+}
+
+void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& boxes)
+{
+ unsigned boxCount = boxes.size();
+
+#if DUMP_TEXT_FRAGMENTS > 0
+ fprintf(stderr, "Dumping all text fragments in text sub tree, %i boxes\n", boxCount);
+
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = boxes.at(boxPosition);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->textRenderer());
+ fprintf(stderr, " textBox properties, start=%i, len=%i\n", textBox->start(), textBox->len());
+ fprintf(stderr, " textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength());
+
+ const UChar* characters = textBox->textRenderer()->characters();
+
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+ String fragmentString(characters + fragment.positionListOffset, fragment.length);
+ fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, positionListOffset=%i, length=%i, characters='%s'\n"
+ , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.positionListOffset, fragment.length, fragmentString.utf8().data());
+ }
+ }
+#endif
+
+
+ if (!boxCount)
+ return;
+
+ AffineTransform textBoxTransformation;
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = boxes.at(boxPosition);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+ AffineTransform& transform = fragment.transform;
+ if (!transform.isIdentity()) {
+ transform.translateRight(fragment.x, fragment.y);
+ transform.translate(-fragment.x, -fragment.y);
+ }
+
+ m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);
+ if (textBoxTransformation.isIdentity())
+ continue;
+
+ if (transform.isIdentity())
+ transform = textBoxTransformation;
+ else
+ transform.multiply(textBoxTransformation);
+ }
+ }
+
+ boxes.clear();
+}
+
+void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style)
+{
+ SVGElement* lengthContext = static_cast<SVGElement*>(text->parent()->node());
+
+ RenderObject* textParent = text->parent();
+ bool definesTextLength = textParent ? parentDefinesTextLength(textParent) : false;
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ SVGTextLayoutAttributes& attributes = text->layoutAttributes();
+ Vector<float>& xValues = attributes.xValues();
+ Vector<float>& yValues = attributes.yValues();
+ Vector<float>& dxValues = attributes.dxValues();
+ Vector<float>& dyValues = attributes.dyValues();
+ Vector<float>& rotateValues = attributes.rotateValues();
+ Vector<SVGTextMetrics>& textMetricsValues = attributes.textMetricsValues();
+
+ unsigned boxStart = textBox->start();
+ unsigned boxLength = textBox->len();
+ unsigned textMetricsSize = textMetricsValues.size();
+ ASSERT(textMetricsSize <= xValues.size());
+ ASSERT(textMetricsSize <= yValues.size());
+ ASSERT(xValues.size() == yValues.size());
+
+ if (boxLength > textMetricsSize)
+ textMetricsSize = boxLength;
+
+ unsigned positionListOffset = 0;
+ unsigned metricsListOffset = 0;
+ const UChar* characters = text->characters();
+
+ const Font& font = style->font();
+ SVGTextLayoutEngineSpacing spacingLayout(font);
+ SVGTextLayoutEngineBaseline baselineLayout(font);
+
+ bool didStartTextFragment = false;
+ bool applySpacingToNextCharacter = false;
+
+ float lastAngle = 0;
+ float baselineShift = baselineLayout.calculateBaselineShift(svgStyle, lengthContext);
+ baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text);
+
+ // Main layout algorithm.
+ unsigned positionListSize = xValues.size();
+ for (; metricsListOffset < textMetricsSize && positionListOffset < positionListSize; ++metricsListOffset) {
+ SVGTextMetrics& metrics = textMetricsValues.at(metricsListOffset);
+ // Advance to text box start location.
+ if (positionListOffset < boxStart) {
+ positionListOffset += metrics.length();
+ continue;
+ }
+
+ // Stop if we've finished processing this text box.
+ if (positionListOffset >= boxStart + boxLength)
+ break;
+
+ float x = xValues.at(positionListOffset);
+ float y = yValues.at(positionListOffset);
+
+ // When we've advanced to the box start offset, determine using the original x/y values,
+ // wheter this character starts a new text chunk, before doing any further processing.
+ if (positionListOffset == boxStart)
+ textBox->setStartsNewTextChunk(text->characterStartsNewTextChunk(boxStart));
+
+ if (metrics == SVGTextMetrics::emptyMetrics()) {
+ positionListOffset += metrics.length();
+ continue;
+ }
+
+ const UChar* currentCharacter = characters + positionListOffset;
+ float angle = 0;
+ if (!rotateValues.isEmpty()) {
+ float newAngle = rotateValues.at(positionListOffset);
+ if (newAngle != SVGTextLayoutAttributes::emptyValue())
+ angle = newAngle;
+ }
+
+ // Calculate glyph orientation angle.
+ float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, svgStyle, *currentCharacter);
+
+ // Calculate glyph advance & x/y orientation shifts.
+ float xOrientationShift = 0;
+ float yOrientationShift = 0;
+ float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, metrics, orientationAngle, xOrientationShift, yOrientationShift);
+
+ // Assign current text position to x/y values, if needed.
+ updateCharacerPositionIfNeeded(x, y);
+
+ // Apply dx/dy value adjustments to current text position, if needed.
+ updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues, positionListOffset);
+
+ // Calculate SVG Fonts kerning, if needed.
+ float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, metrics.glyph());
+
+ // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed.
+ float spacing = spacingLayout.calculateCSSKerningAndSpacing(svgStyle, lengthContext, currentCharacter);
+
+ float textPathOffset = 0;
+ if (m_inPathLayout) {
+ float scaledGlyphAdvance = glyphAdvance * m_textPathScaling;
+ if (m_isVerticalText) {
+ // If there's an absolute y position available, it marks the beginning of a new position along the path.
+ if (y != SVGTextLayoutAttributes::emptyValue())
+ m_textPathCurrentOffset = y + m_textPathStartOffset;
+
+ m_textPathCurrentOffset += m_dy - kerning;
+ m_dy = 0;
+
+ // Apply dx/dy correction and setup translations that move to the glyph midpoint.
+ xOrientationShift += m_dx + baselineShift;
+ yOrientationShift -= scaledGlyphAdvance / 2;
+ } else {
+ // If there's an absolute x position available, it marks the beginning of a new position along the path.
+ if (x != SVGTextLayoutAttributes::emptyValue())
+ m_textPathCurrentOffset = x + m_textPathStartOffset;
+
+ m_textPathCurrentOffset += m_dx - kerning;
+ m_dx = 0;
+
+ // Apply dx/dy correction and setup translations that move to the glyph midpoint.
+ xOrientationShift -= scaledGlyphAdvance / 2;
+ yOrientationShift += m_dy - baselineShift;
+ }
+
+ // Calculate current offset along path.
+ textPathOffset = m_textPathCurrentOffset + scaledGlyphAdvance / 2;
+
+ // Move to next character.
+ m_textPathCurrentOffset += scaledGlyphAdvance + m_textPathSpacing + spacing * m_textPathScaling;
+
+ // Skip character, if we're before the path.
+ if (textPathOffset < 0) {
+ positionListOffset += metrics.length();
+ continue;
+ }
+
+ // Stop processing, if the next character lies behind the path.
+ if (textPathOffset > m_textPathLength)
+ break;
+
+ bool ok = false;
+ FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
+ ASSERT(ok);
+
+ x = point.x();
+ y = point.y();
+ angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
+ ASSERT(ok);
+
+ // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
+ if (m_isVerticalText)
+ angle -= 90;
+ } else {
+ // Apply all previously calculated shift values.
+ if (m_isVerticalText) {
+ x += baselineShift;
+ y -= kerning;
+ } else {
+ x -= kerning;
+ y -= baselineShift;
+ }
+
+ x += m_dx;
+ y += m_dy;
+ }
+
+ // Determine wheter we have to start a new fragment.
+ bool shouldStartNewFragment = false;
+
+ if (m_dx || m_dy)
+ shouldStartNewFragment = true;
+
+ if (!shouldStartNewFragment && (m_isVerticalText || m_inPathLayout))
+ shouldStartNewFragment = true;
+
+ if (!shouldStartNewFragment && (angle || angle != lastAngle || orientationAngle))
+ shouldStartNewFragment = true;
+
+ if (!shouldStartNewFragment && (kerning || applySpacingToNextCharacter || definesTextLength))
+ shouldStartNewFragment = true;
+
+ // If we already started a fragment, close it now.
+ if (didStartTextFragment && shouldStartNewFragment) {
+ applySpacingToNextCharacter = false;
+ recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1));
+ }
+
+ // Eventually start a new fragment, if not yet done.
+ if (!didStartTextFragment || shouldStartNewFragment) {
+ ASSERT(!m_currentTextFragment.positionListOffset);
+ ASSERT(!m_currentTextFragment.length);
+
+ didStartTextFragment = true;
+ m_currentTextFragment.positionListOffset = positionListOffset;
+ m_currentTextFragment.x = x;
+ m_currentTextFragment.y = y;
+
+ // Build fragment transformation.
+ if (angle)
+ m_currentTextFragment.transform.rotate(angle);
+
+ if (xOrientationShift || yOrientationShift)
+ m_currentTextFragment.transform.translate(xOrientationShift, yOrientationShift);
+
+ if (orientationAngle)
+ m_currentTextFragment.transform.rotate(orientationAngle);
+
+ if (m_inPathLayout && m_textPathScaling != 1) {
+ if (m_isVerticalText)
+ m_currentTextFragment.transform.scaleNonUniform(1, m_textPathScaling);
+ else
+ m_currentTextFragment.transform.scaleNonUniform(m_textPathScaling, 1);
+ }
+ }
+
+ // Update current text position, after processing of the current character finished.
+ if (m_inPathLayout)
+ updateCurrentTextPosition(x, y, glyphAdvance);
+ else {
+ // Apply CSS 'kerning', 'letter-spacing' and 'word-spacing' to next character, if needed.
+ if (spacing)
+ applySpacingToNextCharacter = true;
+
+ float xNew = x - m_dx;
+ float yNew = y - m_dy;
+
+ if (m_isVerticalText)
+ xNew -= baselineShift;
+ else
+ yNew += baselineShift;
+
+ updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing);
+ }
+
+ positionListOffset += metrics.length();
+ lastAngle = angle;
+ }
+
+ if (!didStartTextFragment)
+ return;
+
+ // Close last open fragment, if needed.
+ recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1));
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h
new file mode 100644
index 0000000..ad058d8
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextLayoutEngine_h
+#define SVGTextLayoutEngine_h
+
+#if ENABLE(SVG)
+#include "Path.h"
+#include "SVGTextChunkBuilder.h"
+#include "SVGTextFragment.h"
+#include "SVGTextMetrics.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class RenderObject;
+class RenderStyle;
+class RenderSVGInlineText;
+class SVGElement;
+class SVGInlineTextBox;
+class SVGRenderStyle;
+
+// SVGTextLayoutEngine performs the second layout phase for SVG text.
+//
+// The InlineBox tree was created, containing the text chunk information, necessary to apply
+// certain SVG specific text layout properties (text-length adjustments and text-anchor).
+// The second layout phase uses the SVGTextLayoutAttributes stored in the individual
+// RenderSVGInlineText renderers to compute the final positions for each character
+// which are stored in the SVGInlineTextBox objects.
+
+class SVGTextLayoutEngine : public Noncopyable {
+public:
+ SVGTextLayoutEngine();
+ SVGTextChunkBuilder& chunkLayoutBuilder() { return m_chunkLayoutBuilder; }
+
+ void beginTextPathLayout(RenderObject*, SVGTextLayoutEngine& lineLayout);
+ void endTextPathLayout();
+
+ void layoutInlineTextBox(SVGInlineTextBox*);
+ void finishLayout();
+
+private:
+ void updateCharacerPositionIfNeeded(float& x, float& y);
+ void updateCurrentTextPosition(float x, float y, float glyphAdvance);
+ void updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned valueListPosition);
+
+ void recordTextFragment(SVGInlineTextBox*, RenderSVGInlineText*, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics);
+ bool parentDefinesTextLength(RenderObject*) const;
+
+ void layoutTextOnLineOrPath(SVGInlineTextBox*, RenderSVGInlineText*, const RenderStyle*);
+ void finalizeTransformMatrices(Vector<SVGInlineTextBox*>&);
+
+private:
+ Vector<SVGInlineTextBox*> m_lineLayoutBoxes;
+ Vector<SVGInlineTextBox*> m_pathLayoutBoxes;
+ SVGTextChunkBuilder m_chunkLayoutBuilder;
+
+ SVGTextFragment m_currentTextFragment;
+ float m_x;
+ float m_y;
+ float m_dx;
+ float m_dy;
+ bool m_isVerticalText;
+ bool m_inPathLayout;
+
+ // Text on path layout
+ Path m_textPath;
+ float m_textPathLength;
+ float m_textPathStartOffset;
+ float m_textPathCurrentOffset;
+ float m_textPathSpacing;
+ float m_textPathScaling;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp
new file mode 100644
index 0000000..7060ac6
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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)
+#include "SVGTextLayoutEngineBaseline.h"
+
+#include "Font.h"
+#include "RenderObject.h"
+#include "SVGRenderStyle.h"
+#include "SVGTextMetrics.h"
+#include "UnicodeRange.h"
+
+namespace WebCore {
+
+SVGTextLayoutEngineBaseline::SVGTextLayoutEngineBaseline(const Font& font)
+ : m_font(font)
+{
+}
+
+float SVGTextLayoutEngineBaseline::calculateBaselineShift(const SVGRenderStyle* style, SVGElement* lengthContext) const
+{
+ if (style->baselineShift() == BS_LENGTH) {
+ SVGLength baselineShiftValueLength = style->baselineShiftValue();
+ if (baselineShiftValueLength.unitType() == LengthTypePercentage)
+ return baselineShiftValueLength.valueAsPercentage() * m_font.pixelSize();
+
+ return baselineShiftValueLength.value(lengthContext);
+ }
+
+ switch (style->baselineShift()) {
+ case BS_BASELINE:
+ return 0;
+ case BS_SUB:
+ return -m_font.height() / 2;
+ case BS_SUPER:
+ return m_font.height() / 2;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+EAlignmentBaseline SVGTextLayoutEngineBaseline::dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const
+{
+ ASSERT(textRenderer);
+ ASSERT(textRenderer->style());
+ ASSERT(textRenderer->parent());
+ ASSERT(textRenderer->parent()->style());
+
+ const SVGRenderStyle* style = textRenderer->style()->svgStyle();
+ ASSERT(style);
+
+ EDominantBaseline baseline = style->dominantBaseline();
+ if (baseline == DB_AUTO) {
+ if (isVerticalText)
+ baseline = DB_CENTRAL;
+ else
+ baseline = DB_ALPHABETIC;
+ }
+
+ switch (baseline) {
+ case DB_USE_SCRIPT:
+ // FIXME: The dominant-baseline and the baseline-table components are set by determining the predominant script of the character data content.
+ return AB_ALPHABETIC;
+ case DB_NO_CHANGE:
+ return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
+ case DB_RESET_SIZE:
+ return dominantBaselineToAlignmentBaseline(isVerticalText, textRenderer->parent());
+ case DB_IDEOGRAPHIC:
+ return AB_IDEOGRAPHIC;
+ case DB_ALPHABETIC:
+ return AB_ALPHABETIC;
+ case DB_HANGING:
+ return AB_HANGING;
+ case DB_MATHEMATICAL:
+ return AB_MATHEMATICAL;
+ case DB_CENTRAL:
+ return AB_CENTRAL;
+ case DB_MIDDLE:
+ return AB_MIDDLE;
+ case DB_TEXT_AFTER_EDGE:
+ return AB_TEXT_AFTER_EDGE;
+ case DB_TEXT_BEFORE_EDGE:
+ return AB_TEXT_BEFORE_EDGE;
+ default:
+ ASSERT_NOT_REACHED();
+ return AB_AUTO;
+ }
+}
+
+float SVGTextLayoutEngineBaseline::calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const
+{
+ ASSERT(textRenderer);
+ ASSERT(textRenderer->style());
+ ASSERT(textRenderer->style()->svgStyle());
+ ASSERT(textRenderer->parent());
+
+ const RenderObject* textRendererParent = textRenderer->parent();
+ ASSERT(textRendererParent);
+
+ EAlignmentBaseline baseline = textRenderer->style()->svgStyle()->alignmentBaseline();
+ if (baseline == AB_AUTO) {
+ baseline = dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
+ ASSERT(baseline != AB_AUTO);
+ }
+
+ // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling
+ switch (baseline) {
+ case AB_BASELINE:
+ return dominantBaselineToAlignmentBaseline(isVerticalText, textRendererParent);
+ case AB_BEFORE_EDGE:
+ case AB_TEXT_BEFORE_EDGE:
+ return m_font.ascent();
+ case AB_MIDDLE:
+ return m_font.xHeight() / 2;
+ case AB_CENTRAL:
+ return (m_font.ascent() - m_font.descent()) / 2;
+ case AB_AFTER_EDGE:
+ case AB_TEXT_AFTER_EDGE:
+ case AB_IDEOGRAPHIC:
+ return m_font.descent();
+ case AB_ALPHABETIC:
+ return 0;
+ case AB_HANGING:
+ return m_font.ascent() * 8 / 10.f;
+ case AB_MATHEMATICAL:
+ return m_font.ascent() / 2;
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+float SVGTextLayoutEngineBaseline::calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle* style, const UChar& character) const
+{
+ ASSERT(style);
+
+ switch (isVerticalText ? style->glyphOrientationVertical() : style->glyphOrientationHorizontal()) {
+ case GO_AUTO:
+ {
+ // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees.
+ // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees.
+ unsigned int unicodeRange = findCharUnicodeRange(character);
+ if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic)
+ return 90;
+
+ return 0;
+ }
+ case GO_90DEG:
+ return 90;
+ case GO_180DEG:
+ return 180;
+ case GO_270DEG:
+ return 270;
+ case GO_0DEG:
+ default:
+ return 0;
+ }
+}
+
+static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle)
+{
+ return !fabsf(fmodf(orientationAngle, 180));
+}
+
+float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const
+{
+ bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle);
+
+ // The function is based on spec requirements:
+ //
+ // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of
+ // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph.
+ //
+ // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of
+ // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph.
+
+ // Vertical orientation handling.
+ if (isVerticalText) {
+ float ascentMinusDescent = m_font.ascent() - m_font.descent();
+ if (!angle) {
+ xOrientationShift = (ascentMinusDescent - metrics.width()) / 2;
+ yOrientationShift = m_font.ascent();
+ } else if (angle == 180)
+ xOrientationShift = (ascentMinusDescent + metrics.width()) / 2;
+ else if (angle == 270) {
+ yOrientationShift = metrics.width();
+ xOrientationShift = ascentMinusDescent;
+ }
+
+ // Vertical advance calculation.
+ if (angle && !orientationIsMultiplyOf180Degrees)
+ return metrics.width();
+
+ return metrics.height();
+ }
+
+ // Horizontal orientation handling.
+ if (angle == 90)
+ yOrientationShift = -metrics.width();
+ else if (angle == 180) {
+ xOrientationShift = metrics.width();
+ yOrientationShift = -m_font.ascent();
+ } else if (angle == 270)
+ xOrientationShift = metrics.width();
+
+ // Horizontal advance calculation.
+ if (angle && !orientationIsMultiplyOf180Degrees)
+ return metrics.height();
+
+ return metrics.width();
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h b/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h
new file mode 100644
index 0000000..d753b39
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngineBaseline.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextLayoutEngineBaseline_h
+#define SVGTextLayoutEngineBaseline_h
+
+#if ENABLE(SVG)
+#include "SVGRenderStyleDefs.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+class Font;
+class RenderObject;
+class SVGElement;
+class SVGRenderStyle;
+class SVGTextMetrics;
+
+// Helper class used by SVGTextLayoutEngine to handle 'alignment-baseline' / 'dominant-baseline' and 'baseline-shift'.
+class SVGTextLayoutEngineBaseline : public Noncopyable {
+public:
+ SVGTextLayoutEngineBaseline(const Font&);
+
+ float calculateBaselineShift(const SVGRenderStyle*, SVGElement* lengthContext) const;
+ float calculateAlignmentBaselineShift(bool isVerticalText, const RenderObject* textRenderer) const;
+ float calculateGlyphOrientationAngle(bool isVerticalText, const SVGRenderStyle*, const UChar& character) const;
+ float calculateGlyphAdvanceAndOrientation(bool isVerticalText, SVGTextMetrics&, float angle, float& xOrientationShift, float& yOrientationShift) const;
+
+private:
+ EAlignmentBaseline dominantBaselineToAlignmentBaseline(bool isVerticalText, const RenderObject* textRenderer) const;
+
+ const Font& m_font;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp
new file mode 100644
index 0000000..6c54b67
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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)
+#include "SVGTextLayoutEngineSpacing.h"
+
+#include "Font.h"
+#include "SVGRenderStyle.h"
+
+#if ENABLE(SVG_FONTS)
+#include "SVGFontElement.h"
+#endif
+
+namespace WebCore {
+
+SVGTextLayoutEngineSpacing::SVGTextLayoutEngineSpacing(const Font& font)
+ : m_font(font)
+ , m_lastCharacter(0)
+{
+}
+
+float SVGTextLayoutEngineSpacing::calculateSVGKerning(bool isVerticalText, const SVGTextMetrics::Glyph& currentGlyph)
+{
+#if ENABLE(SVG_FONTS)
+ if (!m_font.isSVGFont()) {
+ m_lastGlyph.isValid = false;
+ return 0;
+ }
+
+ SVGFontElement* svgFont = m_font.svgFont();
+ ASSERT(svgFont);
+
+ float kerning = 0;
+ if (m_lastGlyph.isValid) {
+ if (isVerticalText)
+ kerning = svgFont->verticalKerningForPairOfStringsAndGlyphs(m_lastGlyph.unicodeString, m_lastGlyph.name, currentGlyph.unicodeString, currentGlyph.name);
+ else
+ kerning = svgFont->horizontalKerningForPairOfStringsAndGlyphs(m_lastGlyph.unicodeString, m_lastGlyph.name, currentGlyph.unicodeString, currentGlyph.name);
+ }
+
+ m_lastGlyph = currentGlyph;
+ m_lastGlyph.isValid = true;
+ kerning *= m_font.size() / m_font.primaryFont()->unitsPerEm();
+ return kerning;
+#else
+ UNUSED_PARAM(isVerticalText);
+ UNUSED_PARAM(currentGlyph);
+ return false;
+#endif
+}
+
+float SVGTextLayoutEngineSpacing::calculateCSSKerningAndSpacing(const SVGRenderStyle* style, SVGElement* lengthContext, const UChar* currentCharacter)
+{
+ float kerning = 0;
+ SVGLength kerningLength = style->kerning();
+ if (kerningLength.unitType() == LengthTypePercentage)
+ kerning = kerningLength.valueAsPercentage() * m_font.pixelSize();
+ else
+ kerning = kerningLength.value(lengthContext);
+
+ const UChar* lastCharacter = m_lastCharacter;
+ m_lastCharacter = currentCharacter;
+
+ if (!kerning && !m_font.letterSpacing() && !m_font.wordSpacing())
+ return 0;
+
+ float spacing = m_font.letterSpacing() + kerning;
+ if (currentCharacter && lastCharacter && m_font.wordSpacing()) {
+ if (Font::treatAsSpace(*currentCharacter) && !Font::treatAsSpace(*lastCharacter))
+ spacing += m_font.wordSpacing();
+ }
+
+ return spacing;
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h
new file mode 100644
index 0000000..0a6d736
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngineSpacing.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextLayoutEngineSpacing_h
+#define SVGTextLayoutEngineSpacing_h
+
+#if ENABLE(SVG)
+#include "SVGTextMetrics.h"
+
+namespace WebCore {
+
+class Font;
+class SVGRenderStyle;
+class SVGElement;
+
+// Helper class used by SVGTextLayoutEngine to handle 'kerning' / 'letter-spacing' and 'word-spacing'.
+class SVGTextLayoutEngineSpacing : public Noncopyable {
+public:
+ SVGTextLayoutEngineSpacing(const Font&);
+
+ float calculateSVGKerning(bool isVerticalText, const SVGTextMetrics::Glyph& currentGlyph);
+ float calculateCSSKerningAndSpacing(const SVGRenderStyle*, SVGElement* lengthContext, const UChar* currentCharacter);
+
+private:
+ const Font& m_font;
+ const UChar* m_lastCharacter;
+
+#if ENABLE(SVG_FONTS)
+ SVGTextMetrics::Glyph m_lastGlyph;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.cpp b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp
new file mode 100644
index 0000000..58d0ad9
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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)
+#include "SVGTextMetrics.h"
+
+#include "RenderSVGInlineText.h"
+
+namespace WebCore {
+
+SVGTextMetrics::SVGTextMetrics()
+ : m_width(0)
+ , m_height(0)
+ , m_length(0)
+{
+}
+
+SVGTextMetrics::SVGTextMetrics(const Font& font, const TextRun& run, unsigned position, unsigned textLength)
+ : m_width(0)
+ , m_height(0)
+ , m_length(0)
+{
+ int extraCharsAvailable = textLength - (position + run.length());
+ int length = 0;
+
+ m_width = font.floatWidth(run, extraCharsAvailable, length, m_glyph.name);
+ m_height = font.height();
+ m_glyph.unicodeString = String(run.characters(), length);
+ m_glyph.isValid = true;
+
+ ASSERT(length >= 0);
+ m_length = static_cast<unsigned>(length);
+}
+
+bool SVGTextMetrics::operator==(const SVGTextMetrics& other)
+{
+ return m_width == other.m_width
+ && m_height == other.m_height
+ && m_length == other.m_length
+ && m_glyph == other.m_glyph;
+}
+
+SVGTextMetrics SVGTextMetrics::emptyMetrics()
+{
+ DEFINE_STATIC_LOCAL(SVGTextMetrics, s_emptyMetrics, ());
+ s_emptyMetrics.m_length = 1;
+ return s_emptyMetrics;
+}
+
+static TextRun constructTextRun(RenderSVGInlineText* text, const UChar* characters, unsigned position, unsigned length)
+{
+ TextRun run(characters + position, length);
+
+#if ENABLE(SVG_FONTS)
+ ASSERT(text->parent());
+ run.setReferencingRenderObject(text->parent());
+#endif
+
+ // Disable any word/character rounding.
+ run.disableRoundingHacks();
+
+ // We handle letter & word spacing ourselves.
+ run.disableSpacing();
+ return run;
+}
+
+SVGTextMetrics SVGTextMetrics::measureCharacterRange(RenderSVGInlineText* text, unsigned position, unsigned length)
+{
+ ASSERT(text);
+ ASSERT(text->style());
+
+ TextRun run(constructTextRun(text, text->characters(), position, length));
+ return SVGTextMetrics(text->style()->font(), run, position, text->textLength());
+}
+
+void SVGTextMetrics::measureAllCharactersIndividually(RenderSVGInlineText* text, Vector<SVGTextMetrics>& allMetrics)
+{
+ ASSERT(text);
+ ASSERT(text->style());
+
+ const Font& font = text->style()->font();
+ const UChar* characters = text->characters();
+ unsigned length = text->textLength();
+
+ TextRun run(constructTextRun(text, 0, 0, 0));
+ for (unsigned position = 0; position < length; ) {
+ run.setText(characters + position, 1);
+
+ SVGTextMetrics metrics(font, run, position, text->textLength());
+ allMetrics.append(metrics);
+ position += metrics.length();
+ }
+}
+
+}
+
+#endif // ENABLE(SVG)
diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.h b/Source/WebCore/rendering/svg/SVGTextMetrics.h
new file mode 100644
index 0000000..ba18589
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextMetrics.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef SVGTextMetrics_h
+#define SVGTextMetrics_h
+
+#if ENABLE(SVG)
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class Font;
+class RenderSVGInlineText;
+class TextRun;
+
+class SVGTextMetrics {
+public:
+ static SVGTextMetrics emptyMetrics();
+ static SVGTextMetrics measureCharacterRange(RenderSVGInlineText*, unsigned position, unsigned length);
+ static void measureAllCharactersIndividually(RenderSVGInlineText*, Vector<SVGTextMetrics>&);
+
+ bool operator==(const SVGTextMetrics&);
+
+ float width() const { return m_width; }
+ float height() const { return m_height; }
+ unsigned length() const { return m_length; }
+
+ struct Glyph {
+ Glyph()
+ : isValid(false)
+ {
+ }
+
+ bool operator==(const Glyph& other)
+ {
+ return isValid == other.isValid
+ && name == other.name
+ && unicodeString == other.unicodeString;
+ }
+
+ bool isValid;
+ String name;
+ String unicodeString;
+ };
+
+ // Only useful when measuring individual characters, to lookup ligatures.
+ const Glyph& glyph() const { return m_glyph; }
+
+private:
+ SVGTextMetrics();
+ SVGTextMetrics(const Font&, const TextRun&, unsigned position, unsigned textLength);
+
+ float m_width;
+ float m_height;
+ unsigned m_length;
+ Glyph m_glyph;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.cpp b/Source/WebCore/rendering/svg/SVGTextQuery.cpp
new file mode 100644
index 0000000..fcc7924
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextQuery.cpp
@@ -0,0 +1,562 @@
+/*
+ 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 "SVGTextQuery.h"
+
+#if ENABLE(SVG)
+#include "FloatConversion.h"
+#include "InlineFlowBox.h"
+#include "RenderBlock.h"
+#include "RenderInline.h"
+#include "RenderSVGInlineText.h"
+#include "SVGInlineTextBox.h"
+#include "SVGTextMetrics.h"
+#include "VisiblePosition.h"
+
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+// Base structure for callback user data
+struct SVGTextQuery::Data {
+ Data()
+ : isVerticalText(false)
+ , processedCharacters(0)
+ , textRenderer(0)
+ , textBox(0)
+ {
+ }
+
+ bool isVerticalText;
+ unsigned processedCharacters;
+ RenderSVGInlineText* textRenderer;
+ const SVGInlineTextBox* textBox;
+};
+
+static inline InlineFlowBox* flowBoxForRenderer(RenderObject* renderer)
+{
+ if (!renderer)
+ return 0;
+
+ if (renderer->isRenderBlock()) {
+ // If we're given a block element, it has to be a RenderSVGText.
+ ASSERT(renderer->isSVGText());
+ RenderBlock* renderBlock = toRenderBlock(renderer);
+
+ // RenderSVGText only ever contains a single line box.
+ InlineFlowBox* flowBox = renderBlock->firstLineBox();
+ ASSERT(flowBox == renderBlock->lastLineBox());
+ return flowBox;
+ }
+
+ if (renderer->isRenderInline()) {
+ // We're given a RenderSVGInline or objects that derive from it (RenderSVGTSpan / RenderSVGTextPath)
+ RenderInline* renderInline = toRenderInline(renderer);
+
+ // RenderSVGInline only ever contains a single line box.
+ InlineFlowBox* flowBox = renderInline->firstLineBox();
+ ASSERT(flowBox == renderInline->lastLineBox());
+ return flowBox;
+ }
+
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+static inline float mapLengthThroughFragmentTransformation(const SVGTextFragment& fragment, bool isVerticalText, float length)
+{
+ if (fragment.transform.isIdentity())
+ return length;
+
+ if (isVerticalText)
+ return narrowPrecisionToFloat(static_cast<double>(length) * fragment.transform.yScale());
+
+ return narrowPrecisionToFloat(static_cast<double>(length) * fragment.transform.xScale());
+}
+
+SVGTextQuery::SVGTextQuery(RenderObject* renderer)
+{
+ collectTextBoxesInFlowBox(flowBoxForRenderer(renderer));
+}
+
+void SVGTextQuery::collectTextBoxesInFlowBox(InlineFlowBox* flowBox)
+{
+ if (!flowBox)
+ return;
+
+ for (InlineBox* child = flowBox->firstChild(); child; child = child->nextOnLine()) {
+ if (child->isInlineFlowBox()) {
+ // Skip generated content.
+ if (!child->renderer()->node())
+ continue;
+
+ collectTextBoxesInFlowBox(static_cast<InlineFlowBox*>(child));
+ continue;
+ }
+
+ ASSERT(child->isSVGInlineTextBox());
+ m_textBoxes.append(static_cast<SVGInlineTextBox*>(child));
+ }
+}
+
+bool SVGTextQuery::executeQuery(Data* queryData, ProcessTextFragmentCallback fragmentCallback) const
+{
+ ASSERT(!m_textBoxes.isEmpty());
+
+ unsigned processedCharacters = 0;
+ unsigned textBoxCount = m_textBoxes.size();
+
+ // Loop over all text boxes
+ for (unsigned textBoxPosition = 0; textBoxPosition < textBoxCount; ++textBoxPosition) {
+ queryData->textBox = m_textBoxes.at(textBoxPosition);
+ queryData->textRenderer = toRenderSVGInlineText(queryData->textBox->textRenderer());
+ ASSERT(queryData->textRenderer);
+ ASSERT(queryData->textRenderer->style());
+ ASSERT(queryData->textRenderer->style()->svgStyle());
+
+ queryData->isVerticalText = queryData->textRenderer->style()->svgStyle()->isVerticalWritingMode();
+ const Vector<SVGTextFragment>& fragments = queryData->textBox->textFragments();
+
+ // Loop over all text fragments in this text box, firing a callback for each.
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ const SVGTextFragment& fragment = fragments.at(i);
+ if ((this->*fragmentCallback)(queryData, fragment))
+ return true;
+
+ processedCharacters += fragment.length;
+ }
+
+ queryData->processedCharacters = processedCharacters;
+ }
+
+ return false;
+}
+
+bool SVGTextQuery::mapStartEndPositionsIntoFragmentCoordinates(Data* queryData, const SVGTextFragment& fragment, int& startPosition, int& endPosition) const
+{
+ // Reuse the same logic used for text selection & painting, to map our query start/length into start/endPositions of the current text fragment.
+ startPosition -= queryData->processedCharacters;
+ endPosition -= queryData->processedCharacters;
+
+ if (startPosition >= endPosition || startPosition < 0 || endPosition < 0)
+ return false;
+
+ modifyStartEndPositionsRespectingLigatures(queryData, startPosition, endPosition);
+ if (!queryData->textBox->mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition))
+ return false;
+
+ ASSERT(startPosition < endPosition);
+ return true;
+}
+
+void SVGTextQuery::modifyStartEndPositionsRespectingLigatures(Data* queryData, int& startPosition, int& endPosition) const
+{
+ const SVGTextLayoutAttributes& layoutAttributes = queryData->textRenderer->layoutAttributes();
+ const Vector<float>& xValues = layoutAttributes.xValues();
+ const Vector<SVGTextMetrics>& textMetricsValues = layoutAttributes.textMetricsValues();
+
+ unsigned boxStart = queryData->textBox->start();
+ unsigned boxLength = queryData->textBox->len();
+
+ unsigned textMetricsOffset = 0;
+ unsigned textMetricsSize = textMetricsValues.size();
+
+ unsigned positionOffset = 0;
+ unsigned positionSize = xValues.size();
+
+ bool alterStartPosition = true;
+ bool alterEndPosition = true;
+
+ int lastPositionOffset = -1;
+ for (; textMetricsOffset < textMetricsSize && positionOffset < positionSize; ++textMetricsOffset) {
+ const SVGTextMetrics& metrics = textMetricsValues.at(textMetricsOffset);
+
+ // Advance to text box start location.
+ if (positionOffset < boxStart) {
+ positionOffset += metrics.length();
+ continue;
+ }
+
+ // Stop if we've finished processing this text box.
+ if (positionOffset >= boxStart + boxLength)
+ break;
+
+ // If the start position maps to a character in the metrics list, we don't need to modify it.
+ if (startPosition == static_cast<int>(positionOffset))
+ alterStartPosition = false;
+
+ // If the start position maps to a character in the metrics list, we don't need to modify it.
+ if (endPosition == static_cast<int>(positionOffset))
+ alterEndPosition = false;
+
+ // Detect ligatures.
+ if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) {
+ if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) {
+ startPosition = lastPositionOffset;
+ alterStartPosition = false;
+ }
+
+ if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) {
+ endPosition = positionOffset;
+ alterEndPosition = false;
+ }
+ }
+
+ if (!alterStartPosition && !alterEndPosition)
+ break;
+
+ lastPositionOffset = positionOffset;
+ positionOffset += metrics.length();
+ }
+
+ if (!alterStartPosition && !alterEndPosition)
+ return;
+
+ if (lastPositionOffset != -1 && lastPositionOffset - positionOffset > 1) {
+ if (alterStartPosition && startPosition > lastPositionOffset && startPosition < static_cast<int>(positionOffset)) {
+ startPosition = lastPositionOffset;
+ alterStartPosition = false;
+ }
+
+ if (alterEndPosition && endPosition > lastPositionOffset && endPosition < static_cast<int>(positionOffset)) {
+ endPosition = positionOffset;
+ alterEndPosition = false;
+ }
+ }
+}
+
+// numberOfCharacters() implementation
+bool SVGTextQuery::numberOfCharactersCallback(Data*, const SVGTextFragment&) const
+{
+ // no-op
+ return false;
+}
+
+unsigned SVGTextQuery::numberOfCharacters() const
+{
+ if (m_textBoxes.isEmpty())
+ return 0;
+
+ Data data;
+ executeQuery(&data, &SVGTextQuery::numberOfCharactersCallback);
+ return data.processedCharacters;
+}
+
+// textLength() implementation
+struct TextLengthData : SVGTextQuery::Data {
+ TextLengthData()
+ : textLength(0)
+ {
+ }
+
+ float textLength;
+};
+
+bool SVGTextQuery::textLengthCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ TextLengthData* data = static_cast<TextLengthData*>(queryData);
+
+ float fragmentLength = queryData->isVerticalText ? fragment.height : fragment.width;
+ data->textLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength);
+ return false;
+}
+
+float SVGTextQuery::textLength() const
+{
+ if (m_textBoxes.isEmpty())
+ return 0;
+
+ TextLengthData data;
+ executeQuery(&data, &SVGTextQuery::textLengthCallback);
+ return data.textLength;
+}
+
+// subStringLength() implementation
+struct SubStringLengthData : SVGTextQuery::Data {
+ SubStringLengthData(unsigned queryStartPosition, unsigned queryLength)
+ : startPosition(queryStartPosition)
+ , length(queryLength)
+ , subStringLength(0)
+ {
+ }
+
+ unsigned startPosition;
+ unsigned length;
+
+ float subStringLength;
+};
+
+bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData);
+
+ int startPosition = data->startPosition;
+ int endPosition = startPosition + data->length;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ return false;
+
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, endPosition - startPosition);
+ float fragmentLength = queryData->isVerticalText ? metrics.height() : metrics.width();
+
+ data->subStringLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength);
+ return false;
+}
+
+float SVGTextQuery::subStringLength(unsigned startPosition, unsigned length) const
+{
+ if (m_textBoxes.isEmpty())
+ return 0;
+
+ SubStringLengthData data(startPosition, length);
+ executeQuery(&data, &SVGTextQuery::subStringLengthCallback);
+ return data.subStringLength;
+}
+
+// startPositionOfCharacter() implementation
+struct StartPositionOfCharacterData : SVGTextQuery::Data {
+ StartPositionOfCharacterData(unsigned queryPosition)
+ : position(queryPosition)
+ {
+ }
+
+ unsigned position;
+ FloatPoint startPosition;
+};
+
+bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ StartPositionOfCharacterData* data = static_cast<StartPositionOfCharacterData*>(queryData);
+
+ int startPosition = data->position;
+ int endPosition = startPosition + 1;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ return false;
+
+ data->startPosition = FloatPoint(fragment.x, fragment.y);
+
+ if (startPosition) {
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition);
+ if (queryData->isVerticalText)
+ data->startPosition.move(0, metrics.height());
+ else
+ data->startPosition.move(metrics.width(), 0);
+ }
+
+ if (fragment.transform.isIdentity())
+ return true;
+
+ data->startPosition = fragment.transform.mapPoint(data->startPosition);
+ return true;
+}
+
+FloatPoint SVGTextQuery::startPositionOfCharacter(unsigned position) const
+{
+ if (m_textBoxes.isEmpty())
+ return FloatPoint();
+
+ StartPositionOfCharacterData data(position);
+ executeQuery(&data, &SVGTextQuery::startPositionOfCharacterCallback);
+ return data.startPosition;
+}
+
+// endPositionOfCharacter() implementation
+struct EndPositionOfCharacterData : SVGTextQuery::Data {
+ EndPositionOfCharacterData(unsigned queryPosition)
+ : position(queryPosition)
+ {
+ }
+
+ unsigned position;
+ FloatPoint endPosition;
+};
+
+bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData);
+
+ int startPosition = data->position;
+ int endPosition = startPosition + 1;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ return false;
+
+ data->endPosition = FloatPoint(fragment.x, fragment.y);
+
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition + 1);
+ if (queryData->isVerticalText)
+ data->endPosition.move(0, metrics.height());
+ else
+ data->endPosition.move(metrics.width(), 0);
+
+ if (fragment.transform.isIdentity())
+ return true;
+
+ data->endPosition = fragment.transform.mapPoint(data->endPosition);
+ return true;
+}
+
+FloatPoint SVGTextQuery::endPositionOfCharacter(unsigned position) const
+{
+ if (m_textBoxes.isEmpty())
+ return FloatPoint();
+
+ EndPositionOfCharacterData data(position);
+ executeQuery(&data, &SVGTextQuery::endPositionOfCharacterCallback);
+ return data.endPosition;
+}
+
+// rotationOfCharacter() implementation
+struct RotationOfCharacterData : SVGTextQuery::Data {
+ RotationOfCharacterData(unsigned queryPosition)
+ : position(queryPosition)
+ , rotation(0)
+ {
+ }
+
+ unsigned position;
+ float rotation;
+};
+
+bool SVGTextQuery::rotationOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ RotationOfCharacterData* data = static_cast<RotationOfCharacterData*>(queryData);
+
+ int startPosition = data->position;
+ int endPosition = startPosition + 1;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ return false;
+
+ AffineTransform newTransform(fragment.transform);
+ newTransform.scale(1 / fragment.transform.xScale(), 1 / fragment.transform.yScale());
+ data->rotation = narrowPrecisionToFloat(rad2deg(atan2(newTransform.b(), newTransform.a())));
+ return true;
+}
+
+float SVGTextQuery::rotationOfCharacter(unsigned position) const
+{
+ if (m_textBoxes.isEmpty())
+ return 0;
+
+ RotationOfCharacterData data(position);
+ executeQuery(&data, &SVGTextQuery::rotationOfCharacterCallback);
+ return data.rotation;
+}
+
+// extentOfCharacter() implementation
+struct ExtentOfCharacterData : SVGTextQuery::Data {
+ ExtentOfCharacterData(unsigned queryPosition)
+ : position(queryPosition)
+ {
+ }
+
+ unsigned position;
+ FloatRect extent;
+};
+
+static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent)
+{
+ extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->style()->font().ascent()));
+
+ if (startPosition) {
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition);
+ if (queryData->isVerticalText)
+ extent.move(0, metrics.height());
+ else
+ extent.move(metrics.width(), 0);
+ }
+
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, 1);
+ extent.setSize(FloatSize(metrics.width(), metrics.height()));
+
+ if (fragment.transform.isIdentity())
+ return;
+
+ extent = fragment.transform.mapRect(extent);
+}
+
+bool SVGTextQuery::extentOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ ExtentOfCharacterData* data = static_cast<ExtentOfCharacterData*>(queryData);
+
+ int startPosition = data->position;
+ int endPosition = startPosition + 1;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ return false;
+
+ calculateGlyphBoundaries(queryData, fragment, startPosition, data->extent);
+ return true;
+}
+
+FloatRect SVGTextQuery::extentOfCharacter(unsigned position) const
+{
+ if (m_textBoxes.isEmpty())
+ return FloatRect();
+
+ ExtentOfCharacterData data(position);
+ executeQuery(&data, &SVGTextQuery::extentOfCharacterCallback);
+ return data.extent;
+}
+
+// characterNumberAtPosition() implementation
+struct CharacterNumberAtPositionData : SVGTextQuery::Data {
+ CharacterNumberAtPositionData(const FloatPoint& queryPosition)
+ : position(queryPosition)
+ {
+ }
+
+ FloatPoint position;
+};
+
+bool SVGTextQuery::characterNumberAtPositionCallback(Data* queryData, const SVGTextFragment& fragment) const
+{
+ CharacterNumberAtPositionData* data = static_cast<CharacterNumberAtPositionData*>(queryData);
+
+ FloatRect extent;
+ for (unsigned i = 0; i < fragment.length; ++i) {
+ int startPosition = data->processedCharacters + i;
+ int endPosition = startPosition + 1;
+ if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition))
+ continue;
+
+ calculateGlyphBoundaries(queryData, fragment, startPosition, extent);
+ if (extent.contains(data->position)) {
+ data->processedCharacters += i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int SVGTextQuery::characterNumberAtPosition(const FloatPoint& position) const
+{
+ if (m_textBoxes.isEmpty())
+ return -1;
+
+ CharacterNumberAtPositionData data(position);
+ if (!executeQuery(&data, &SVGTextQuery::characterNumberAtPositionCallback))
+ return -1;
+
+ return data.processedCharacters;
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.h b/Source/WebCore/rendering/svg/SVGTextQuery.h
new file mode 100644
index 0000000..9a671f4
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextQuery.h
@@ -0,0 +1,75 @@
+/*
+ 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.
+*/
+
+#ifndef SVGTextQuery_h
+#define SVGTextQuery_h
+
+#if ENABLE(SVG)
+#include "FloatRect.h"
+#include "SVGTextFragment.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class InlineFlowBox;
+class RenderObject;
+class SVGInlineTextBox;
+
+class SVGTextQuery {
+public:
+ SVGTextQuery(RenderObject*);
+
+ unsigned numberOfCharacters() const;
+ float textLength() const;
+ float subStringLength(unsigned startPosition, unsigned length) const;
+ FloatPoint startPositionOfCharacter(unsigned position) const;
+ FloatPoint endPositionOfCharacter(unsigned position) const;
+ float rotationOfCharacter(unsigned position) const;
+ FloatRect extentOfCharacter(unsigned position) const;
+ int characterNumberAtPosition(const FloatPoint&) const;
+
+ // Public helper struct. Private classes in SVGTextQuery inherit from it.
+ struct Data;
+
+private:
+ typedef bool (SVGTextQuery::*ProcessTextFragmentCallback)(Data*, const SVGTextFragment&) const;
+ bool executeQuery(Data*, ProcessTextFragmentCallback) const;
+
+ void collectTextBoxesInFlowBox(InlineFlowBox*);
+ bool mapStartEndPositionsIntoFragmentCoordinates(Data*, const SVGTextFragment&, int& startPosition, int& endPosition) const;
+ void modifyStartEndPositionsRespectingLigatures(Data*, int& startPosition, int& endPosition) const;
+
+private:
+ bool numberOfCharactersCallback(Data*, const SVGTextFragment&) const;
+ bool textLengthCallback(Data*, const SVGTextFragment&) const;
+ bool subStringLengthCallback(Data*, const SVGTextFragment&) const;
+ bool startPositionOfCharacterCallback(Data*, const SVGTextFragment&) const;
+ bool endPositionOfCharacterCallback(Data*, const SVGTextFragment&) const;
+ bool rotationOfCharacterCallback(Data*, const SVGTextFragment&) const;
+ bool extentOfCharacterCallback(Data*, const SVGTextFragment&) const;
+ bool characterNumberAtPositionCallback(Data*, const SVGTextFragment&) const;
+
+private:
+ Vector<SVGInlineTextBox*> m_textBoxes;
+};
+
+}
+
+#endif
+#endif