Bug 60254

Summary: Remove platform layering violation: TextRun stores RenderObjects for SVG Fonts support
Product: WebKit Reporter: Nikolas Zimmermann <zimmermann>
Component: PlatformAssignee: Nikolas Zimmermann <zimmermann>
Status: RESOLVED FIXED    
Severity: Normal CC: adamk, darin, dglazkov, eric, hyatt, koivisto, krit, leviw, mitz, webkit.review.bot
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 60255    
Bug Blocks:    
Attachments:
Description Flags
Patch v1
eric: review-
Patch v2
webkit.review.bot: commit-queue-
Patch v3
none
Patch v4 koivisto: review+

Description Nikolas Zimmermann 2011-05-05 00:29:36 PDT
Remove platform layering violation: TextRun stores RenderObjects for SVG Fonts support
Comment 1 Nikolas Zimmermann 2011-05-18 02:24:10 PDT
Created attachment 93888 [details]
Patch v1

An important piece of work toward my SVG Fonts rewrite patch (see master bug 59085).
Comment 2 Eric Seidel (no email) 2011-05-18 02:40:02 PDT
Comment on attachment 93888 [details]
Patch v1

View in context: https://bugs.webkit.org/attachment.cgi?id=93888&action=review

It seems that you really want TextRuns to be pointers so you can use virtual function dispatch.  Since they aren't, you're adding this "extra data" pointer.  I think "extra data" is not a very descriptive name for what you're doing.  Maybe this is some sort of observer for the text run?  It doesn't seem to be extra storage.

> Source/WebCore/rendering/svg/SVGInlineTextBox.cpp:400
> +    if (RenderTextRun::ExtraData* extraData = textRun.extraData())
> +        extraData->setActivePaintingResource(0);

Why wouldn't this just be a setActivePaintingResource method on RenderTextRun?  Why do callers need to know of the existence of ExtraData?  The RenderTextRun object can make the call through the pointer internally.
Comment 3 Eric Seidel (no email) 2011-05-18 02:42:16 PDT
Comment on attachment 93888 [details]
Patch v1

View in context: https://bugs.webkit.org/attachment.cgi?id=93888&action=review

> Source/WebCore/platform/graphics/TextRun.h:107
> +        virtual bool isRenderTextRun() const { return false; }

This feels very strange.  You shouldn't be asking this "extra data" member what type the thing holding onto it is.  This is all one big hack around not having virtual dispatch for these stack objects it seems.

Does the TextRun itself need to have these members?  Or can there be a SVGTextRun which has a TextRun instead of is-a TextRun?
Comment 4 Eric Seidel (no email) 2011-05-18 02:44:03 PDT
Comment on attachment 93888 [details]
Patch v1

It seems all of the places which currently use TextRuns and grab at these SVG-specific members, don't really need to use TextRuns.  They could use some other class which has-a TextRun, instead of is-a TextRun.
Comment 5 Nikolas Zimmermann 2011-05-18 03:30:26 PDT
(In reply to comment #4)
> (From update of attachment 93888 [details])
> It seems all of the places which currently use TextRuns and grab at these SVG-specific members, don't really need to use TextRuns.  They could use some other class which has-a TextRun, instead of is-a TextRun.

There is just one place: SVGFonts.cpp, and it doesn't have access to anything but TextRuns.
After my SVGFonts rewrite, the SVG Fonts code is completly integrated within FontFastPath, where we only deal with TextRuns.

The ExtraData "hack" is exactly because of the fact, that I didn't want to make TextRuns refcounted, and add virtual functions. The idea is to optimize for non-SVGFonts usage patterns, so I am afraid of making text runs refcounted and add virtual functions just for the sake of SVG Fonts, hence the whole ExtraData concept.

I'd like to hear more opinions.
Comment 6 Nikolas Zimmermann 2011-05-18 03:32:09 PDT
(In reply to comment #3)
> (From update of attachment 93888 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=93888&action=review
> 
> > Source/WebCore/platform/graphics/TextRun.h:107
> > +        virtual bool isRenderTextRun() const { return false; }
> 
> This feels very strange.  You shouldn't be asking this "extra data" member what type the thing holding onto it is.  This is all one big hack around not having virtual dispatch for these stack objects it seems.
> 
> Does the TextRun itself need to have these members?  Or can there be a SVGTextRun which has a TextRun instead of is-a TextRun?

To rephrase: I could add a new class FooTextRun, that holds a RenderObject* and a TextRun object, as you said. The problem is that code in platform/ can't use FooTextRun directly, as it depends on RenderObject, and we'd have the same layering violation again.

I'm not saying your approach isn't good, but it induces layering problems, that's why I decided to go the ExtraData way, minimizing the cost for platform fonts...
Comment 7 Antti Koivisto 2011-05-19 01:58:58 PDT
I think the approach is good.

ExtraData might be better called RenderingContext or similar.

I don't think needs to be refcounted, OwnPtr should do just fine.

To really eliminate the layering violation the SVG calls in platform text drawing code should be replaced with virtual calls to RenderingContext:

drawTextUsingSVGFont() -> run.renderingContext()->drawText() and similar.
Comment 8 Antti Koivisto 2011-05-19 02:00:16 PDT
I'd like to see the full patch that eliminates the layering violation fully to validate we are going to right direction. From my reading it shouldn't be that complex.
Comment 9 Nikolas Zimmermann 2011-05-19 02:07:07 PDT
(In reply to comment #7)
> I think the approach is good.
> 
> ExtraData might be better called RenderingContext or similar.
Ok, I tried to make it generic, but why not.

> 
> I don't think needs to be refcounted, OwnPtr should do just fine.
Nope, TextRuns can be copied, and OwnPtr's operator= is private and not defined, so we have to use refcounting, if we want to avoid allocating new ExtraData objects upon assigning TextRuns.

> 
> To really eliminate the layering violation the SVG calls in platform text drawing code should be replaced with virtual calls to RenderingContext:
> 
> drawTextUsingSVGFont() -> run.renderingContext()->drawText() and similar.
As discussed on IRC, this will be part of the SVG Fonts rewrite, I don't want to invest time in the current SVGFont.cpp code to kill the violation completely.
Comment 10 Antti Koivisto 2011-05-19 02:26:31 PDT
(In reply to comment #9)

Actually I don't think you need RenderTextRun at all. It has almost no functionality. constructTextRun() can choose wether to construct ExtraData/SVGRenderingContext or not. The patch would get much smaller and easier to review.

> As discussed on IRC, this will be part of the SVG Fonts rewrite, I don't want to invest time in the current SVGFont.cpp code to kill the violation completely.

The bug title indicates that this is about eliminating the violations. I don't see the point otherwise.
Comment 11 Eric Seidel (no email) 2011-05-19 05:26:08 PDT
(In reply to comment #9)
> (In reply to comment #7)
> > I think the approach is good.
> > 
> > ExtraData might be better called RenderingContext or similar.
> Ok, I tried to make it generic, but why not.
> 
> > 
> > I don't think needs to be refcounted, OwnPtr should do just fine.
> Nope, TextRuns can be copied, and OwnPtr's operator= is private and not defined, so we have to use refcounting, if we want to avoid allocating new ExtraData objects upon assigning TextRuns.

I take it TextRuns are copied?  Should they be?
Comment 12 Nikolas Zimmermann 2011-05-20 04:32:31 PDT
(In reply to comment #10)
> (In reply to comment #9)
> 
> Actually I don't think you need RenderTextRun at all. It has almost no functionality. constructTextRun() can choose wether to construct ExtraData/SVGRenderingContext or not. The patch would get much smaller and easier to review.
I tested this idea, and it works like a charme. I called it AbstractRenderingContext, a sub-class of TextRun, and a TextRunRenderingContext class in rendering, inheriting from TextRun::AbstractRenderingContext.
I sucessfully moved the drawTextUsingSVGFont() etc. functions in there, and implemented them in rendering/svg/SVGTextRunRenderingContext.cpp (moved from svg/SVGFont.cpp).

So Font now uses the AbstractRenderingContext, if present, to delegate drawing/measuring there. It doesn't rely anymore on Font::drawTextUsingSVGFOnt which was implemented in svg/SVGFont.cpp, which is a clear violation.

We still have a violation in SimpleFontData, which stores a SVGFontData object from svg/SVGFontData.cpp.
I could solve it exactly the same way as above.


> 
> > As discussed on IRC, this will be part of the SVG Fonts rewrite, I don't want to invest time in the current SVGFont.cpp code to kill the violation completely.
> 
> The bug title indicates that this is about eliminating the violations. I don't see the point otherwise.
You're right, I've patched the existing SVG Fonts code, so you can see the full scope of the patch.

Preparing a new patch soon..
Comment 13 Nikolas Zimmermann 2011-05-20 07:29:08 PDT
Created attachment 94218 [details]
Patch v2
Comment 14 WebKit Review Bot 2011-05-20 07:32:49 PDT
Attachment 94218 [details] did not pass style-queue:

Failed to run "['Tools/Scripts/check-webkit-style', '--diff-files', u'Source/WebCore/CMakeLists.txt', u'Source/W..." exit_code: 1

Source/WebCore/svg/SVGFontData.cpp:26:  Alphabetical sorting problem.  [build/include_order] [4]
Total errors found: 1 in 36 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 15 WebKit Review Bot 2011-05-20 07:53:33 PDT
Comment on attachment 94218 [details]
Patch v2

Attachment 94218 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://queues.webkit.org/results/8721306
Comment 16 Early Warning System Bot 2011-05-20 07:54:29 PDT
Comment on attachment 94218 [details]
Patch v2

Attachment 94218 [details] did not pass qt-ews (qt):
Output: http://queues.webkit.org/results/8720320
Comment 17 Nikolas Zimmermann 2011-05-20 08:18:58 PDT
Created attachment 94223 [details]
Patch v3
Comment 18 WebKit Review Bot 2011-05-20 09:24:58 PDT
Comment on attachment 94218 [details]
Patch v2

Attachment 94218 [details] did not pass cr-mac-ews (chromium):
Output: http://queues.webkit.org/results/8721346
Comment 19 Antti Koivisto 2011-05-24 01:59:14 PDT
Comment on attachment 94223 [details]
Patch v3

View in context: https://bugs.webkit.org/attachment.cgi?id=94223&action=review

Looks good basically, I have some naming nits.

> Source/WebCore/GNUmakefile.list.am:3086
> +	Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp \
>  	Source/WebCore/rendering/svg/SVGTextMetrics.cpp \
>  	Source/WebCore/rendering/svg/SVGTextMetrics.h \
>  	Source/WebCore/rendering/svg/SVGTextQuery.cpp \
>  	Source/WebCore/rendering/svg/SVGTextQuery.h \
>  	Source/WebCore/rendering/TableLayout.h \
> +	Source/WebCore/rendering/TextRunRenderingContext.h \

Why TextRunRenderingContext.h vs. SVGTextRunRenderingContext.cpp?

> Source/WebCore/platform/graphics/TextRun.h:111
> +    class AbstractRenderingContext : public RefCounted<AbstractRenderingContext> {

I would just leave out the word "Abstract" here.

> Source/WebCore/platform/graphics/TextRun.h:114
> +    public:
> +        virtual ~AbstractRenderingContext() { }
> +        virtual bool isWebCoreRenderingContext() const = 0;

All likely clients are in WebCore (as is this class) so name WebCoreRenderingContext doesn't really differentiate. Maybe just remove the whole function for now, you only have one concrete implementation so casting is always safe. 

To do this in really non-layer violating way for multiple implementations, you would need to have "int type" field, with enum values defined outside platform.

> Source/WebCore/rendering/RenderCombineText.cpp:96
> -    TextRun run = TextRun(String(text()));
> +    TextRun run = RenderBlock::constructTextRun(this, originalFont(), String(text()), style());

I know it is now in this patch but having constructTextRun in RenderBlock seems random. Why isn't it a free standing function?

> Source/WebCore/rendering/TextRunRenderingContext.h:32
> +class TextRunRenderingContext : public TextRun::AbstractRenderingContext {
> +public:

I think this should be called SVGTextRunRenderingContext since it has a very specific purpose.

> Source/WebCore/rendering/svg/SVGInlineTextBox.cpp:389
> +    TextRun::AbstractRenderingContext* abstractRenderingContext = textRun.renderingContext();
> +    if (abstractRenderingContext && abstractRenderingContext->isWebCoreRenderingContext())
> +        static_cast<TextRunRenderingContext*>(abstractRenderingContext)->setActivePaintingResource(m_paintingResource);

As mentioned above isWebCoreRenderingContext() naming is weird and also unnecessary here.

> Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp:82
> +template<typename SVGTextRunData>
> +struct SVGTextRunWalker {
> +    typedef bool (*SVGTextRunWalkerCallback)(const SVGGlyph&, SVGTextRunData&);
> +    typedef void (*SVGTextRunWalkerMissingGlyphCallback)(const TextRun&, SVGTextRunData&);
> +
> +    SVGTextRunWalker(const SVGFontData* fontData, SVGFontElement* fontElement, SVGTextRunData& data,
> +                     SVGTextRunWalkerCallback callback, SVGTextRunWalkerMissingGlyphCallback missingGlyphCallback)

Instead of using function pointers you could make the callbacks template parameters for more inlining. Probably not for this patch.
Comment 20 Nikolas Zimmermann 2011-05-24 03:01:18 PDT
(In reply to comment #19)
> (From update of attachment 94223 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=94223&action=review
> 
> Looks good basically, I have some naming nits.
Great.

> 
> > Source/WebCore/GNUmakefile.list.am:3086
> > +	Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp \
> >  	Source/WebCore/rendering/svg/SVGTextMetrics.cpp \
> >  	Source/WebCore/rendering/svg/SVGTextMetrics.h \
> >  	Source/WebCore/rendering/svg/SVGTextQuery.cpp \
> >  	Source/WebCore/rendering/svg/SVGTextQuery.h \
> >  	Source/WebCore/rendering/TableLayout.h \
> > +	Source/WebCore/rendering/TextRunRenderingContext.h \
> 
> Why TextRunRenderingContext.h vs. SVGTextRunRenderingContext.cpp?
Fixed.

> 
> > Source/WebCore/platform/graphics/TextRun.h:111
> > +    class AbstractRenderingContext : public RefCounted<AbstractRenderingContext> {
> 
> I would just leave out the word "Abstract" here.
Fixed, same for AbstractFontData.

> 
> > Source/WebCore/platform/graphics/TextRun.h:114
> > +    public:
> > +        virtual ~AbstractRenderingContext() { }
> > +        virtual bool isWebCoreRenderingContext() const = 0;
> 
> All likely clients are in WebCore (as is this class) so name WebCoreRenderingContext doesn't really differentiate. Maybe just remove the whole function for now, you only have one concrete implementation so casting is always safe. 
Ditto.

> 
> To do this in really non-layer violating way for multiple implementations, you would need to have "int type" field, with enum values defined outside platform.
Right, but as there's just one consumer at the moment, I'll leave it as-is.

> 
> > Source/WebCore/rendering/RenderCombineText.cpp:96
> > -    TextRun run = TextRun(String(text()));
> > +    TextRun run = RenderBlock::constructTextRun(this, originalFont(), String(text()), style());
> 
> I know it is now in this patch but having constructTextRun in RenderBlock seems random. Why isn't it a free standing function?
A free standing function was disliked previously, so I added it to RenderBlock. Can we leave it as-is for now, and eventually move it, if we reach consensus?

> 
> > Source/WebCore/rendering/TextRunRenderingContext.h:32
> > +class TextRunRenderingContext : public TextRun::AbstractRenderingContext {
> > +public:
> 
> I think this should be called SVGTextRunRenderingContext since it has a very specific purpose.
Fixed.

> 
> > Source/WebCore/rendering/svg/SVGInlineTextBox.cpp:389
> > +    TextRun::AbstractRenderingContext* abstractRenderingContext = textRun.renderingContext();
> > +    if (abstractRenderingContext && abstractRenderingContext->isWebCoreRenderingContext())
> > +        static_cast<TextRunRenderingContext*>(abstractRenderingContext)->setActivePaintingResource(m_paintingResource);
> 
> As mentioned above isWebCoreRenderingContext() naming is weird and also unnecessary here.
Fixed.

> 
> > Source/WebCore/rendering/svg/SVGTextRunRenderingContext.cpp:82
> > +template<typename SVGTextRunData>
> > +struct SVGTextRunWalker {
> > +    typedef bool (*SVGTextRunWalkerCallback)(const SVGGlyph&, SVGTextRunData&);
> > +    typedef void (*SVGTextRunWalkerMissingGlyphCallback)(const TextRun&, SVGTextRunData&);
> > +
> > +    SVGTextRunWalker(const SVGFontData* fontData, SVGFontElement* fontElement, SVGTextRunData& data,
> > +                     SVGTextRunWalkerCallback callback, SVGTextRunWalkerMissingGlyphCallback missingGlyphCallback)
> 
> Instead of using function pointers you could make the callbacks template parameters for more inlining. Probably not for this patch.
The whole code is gone, as soon as the SVG Fonts rewrite patch lands, no more use of callbacks or function pointers in the new code.

Preparing a new patch soon.
Comment 21 Nikolas Zimmermann 2011-05-24 06:14:26 PDT
Created attachment 94599 [details]
Patch v4

Fixed Anttis comments.
Comment 22 Antti Koivisto 2011-05-24 06:45:26 PDT
Comment on attachment 94599 [details]
Patch v4

View in context: https://bugs.webkit.org/attachment.cgi?id=94599&action=review

r=me

> Source/WebCore/rendering/svg/SVGTextRunRenderingContext.h:38
> +
> +    RenderObject* context() const { return m_context; }

This could have some more specific name than "context", especially since the class itself is called *Context.
Comment 23 Nikolas Zimmermann 2011-05-24 08:28:25 PDT
(In reply to comment #22)
> (From update of attachment 94599 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=94599&action=review
> 
> r=me
> 
> > Source/WebCore/rendering/svg/SVGTextRunRenderingContext.h:38
> > +
> > +    RenderObject* context() const { return m_context; }
> 
> This could have some more specific name than "context", especially since the class itself is called *Context.

Renamed to renderer(). Thank for the review, landed in r87152.
Comment 24 Nikolas Zimmermann 2011-05-24 09:11:27 PDT
Landed a win build fix in r87156.
Comment 25 Nikolas Zimmermann 2011-05-24 09:11:48 PDT
Landed follow-up build fix in r87158.
Comment 26 Adam Klein 2011-05-24 10:14:58 PDT
This patch seems to have caused a regression on Chromium Webkit/Linux; see https://bugs.webkit.org/show_bug.cgi?id=61370