Bug 116020

Summary: [GTK][WK2] Java applets remain visible even if you navigate to a different page
Product: WebKit Reporter: Manuel Rego Casasnovas <rego>
Component: WebKitGTKAssignee: Zan Dobersek <zan>
Status: RESOLVED WORKSFORME    
Severity: Normal CC: benjamin, cgarcia, csaavedra, gustavo, kling, mcatanzaro, mrobinson, zan
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch none

Description Manuel Rego Casasnovas 2013-05-13 03:54:00 PDT
Steps to reproduce in MiniBrowser:
1) Go to http://lessonplans.btskinner.com/applet.html
2) Change the URL in MiniBrowser to http://www.webkit.org
3) You'll see that the applet is still visible and working in WebKit page.

BTW, you need to install "icedtea-plugin" in your machine in order to have the Java applets working.
Comment 1 Manuel Rego Casasnovas 2013-05-13 04:02:30 PDT
Some more information about the bug.

In WebPageProxy::createPluginContainer() we connect a callback to "plug-removed" signal (https://developer.gnome.org/gtk3/stable/GtkSocket.html#GtkSocket-plug-removed). But this signal is never emitted.

I've been doing some tests and if I emit manually the signal from WebKitWebView::webkit_web_view_load_uri() the applet is still visible in the other page.

I had to destroy manually the applet to make it disappear.

I've added the following lines to WebKitWebView::webkit_web_view_load_uri() in order to do this quick hack:

    GList* items = gtk_container_get_children(GTK_CONTAINER(webView));
    GList* iter;

    for (iter = items; iter; iter = g_list_next(iter)) {
        GtkWidget* item = GTK_WIDGET(iter->data);

        if (GTK_IS_SOCKET(item)) {
            gboolean result;
            // With this the callback is called, but the applet is not destroyed.
            g_signal_emit_by_name(item, "plug-removed", &result);
            // This destroys the applet, gtk_container_remove() also works here.
            gtk_widget_destroy(item);
        }
    }
Comment 2 Zan Dobersek 2013-05-13 07:05:56 PDT
Fun!
Comment 3 Zan Dobersek 2013-05-13 13:50:56 PDT
Spent a good amount of time on this, no success yet.

When navigating away from the test page, I can see that the HTMLAppletElement is not getting detached from the DOM, and that's why its renderer isn't getting destroyed. HTMLAppletElement inherits from HTMLPlugInImageElement, which inherist from HTMLPluginElement, which inherist from HTMLElement and so on to the Node class.

The renderer is a RenderApplet, which inherits all the way from RenderWidget, which holds a reference to the Widget that it renders, the Widget being the PluginView that's created in the WebFrameLoaderClient::createPlugin method (called from SubframeLoader::loadPlugin).

Continuing on the note of HTMLAppletElement not detaching, the node normally detaches (and has its render destroyed) in its deconstructor. Since this is not happening in WK2, I think there's a reference of the HTMLAppletElement object being kept somewhere, preventing for the element to be destroyed. Not 100% sure about this, but it certainly seems so to me given that in WK1, the HTML element is destroyed first, with the renderer destroyed second and the PluginView following immediately after.

The PluginView class actually holds a RefPtr for the HTMLPluginElement, the very element for which the plugin is loaded. Replacing the RefPtr with a raw pointer to the HTMLPluginElement object doesn't help, the problem persists, though it's kind of unusual to do so, especially given that this is not done in the WK1 layer (in Source/WebCore/plugins/PluginView.cpp).
Comment 4 Zan Dobersek 2013-05-14 07:19:12 PDT
The HTMLPlugInElement not being detached is due to the page cache enabled when using the MiniBrowser. Specifically, the whole DOM document is not detached & destroyed  (initiaded at [1]) if the page cache is in use. As proof, with the page cache disabled, the Java applet renderer is properly destroyed when navigating away from the test page.

Still, even with the page cache enabled, the plugin renderer should be removed.
When the FrameView is being replaced in Frame::createView (which happens when navigating to another location), the current FrameView's parent is set to not visible[2]. The FrameView inherits from the ScrollView, meaning that ScrollView::setParentVisible[3] is called. That method calls the same method on every child widget, which, on the test page, is also the WebKit2 PluginView[4], the Widget-implementing object that controls the plugin. The PluginView doesn't doesn't implement the setParentVisible function, but when added, it does get called with the 'visible' argument being false. Still, that has no effect on hiding the widget when navigating away (which of course doesn't happen when it should).

[1] http://trac.webkit.org/browser/trunk/Source/WebCore/page/Frame.cpp#L262
[2] http://trac.webkit.org/browser/trunk/Source/WebCore/page/Frame.cpp#L772
[3] http://trac.webkit.org/browser/trunk/Source/WebCore/platform/ScrollView.cpp#L1249
[4] http://trac.webkit.org/browser/trunk/Source/WebKit2/WebProcess/Plugins/PluginView.h
Comment 5 Zan Dobersek 2013-05-16 07:10:55 PDT
The main problem here seems to be that the pages containing the plugin are still stored into the page cache when navigating away from them. For the GTK port, I don't believe that's a good idea.

For instance, when leaving Youtube with a video playing, the Flash plugin would also be stored in the page cache had it not been for unload event listeners on the page that actually prevent the page from entering the cache[1]. Similarly with the pages containing Java applets, the applet's PluginView is kept as is instead of being destroyed, meaning that the windowed plugin is both not hidden and is kept alive and functioning - even if it would be hidden, it would most likely continue to run in the background (note that I haven't really tested that, but can if someone explicitly requests for it) which I believe is not expected.

The pages that contain plugins can be prevented from entering the cache by adjusting the according setting[2] which defaults to false. In WebKit1GTK+ the setting was not adjusted at all (meaning its value was kept at false), while in WebKit2GTK+ the setting defaults to true[3].

I'd recommend to use false as the default for this setting in WebKit2GTK+. If approved, the patch is pretty straight-forward.

[1] http://trac.webkit.org/browser/trunk/Source/WebCore/history/PageCache.cpp#L305
[2] http://trac.webkit.org/browser/trunk/Source/WebCore/page/Settings.in#L72
[3] http://trac.webkit.org/browser/trunk/Source/WebKit2/Shared/WebPreferencesStore.h#L105
Comment 6 Gustavo Noronha (kov) 2013-05-16 10:11:17 PDT
It makes sense to me. On Mac all plugins are windowless AFAIK. I remember talking about this with Martin at some point and he mentioned forcing all plugins to go windowless for wk2gtk, was that not possible after all or was it decided against?
Comment 7 Martin Robinson 2013-05-16 10:28:30 PDT
(In reply to comment #6)
> It makes sense to me. On Mac all plugins are windowless AFAIK. I remember talking about this with Martin at some point and he mentioned forcing all plugins to go windowless for wk2gtk, was that not possible after all or was it decided against?

Hrm. I don't remember the specifics, but we support windowed plugins now.
Comment 8 Zan Dobersek 2013-05-17 11:38:26 PDT
Created attachment 202127 [details]
Patch
Comment 9 Martin Robinson 2013-05-17 11:40:32 PDT
Comment on attachment 202127 [details]
Patch

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

Looks okay, but needs a WebKit2 owner to review it formally.

> Source/WebKit2/WebProcess/Plugins/Netscape/x11/NetscapePluginX11.cpp:197
> +    g_signal_connect(socket, "plug-removed", G_CALLBACK(socketPlugRemovedCallback), m_platformPluginWidget);

Are you sure that removing the widget doesn't destroy it as a side-effect of decreasing the reference count?
Comment 10 Zan Dobersek 2013-05-17 11:48:46 PDT
Comment on attachment 202127 [details]
Patch

Removing it from where exactly?

This callback gets fired when the windowed plugin is destroyed/stopped, so the intermediate GtkSocket (the one created on the previous line) has this signal emitted and the callback is removed.

This GtkSocket is inside the GtkPlug that's belonging to the GtkSocket that was created in the UIProcess. That GtkSocket does not get removed until its GtkPlug is removed.

Not destroying the parent plug of the windowed widget's socket leaves a gray area where the UIProcess socket is displaying the contents of the now-defunct parent plug.
Comment 11 Zan Dobersek 2013-05-19 00:13:27 PDT
Benjamin, Andreas -- could one of you please take a look at this one?
Comment 12 Anders Carlsson 2013-05-19 15:41:02 PDT
Comment on attachment 202127 [details]
Patch

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

> Source/WebKit2/Shared/WebPreferencesStore.h:107
> -    macro(PageCacheSupportsPlugins, pageCacheSupportsPlugins, Bool, bool, true) \
> +    macro(PageCacheSupportsPlugins, pageCacheSupportsPlugins, Bool, bool, DEFAULT_PAGE_CACHE_SUPPORTS_PLUGINS_ENABLED) \

I don't understand why this change is needed.
Comment 13 Zan Dobersek 2013-05-19 23:10:37 PDT
Comment on attachment 202127 [details]
Patch

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

>> Source/WebKit2/Shared/WebPreferencesStore.h:107
>> +    macro(PageCacheSupportsPlugins, pageCacheSupportsPlugins, Bool, bool, DEFAULT_PAGE_CACHE_SUPPORTS_PLUGINS_ENABLED) \
> 
> I don't understand why this change is needed.

The GTK port still supports windowed Netscape plugins. When entered into the page cache, their PluginView is not destroyed, so these plugins continue to run as if nothing happened. Because they are windowed, their display window does not get hidden or destroyed, so they are still displayed when navigating away from the page that contained them. Disabling this setting for the GTK port fixes this without any unnecessary hacks that would be required in the PluginView and would extend all the way into the plugin process.

This setting was also disabled for the WebKit1 GTK port, so this wasn't happening there. Furthermore, of the WebKit1 layer ports, only the Mac port was enabling this setting (or at least exposing the enabling and disabling mechanism), meaning that Qt and EFL ports of the WK2 layer may now be exposed to the very same problem (if they support windowed plugins).
Comment 14 Zan Dobersek 2013-06-12 12:07:43 PDT
Any remaining objections from WK2 owner(s)?
Comment 15 Martin Robinson 2014-11-19 15:59:00 PST
Interested in pushing this fix still?
Comment 16 Michael Catanzaro 2015-12-22 06:56:17 PST
Zan, is this patch still needed?
Comment 17 Zan Dobersek 2016-01-03 01:58:40 PST
The patch in this form doesn't apply anymore, and I'm not aware of Java applets still being a problem.
Comment 18 Michael Catanzaro 2016-01-03 08:15:47 PST
Comment on attachment 202127 [details]
Patch

Removing from request queue then, and closing the bug... please reopen if this is discovered to still be a problem.