Bug 58027 - Avoid leaking document when leaving google.com due to geolocation permission request
Summary: Avoid leaking document when leaving google.com due to geolocation permission ...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: 528+ (Nightly build)
Hardware: PC All
: P2 Normal
Assignee: Satish Sampath
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-04-07 03:59 PDT by Kenneth Rohde Christiansen
Modified: 2011-10-19 17:27 PDT (History)
6 users (show)

See Also:


Attachments
Patch (2.44 KB, patch)
2011-04-07 04:08 PDT, Kenneth Rohde Christiansen
no flags Details | Formatted Diff | Diff
Patch (1.58 KB, patch)
2011-10-15 02:11 PDT, Satish Sampath
no flags Details | Formatted Diff | Diff
Patch (2.73 KB, patch)
2011-10-16 02:24 PDT, Satish Sampath
no flags Details | Formatted Diff | Diff
Patch (2.74 KB, patch)
2011-10-16 14:15 PDT, Satish Sampath
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Kenneth Rohde Christiansen 2011-04-07 03:59:42 PDT
SSIA
Comment 1 Kenneth Rohde Christiansen 2011-04-07 04:08:49 PDT
Created attachment 88606 [details]
Patch
Comment 2 John Knottenbelt 2011-04-07 04:24:34 PDT
Comment on attachment 88606 [details]
Patch

This seems correct to me. If we are removing the notifiers from the oneShots and watchers list, it also makes sense to remove them from the other lists too.
Comment 3 Steve Block 2011-04-08 04:35:28 PDT
Comment on attachment 88606 [details]
Patch

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

> Source/WebCore/ChangeLog:11
> +        In fatalErrorOccurred (which is called on cancellation), the notifier

What do you mean by 'cancellation'? Can you elaborate in the bug exactly what the problem is? Presumably it's not unique to google.com?

> Source/WebCore/ChangeLog:18
> +

Can you add a test for this? If not, you should explain here why not.

> Source/WebCore/page/Geolocation.cpp:342
> +    m_requestsAwaitingCachedPosition.remove(notifier);

I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?
Comment 4 Kenneth Rohde Christiansen 2011-04-08 05:08:13 PDT
(In reply to comment #3)
> (From update of attachment 88606 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=88606&action=review
> 
> > Source/WebCore/ChangeLog:11
> > +        In fatalErrorOccurred (which is called on cancellation), the notifier
> 
> What do you mean by 'cancellation'? Can you elaborate in the bug exactly what the problem is? Presumably it's not unique to google.com?

Reloading the page or loading another one, leaks document() probably due to event holding a ref to document (presumable TargetEvent).

It probably happens on other pages as well (using watchPosition?), but I didn't manage to trigger the leak with my own simple tests, and debugging what exactly is happening on google.com is pretty hard due to the obscured javascript code.

> 
> > Source/WebCore/ChangeLog:18
> > +
> 
> Can you add a test for this? If not, you should explain here why not.

The test can only work for ports using the PREEMPT_... build flag, and I am not sure now to actually test this, so any help is appreciated. Testing it is hard due to the reloading, plus the fact that I cannot AFAIK count the document references from LayoutTestController.

> > Source/WebCore/page/Geolocation.cpp:342
> > +    m_requestsAwaitingCachedPosition.remove(notifier);
> 
> I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?

I added this one for completions sake. It can be left out.
Comment 5 Steve Block 2011-04-08 06:19:15 PDT
Comment on attachment 88606 [details]
Patch

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

>>> Source/WebCore/ChangeLog:11
>>> +        In fatalErrorOccurred (which is called on cancellation), the notifier
>> 
>> What do you mean by 'cancellation'? Can you elaborate in the bug exactly what the problem is? Presumably it's not unique to google.com?
> 
> Reloading the page or loading another one, leaks document() probably due to event holding a ref to document (presumable TargetEvent).
> 
> It probably happens on other pages as well (using watchPosition?), but I didn't manage to trigger the leak with my own simple tests, and debugging what exactly is happening on google.com is pretty hard due to the obscured javascript code.

Do you see this only on Chromium? I think it would be good to understand a little more about the problem before we make the fix. The Geolocation callbacks don't ref their ScriptExecutionContext, so I don't see how removing this notifier from this list will avoid a leak. Presumably the Geolocation object is leaked too?
Comment 6 Kenneth Rohde Christiansen 2011-04-09 04:20:31 PDT
> Do you see this only on Chromium? I think it would be good to understand a little more about the problem before we make the fix. The Geolocation callbacks don't ref their ScriptExecutionContext, so I don't see how removing this notifier from this list will avoid a leak. Presumably the Geolocation object is leaked too?

This happens on our Qt WebKit2 port (a branch on gitorious). It was pretty hard to debug because Document is ref'ed for each event, but adding this plugged the leak.
Comment 7 Eric Seidel (no email) 2011-05-23 15:09:20 PDT
Comment on attachment 88606 [details]
Patch

We need to find some way to test this.  Manual tests are fine.
Comment 8 John Knottenbelt 2011-05-25 03:18:22 PDT
Comment on attachment 88606 [details]
Patch

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

A test to count the number of notifiers left in the lists after page reload should be possible. However, exposing this count seems only useful for this particular test case. Is there a way to add this test without complicating the interfaces of the Geolocation, GeolocationController, GeolocationClient and embedder-specific client classes?

>>> Source/WebCore/page/Geolocation.cpp:342
>>> +    m_requestsAwaitingCachedPosition.remove(notifier);
>> 
>> I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?
> 
> I added this one for completions sake. It can be left out.

Steve: Yes, we can get a build up of notifiers in this case, since the main Frame (and therefore Geolocation instance) is reused across page reloads.
Comment 9 John Knottenbelt 2011-05-25 03:33:45 PDT
fast/dom/Geolocation/page-reload-cancel-permission-requests.html is a related test that tests that geolocation permission requests are properly cancelled at the GeolocationClient on page reload.

(In reply to comment #8)
> (From update of attachment 88606 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=88606&action=review
> 
> A test to count the number of notifiers left in the lists after page reload should be possible. However, exposing this count seems only useful for this particular test case. Is there a way to add this test without complicating the interfaces of the Geolocation, GeolocationController, GeolocationClient and embedder-specific client classes?
> 
> >>> Source/WebCore/page/Geolocation.cpp:342
> >>> +    m_requestsAwaitingCachedPosition.remove(notifier);
> >> 
> >> I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?
> > 
> > I added this one for completions sake. It can be left out.
> 
> Steve: Yes, we can get a build up of notifiers in this case, since the main Frame (and therefore Geolocation instance) is reused across page reloads.
Comment 10 Satish Sampath 2011-10-14 08:28:38 PDT
I've noticed the same issue. I've tested only with Chromium so not sure if it happens with other browsers as well. FWIW, I found adding the following to the Geolocation::reset() method works as well.

#if USE(PREEMPT_GEOLOCATION_PERMISSION)
    m_pendingForPermissionNotifiers.remove(notifier);
#endif
Comment 11 Satish Sampath 2011-10-14 08:31:44 PDT
I can also confirm that the leaking of Document was due to these pending GeoNotifier objects. In chromium these were creating a persistent v8 handle which weren't released properly (as these were still alive when the page closed) caused the whole DOM to be leaked when the page closed.
Comment 12 Satish Sampath 2011-10-14 08:46:12 PDT
Sorry, I should've mentioned that I used this code in the reset() method:

#if USE(PREEMPT_GEOLOCATION_PERMISSION)
    m_pendingForPermissionNotifiers.clear();
#endif
Comment 13 John Knottenbelt 2011-10-14 09:26:03 PDT
I think we should also be removing the watch from  m_pendingForPermissionNotifiers in Geolocation::clearWatch to avoid potential build up of watches by repeated calls to watchPosition and clearWatch.
Comment 14 Satish Sampath 2011-10-15 02:11:51 PDT
Created attachment 111130 [details]
Patch
Comment 15 Kenneth Rohde Christiansen 2011-10-15 05:52:12 PDT
Comment on attachment 111130 [details]
Patch

Nice, but can you implement John's suggestion as well? It could be a follow up.
Comment 16 John Knottenbelt 2011-10-15 10:25:53 PDT
Comment on attachment 111130 [details]
Patch

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

> Source/WebCore/page/Geolocation.cpp:344
> +#if USE(PREEMPT_GEOLOCATION_PERMISSION)

The change to Geolocation::reset() is enough - m_pendingForPermissionNotifiers is cleared in Geolocation::setIsAllowed. All other calls to setFatalError are called after permission has been determined, so the notifier won't be in this list.

It would be good to move these lines to Geolocation::clearWatch(), though, to avoid a potential build up of watches in this set.
Comment 17 Satish Sampath 2011-10-16 02:18:02 PDT
Comment on attachment 111130 [details]
Patch

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

>> Source/WebCore/page/Geolocation.cpp:344
>> +#if USE(PREEMPT_GEOLOCATION_PERMISSION)
> 
> The change to Geolocation::reset() is enough - m_pendingForPermissionNotifiers is cleared in Geolocation::setIsAllowed. All other calls to setFatalError are called after permission has been determined, so the notifier won't be in this list.
> 
> It would be good to move these lines to Geolocation::clearWatch(), though, to avoid a potential build up of watches in this set.

Will do.
Comment 18 Satish Sampath 2011-10-16 02:24:03 PDT
Created attachment 111178 [details]
Patch
Comment 19 Kenneth Rohde Christiansen 2011-10-16 02:33:45 PDT
Comment on attachment 111178 [details]
Patch

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

> Source/WebCore/page/Geolocation.cpp:178
> +Geolocation::GeoNotifier* Geolocation::Watchers::get(int id)

WebKit style would be to call this method for "find"
Comment 20 Kenneth Rohde Christiansen 2011-10-16 02:35:58 PDT
Comment on attachment 111178 [details]
Patch

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

> Source/WebCore/page/Geolocation.cpp:436
> +    GeoNotifier* notifier = m_watchers.get(watchId);
> +    if (notifier)
> +        m_pendingForPermissionNotifiers.remove(notifier);

You could change this into:

 if (GeoNotifier* notifier = m_watchers.find(watchId))
     m_pendingForPermissionNotifiers.remove(notifier);
Comment 21 Kenneth Rohde Christiansen 2011-10-16 02:40:20 PDT
> > The change to Geolocation::reset() is enough - m_pendingForPermissionNotifiers is cleared in Geolocation::setIsAllowed.

I think it would be better do document such behavior using ASSERTs if possible.
Comment 22 Satish Sampath 2011-10-16 14:13:42 PDT
Comment on attachment 111178 [details]
Patch

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

>> Source/WebCore/page/Geolocation.cpp:178
>> +Geolocation::GeoNotifier* Geolocation::Watchers::get(int id)
> 
> WebKit style would be to call this method for "find"

Will do

>> Source/WebCore/page/Geolocation.cpp:436
>> +        m_pendingForPermissionNotifiers.remove(notifier);
> 
> You could change this into:
> 
>  if (GeoNotifier* notifier = m_watchers.find(watchId))
>      m_pendingForPermissionNotifiers.remove(notifier);

Will do
Comment 23 Satish Sampath 2011-10-16 14:15:26 PDT
Created attachment 111186 [details]
Patch
Comment 24 Kenneth Rohde Christiansen 2011-10-17 01:39:11 PDT
Comment on attachment 111186 [details]
Patch

John are you OK with this?
Comment 25 John Knottenbelt 2011-10-17 01:43:53 PDT
(In reply to comment #24)
> (From update of attachment 111186 [details])
> John are you OK with this?

Looks good to me. thanks for reviewing!
Comment 26 WebKit Review Bot 2011-10-17 02:01:29 PDT
Comment on attachment 111186 [details]
Patch

Clearing flags on attachment: 111186

Committed r97594: <http://trac.webkit.org/changeset/97594>
Comment 27 WebKit Review Bot 2011-10-17 02:01:38 PDT
All reviewed patches have been landed.  Closing bug.
Comment 28 Steve Block 2011-10-17 08:32:58 PDT
Comment on attachment 111186 [details]
Patch

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

> Source/WebCore/page/Geolocation.h:116
> +        GeoNotifier* find(int id);

Do we need this new method? All we do with the GeoNotifier returned by this method is pass it to remove(GeoNotifier*), but Watchers already offers a remove(int id) method, which achieves the same thing.
Comment 29 Satish Sampath 2011-10-17 08:38:46 PDT
Comment on attachment 111186 [details]
Patch

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

>> Source/WebCore/page/Geolocation.h:116
>> +        GeoNotifier* find(int id);
> 
> Do we need this new method? All we do with the GeoNotifier returned by this method is pass it to remove(GeoNotifier*), but Watchers already offers a remove(int id) method, which achieves the same thing.

You're right, I don't know how I missed that :/ Will upload a patch to remove this method and use remove(int id) instead.
Comment 30 Satish Sampath 2011-10-17 08:45:09 PDT
Comment on attachment 111186 [details]
Patch

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

>>> Source/WebCore/page/Geolocation.h:116
>>> +        GeoNotifier* find(int id);
>> 
>> Do we need this new method? All we do with the GeoNotifier returned by this method is pass it to remove(GeoNotifier*), but Watchers already offers a remove(int id) method, which achieves the same thing.
> 
> You're right, I don't know how I missed that :/ Will upload a patch to remove this method and use remove(int id) instead.

m_pendingForPermissionNotifiers is not part of m_watchers. So calling Watchers::remove(int id) can't remove this pending item. So leaving it as is.
Comment 31 Steve Block 2011-10-17 09:14:26 PDT
>>>> Source/WebCore/page/Geolocation.cpp:342
>>>> +    m_requestsAwaitingCachedPosition.remove(notifier);
>>> 
>>> I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?
>> I added this one for completions sake. It can be left out.
> Steve: Yes, we can get a build up of notifiers in this case, since the main Frame (and therefore Geolocation instance) is reused across page reloads.

John, are you still concerned about this? How exactly do we handle cancelling regular requests in this case? It looks like the only time we do any cancelling of requests is when reset() or disconectFrame() is called. Is either of these called in this case? If you think there's a problem, let's open a new bug, preferably with a test case.
Comment 32 John Knottenbelt 2011-10-17 10:17:54 PDT
Steve, are you asking about my earlier proposed change to Geolocation::fatalErrorOccurred? If so, I think it's not necessary to make any changes there now as they will be cleared up in ::reset() with Satish's change.

(In reply to comment #31)
> >>>> Source/WebCore/page/Geolocation.cpp:342
> >>>> +    m_requestsAwaitingCachedPosition.remove(notifier);
> >>> 
> >>> I think that the only way for a notifier to be in this list when it encounters a fatal error is when Geolocation::reset() is called. Is this what you're protecting against?
> >> I added this one for completions sake. It can be left out.
> > Steve: Yes, we can get a build up of notifiers in this case, since the main Frame (and therefore Geolocation instance) is reused across page reloads.
> 
> John, are you still concerned about this? How exactly do we handle cancelling regular requests in this case? It looks like the only time we do any cancelling of requests is when reset() or disconectFrame() is called. Is either of these called in this case? If you think there's a problem, let's open a new bug, preferably with a test case.
Comment 33 Steve Block 2011-10-19 10:03:18 PDT
Yes, I was talking about your comment about clearing m_requestsAwaitingCachedPosition in fatalErrorOccurred() in the first patch set uploaded to this bug. Satish's patch doesn't touch m_requestsAwaitingCachedPosition, right?
Comment 34 John Knottenbelt 2011-10-19 17:27:45 PDT
Steve, I'm not sure if there is a problem there now. I've opened https://bugs.webkit.org/show_bug.cgi?id=70461 to track an investigation of the question.