Bug 16445 - Refactor EventTargetNode & JSEventTargetNode for an upcoming SVG patch
Summary: Refactor EventTargetNode & JSEventTargetNode for an upcoming SVG patch
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: SVG (show other bugs)
Version: 528+ (Nightly build)
Hardware: Mac OS X 10.4
: P2 Normal
Assignee: Nikolas Zimmermann
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-12-14 17:45 PST by Nikolas Zimmermann
Modified: 2007-12-15 14:50 PST (History)
4 users (show)

See Also:


Attachments
Initial patch (92.96 KB, patch)
2007-12-14 18:12 PST, Nikolas Zimmermann
no flags Details | Formatted Diff | Diff
Updated patch (92.53 KB, patch)
2007-12-14 19:13 PST, Nikolas Zimmermann
no flags Details | Formatted Diff | Diff
Updated patch v2 (86.36 KB, patch)
2007-12-15 12:22 PST, Nikolas Zimmermann
eric: review-
Details | Formatted Diff | Diff
Updated patch v3 (86.75 KB, patch)
2007-12-15 14:43 PST, Nikolas Zimmermann
eric: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Nikolas Zimmermann 2007-12-14 17:45:17 PST
To fix bug 15413 (SVGElementInstance does not implement EventTarget) we need to introduce a new class called EventTargetSVGElementInstance inheriting from EventTarget & SVGElementInstance - just like EventTargetNode works.

Some information regarding SVGElementInstance & EventTarget:
SVG spec defines how event handling works for <use> elements and their corresponding SVGElementInstance objects - in particular it says that SVGElementInstance and the element it references share an event listener list. In our SVG implementation SVGElementInstance contains a pointer to the original referenced element as well as it's corresponding shadow tree element. The goal is that SVGUseElement now creates EventTargetSVGElementInstance objects instead of pure SVGElementInstance objects and installs any event listeners added through markup and/or JS on the shadow tree element! This way we can share code using EventTargetNode, as the only difference for EventTargetSVGElementInstance is the origin where it stores it's event listeners.

Example: <defs><rect id="rect"../> <use xlink:href="url(#rect)" id="test"/>
useElement = document.getElementById("test");
rectElement = document.getElementById("rect");

Case #1: someUseElement.instanceRoot.addEventListener(...)
calls 'addEventListener' on the 'EventTargetSVGElementInstance' object - which in turn install an event listener on the shadow tree element.

Case #2:  rectElement.addEventListener(...)
calls 'addEventListener ' on the 'EventTargetNode' object. Altering the event listeners will result in an update of the SVGElementInstances which belong to the 'rectElement' - this will reclone the shadow tree elements -> this way everything stays synchronized.

As it's possible to share a lot of code with the new (JS)EventTargetSVGElementInstance classes - I refactored the EventTargetNode & JSEventTargetNode class into base classes. JSEventTargetNode now forwards most calls to EventTargetNode - same for JSEventTargetSVGElementInstance.

This patch only contains the refactorization of the EventTargetNode classes - no SVG stuff contained here.
Comment 1 Nikolas Zimmermann 2007-12-14 18:12:18 PST
Created attachment 17900 [details]
Initial patch
Comment 2 Oliver Hunt 2007-12-14 18:26:53 PST
Okay, so far this looks sane, although i'd like maciej and possibly hyatt to look at it as well.

My only real concern atm is that a number of the new files use large quantities of code from other files, so the appropriate copyrights should be moved across as well.
Comment 3 Nikolas Zimmermann 2007-12-14 18:32:32 PST
(In reply to comment #2)
> Okay, so far this looks sane, although i'd like maciej and possibly hyatt to
> look at it as well.
> 
> My only real concern atm is that a number of the new files use large quantities
> of code from other files, so the appropriate copyrights should be moved across
> as well.
> 
I started of with fresh header templates and forgot about them - readded all existing copyrights in the approriate files (all files in bindings/js for instance).

Niko
Comment 4 Nikolas Zimmermann 2007-12-14 19:13:23 PST
Created attachment 17901 [details]
Updated patch

New patch after discussion with Maciej - remove the two callbacks and the JSEventTarget* owner pointer from JSEventTargetBase to save size. We can now store JSEventTargetBase by value in JSEventTargetNode (& JSEventTargetSVGElementInstance).

Fixed copyright as noticed by Oliver.
Comment 5 Alexey Proskuryakov 2007-12-15 01:56:06 PST
When reading the spec, I thought that a more natural approach would be to tweak event dispatching, not the elements themselves. It seems unfortunate that listener lists have to be kept in sync - as a general rule, everything that needs to be kept in sync will get out of sync sooner or later.

I.e. when dispatching an event to SVGElementInstance, dispatchGenericEvent() would take listener list from  the original, and so would addEventListener/removeEventListener.

Does this make any sense? Or did I just misunderstand the desired behavior?
Comment 6 Nikolas Zimmermann 2007-12-15 04:48:55 PST
(In reply to comment #5)
> When reading the spec, I thought that a more natural approach would be to tweak
> event dispatching, not the elements themselves. It seems unfortunate that
> listener lists have to be kept in sync - as a general rule, everything that
> needs to be kept in sync will get out of sync sooner or later.
> 
> I.e. when dispatching an event to SVGElementInstance, dispatchGenericEvent()
> would take listener list from  the original, and so would
> addEventListener/removeEventListener.
> 
> Does this make any sense? Or did I just misunderstand the desired behavior?

Hi Alexey, thanks for taking time to check this approach.

It will be easier to discuss once this patch is in, and I actually uploaded the follow-up SVG patch.
The idea is that EventTargetSVGElementInstance, addEventListener/removeEventListener/dispatchEvent etc.. call EventTarget::addEventListener(correspondingElement(), ...) which means that any events dispatched to an element, which gets directly or indirectly referenced by <use> will be intercepted in dispatchGenericEvent() and their 'target' & 'currentTarget' property will be modified, to point to the SVGElementInstance objects. So what you are describing above, seems perfectly fine.

Greetings,
Niko
Comment 7 Nikolas Zimmermann 2007-12-15 05:10:01 PST
Rereading my patch, I have some fears regarding s_prototypeName initialization.
Anyone has a suggestion for a nicer way  not involving a static const char*?

Greetings,
Niko
Comment 8 Nikolas Zimmermann 2007-12-15 12:22:16 PST
Created attachment 17915 [details]
Updated patch v2

Incorporated Erics suggestions:
- create helper method to convert from JSEventTargetProperties tokens to AtomicString to simplify getListener/setListener implementation
- Better way to handle s_prototypeName (better, as in actually working reliable!)

No regressions.
Comment 9 Eric Seidel (no email) 2007-12-15 12:39:06 PST
Comment on attachment 17915 [details]
Updated patch v2

I think this needs at least one more round for code clarity:


WildFox: your indenting is kinda strange in JSEventTargetPrototypeInformation

WildFox: IMO the big +    if (eventType == DOMSubtreeModifiedEvent) if-chain should be abstracted into a static inline funtion
type = listenerTypeForEventType(eventType)
WildFox: actually, better would probably be shouldEnableSpecialListenerTypeOnDocument(eventType, type)
then you can combine it with the next if
something like that

WildFox: you also seem to have done a search replace of /listen//
WildFox: +        // We do want capturing event ers to be invoked here, even though
WildFox: you should probably search for " ers"

WildFox: if you're moving the code anyway, you shoudl clean up the "why don't we recalc the node chain here" comment
which is about 15 lines long and only needs to be 2 :)
"We don't recalc the node chain here because she spec says so:"
[spec quote]

WildFox: all of your:
+#if ENABLE(SVG)
+        evt->setCurrentTarget(applySVGEventTargetRules(eventTargetNode, evt.get()));
blocks shoudl just change to a single static iline
setCurrentTargetRespectingSVGTargetRules(evt, eventTargetNode)
WildFox: and then you can have the special logic all in one plac,e instead of lots of copy/paste #if ENABLE code
Comment 10 Nikolas Zimmermann 2007-12-15 14:43:27 PST
Created attachment 17917 [details]
Updated patch v3

Updated, fixing Erics comments.
Comment 11 Eric Seidel (no email) 2007-12-15 14:48:11 PST
Comment on attachment 17917 [details]
Updated patch v3

Looks good.

+    // Remove existing identical er set with identical arguments.

Still need to do a search for " er" and fix those to " listener". :)

dispatchGenericEvent is horribly long, we'll eventually need to break it up into more static inlines.

Looks good.
Comment 12 Nikolas Zimmermann 2007-12-15 14:50:54 PST
Landed in r28756 with Erics last comment fixed.