On Sun, 22 May 2022 14:06:41 GMT, Jay Bhaskar <jbhas...@openjdk.org> wrote:

>> This PR is new implementation of JavaEvent listener memory management.
>> Issue  
>> [JDK-8088420](https://bugs.openjdk.java.net/browse/JDK-8088420?filter=-1)
>> 
>> 1. Calling remove event listener does not free jni global references.
>> 2. When WebView goes out of scope (disposed from app) , its Event Listeners 
>> are not being garbage collected.
>> 
>> Solution:
>> 1.  Detached the jni global reference from JavaEventListener.
>> 2. Create scoped ref counted wrapper class JavaObjectWrapperHandler for jni 
>> global reference.
>> 3. Create unique  JavaObjectWrapperHandler object for each JavaEventListener.
>> 4. EventListenerManager is a singleton class , which stores the 
>> JavaObjectWrapperHandler mapped with JavaEventListener.
>> 5. EventListenerManager also stores the JavaEventListener mapped with 
>> DOMWindow.
>> 6. When Event listener explicitly removed , JavaEventListener is being 
>> forwarded to EventListenerManager to clear the listener.
>> 7. When WebView goes out of scope, EventListenerManager will de-registered 
>> all the event listeners based on the ref counts attached with WebView 
>> DOMWindow.
>
> Jay Bhaskar has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Adding JGObject in plave of raw jni object

I left a couple comments and one question on the changes to the fix.

The new tests look good. I left a few comments and suggestions on the test, but 
overall they are good additions to verify the fix.

modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.cpp
 line 29:

> 27: #include "JavaEventListener.h"
> 28: #include "DOMWindow.h"
> 29: #include <stdio.h>

You probably don't need to include `stdio.h`

modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.cpp
 line 99:

> 97: }
> 98: 
> 99: void EventListenerManager::resetDOMWindow(DOMWindow* window)

Have you validated this logic? If I understand correctly, the 
`isReferringToOtherListener` var will be true if any listener in the list of 
listeners for this DOM window has a ref count > 1. Is my understanding correct? 
This doesn't seem quite right to me, but I may be missing something.

modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.h
 line 53:

> 51:     JavaObjectWrapperHandler(const JLObject& handler) {
> 52:         JNIEnv *env = nullptr;
> 53:         env = JavaScriptCore_GetJavaEnv();

You can remove these two lines, since `env` is now unused here.

modules/javafx.web/src/main/native/Source/WebCore/bindings/java/EventListenerManager.h
 line 60:

> 58:         JNIEnv *env = nullptr;
> 59:         env = JavaScriptCore_GetJavaEnv();
> 60:         env->DeleteGlobalRef(handler_);

I think you can replace these three lines with: `handler_.clear();`

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 96:

> 94:     }
> 95: 
> 96:     static class MyListener1 implements EventListener {

There is no need for another subclass of `EventListener`. It is unused and can 
be removed.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 630:

> 628:      */
> 629:     @Test
> 630:     public void TestStrongRefNewContentLoad() throws Exception {

You should set `webView2 = null` here

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 657:

> 655:         });
> 656: 
> 657:         // Verify that all listeners have not been released

This comment is not right. It should be something like:


    // Verify that the click event is not delivered to the event handler.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 664:

> 662:         listeners1.clear();
> 663:         domNodes1.clear();
> 664:         webViewRefs.clear();

This makes the subsequent call to `assertNumActive` ineffective. There should 
never be a need for any test method to explicitly modify the global refs lists.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 672:

> 670:         // Verify that no listeners are strongly held
> 671:         assertNumActive("MyListener", listenerRefs, 0);
> 672:         listenerRefs.clear();

It is not necessary to clear the `listenerRefs` list. There is never a need for 
a test to explicitly modify this list.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 676:

> 674: 
> 675:     /**
> 676:      * Test that the listener ref cont increase on addevent and decrease 
> on remove event

Typo: cont -> count

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 706:

> 704:         });
> 705: 
> 706:         // Verify that the events are delivered to the listeners (0 and 
> 2 are same)

Actually all three refer to the same listener.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 713:

> 711:         MyListener tmpListener = listeners.get(0).get();
> 712: 
> 713:         // remove previously registered listeners fro dom nodes

Typo: fro -> from

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 716:

> 714:         submit(() -> {
> 715:             domNodes1 = getDomNodes(webView1);
> 716:             assertEquals(NUM_DOM_NODES, domNodes1.size());

I don't think you need to repeat these two calls.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 731:

> 729:         });
> 730: 
> 731:         // Verify that the events are delivered to the listeners (0 and 
> 2 are same)

This comment is wrong. What you are verifying is that the events are _not_ 
delivered, which is why the count remains at 3.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 765:

> 763:         assertEquals("Click count", 6, 
> listeners.get(1).get().getClickCount() + 
> listeners.get(0).get().getClickCount());
> 764: 
> 765:         // add events listeners again

that should be "remove"

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 797:

> 795:         // Verify that no listeners are strongly held
> 796:         assertNumActive("MyListener", listenerRefs, 0);
> 797:         listenerRefs.clear();

No need to clear.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 855:

> 853:         // Verify that active listener
> 854:         assertNumActive("MyListener", listenerRefs, 0);
> 855:         listenerRefs.clear();

No need to clear this list.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 921:

> 919:         // Verify that the events are delivered to both webviews
> 920:         Thread.sleep(100);
> 921:         assertEquals("Click count", 3, 
> listeners.get(0).get().getClickCount());

You might want to assert the counts for the other listeners, too.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 959:

> 957:         domNodes2.clear();
> 958:         webView2 = null;
> 959:         Thread.sleep(100);

This isn't harmful, but isn't needed. The sleep(100) is only used in the tests 
prior to checking click count. The `assertNumActive` method already does sleep 
between checks.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 962:

> 960:         // Verify that active listener
> 961:         assertNumActive("MyListener", listenerRefs, 0);
> 962:         listenerRefs.clear();

Same comment as previously about not clearing this list.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 991:

> 989:             click(webView1, 0);
> 990:         });
> 991: 

It would be useful to call `assertNumActive' and verify that there are two 
active refs.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 1003:

> 1001:             click(webView1, 0);
> 1002:         });
> 1003: 

It would be useful to call `assertNumActive' and verify that there is one 
active ref.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 1005:

> 1003: 
> 1004:         // Verify that listener has been released
> 1005:         assertEquals("Click count", 1, 
> listeners.get(0).getClickCount());

sleep(100) before this check.

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 1008:

> 1006:         assertEquals("Click count", 2, 
> listeners.get(1).getClickCount());
> 1007: 
> 1008:         // make web view , goes out of scope

Suggestion: "// make WebView go out of scope"

modules/javafx.web/src/test/java/test/javafx/scene/web/EventListenerLeakTest.java
 line 1016:

> 1014:         // Verify that active listener
> 1015:         assertNumActive("MyListener", listenerRefs, 0);
> 1016:         listenerRefs.clear();

Same comment about not needing to clear this.

-------------

PR: https://git.openjdk.java.net/jfx/pull/799

Reply via email to