Bug 169322 - Resource Load Statistics: Communicate to the network process which domains to partition
Summary: Resource Load Statistics: Communicate to the network process which domains to...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebKit2 (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: John Wilander
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2017-03-07 17:21 PST by John Wilander
Modified: 2017-03-08 20:02 PST (History)
8 users (show)

See Also:


Attachments
Patch (25.78 KB, patch)
2017-03-07 20:32 PST, John Wilander
no flags Details | Formatted Diff | Diff
Patch (56.25 KB, patch)
2017-03-08 13:19 PST, John Wilander
no flags Details | Formatted Diff | Diff
Patch (56.31 KB, patch)
2017-03-08 13:57 PST, John Wilander
no flags Details | Formatted Diff | Diff
Patch (56.31 KB, patch)
2017-03-08 14:28 PST, John Wilander
no flags Details | Formatted Diff | Diff
Patch (53.28 KB, patch)
2017-03-08 17:02 PST, John Wilander
no flags Details | Formatted Diff | Diff
Patch (53.19 KB, patch)
2017-03-08 18:15 PST, John Wilander
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description John Wilander 2017-03-07 17:21:13 PST
The UI process should let the network process know which domains it should partition cookies for.
Comment 1 John Wilander 2017-03-07 17:22:02 PST
rdar://problem/30768921
Comment 2 John Wilander 2017-03-07 20:32:30 PST
Created attachment 303770 [details]
Patch
Comment 3 John Wilander 2017-03-08 00:44:18 PST
I have a proposed test setup for this but there's something going on in partitioning between localhost and 127.0.0.1. Will investigate further.
Comment 4 Brent Fulgham 2017-03-08 09:52:38 PST
Comment on attachment 303770 [details]
Patch

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

> Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:103
> +    Vector<String> prevalentResourceDomainsWithoutUserInteraction;

If 'loadedStatistics' is generally likely to be more than 16 (or whatever the default Vector size is), we should call "prevalentResourceDomainsWithoutUserInteraction.reserveCapacity(loadedStatistics.size())" to avoid having to grow the Vector multiple times during the loop.

> Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:150
> +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const String& primaryDomain, bool value)> handler)

I think this needs to be "std::function<void(const ... etc.)>&& handler"

> Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:155
> +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const Vector<String>& primaryDomains, bool value)> handler)

Ditto --> I think handler has to be passed by && to avoid a copy.

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:114
> +    for (HashSet<String>::iterator iter = m_cookiePartitioningList.begin(); iter != m_cookiePartitioningList.end(); ++iter) {

Please use a modern C++ iterator here:

for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {
    ...

> Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.cpp:213
> +    

Please undo this whitespace change.
Comment 5 John Wilander 2017-03-08 13:19:14 PST
Created attachment 303833 [details]
Patch
Comment 6 John Wilander 2017-03-08 13:21:14 PST
(In reply to comment #4)
> Comment on attachment 303770 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=303770&action=review
> 
> > Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:103
> > +    Vector<String> prevalentResourceDomainsWithoutUserInteraction;
> 
> If 'loadedStatistics' is generally likely to be more than 16 (or whatever
> the default Vector size is), we should call
> "prevalentResourceDomainsWithoutUserInteraction.
> reserveCapacity(loadedStatistics.size())" to avoid having to grow the Vector
> multiple times during the loop.

Fixed. Alex added that I can use reserveInitialCapacity() and make use of uncheckedAppend() in the population of the vector.

> > Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:150
> > +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const String& primaryDomain, bool value)> handler)
> 
> I think this needs to be "std::function<void(const ... etc.)>&& handler"

Fixed.

> > Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:155
> > +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const Vector<String>& primaryDomains, bool value)> handler)
> 
> Ditto --> I think handler has to be passed by && to avoid a copy.

Fixed.

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:114
> > +    for (HashSet<String>::iterator iter = m_cookiePartitioningList.begin(); iter != m_cookiePartitioningList.end(); ++iter) {
> 
> Please use a modern C++ iterator here:
> 
> for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {
>     ...

Fixed.

> > Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.cpp:213
> > +    
> 
> Please undo this whitespace change.

Fixed.

Thanks, Brent!
Comment 7 John Wilander 2017-03-08 13:57:58 PST
Created attachment 303837 [details]
Patch
Comment 8 John Wilander 2017-03-08 13:59:15 PST
Added a platform check for the network process message in WebCookieManagerProxy::setCookieStoragePartitioningEnabled() since it's only available on Cocoa platforms.
Comment 9 John Wilander 2017-03-08 14:28:23 PST
Created attachment 303844 [details]
Patch
Comment 10 Alex Christensen 2017-03-08 15:53:35 PST
Comment on attachment 303844 [details]
Patch

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

> Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:151
> +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const String& primaryDomain, bool value)>&& handler)

Function, not std::function
Maybe in followup since this is done elsewhere in this code.

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:147
> +    for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {

This is going to be quite hot code if we call this for each network request.  Is there some guarantee that the set is going to remain small?  Do we really need to iterate the set twice, or at all?  I would be ok with adding a hash lookup, but iterating a container with unlimited size will slow down browsing a lot.

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:152
> +    for (HashSet<String>::iterator iter = m_cookiePartitioningList.begin(); iter != m_cookiePartitioningList.end(); ++iter) {

Use c++11 for loops:
for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {

Also, m_cookiePartitioningList has a bad name.  I think it should be something with "hosts" in it.

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:164
> +void NetworkStorageSession::shouldPartitionCookiesForHost(const String& host, bool value)

setShouldPartitionCookiesForHost

> Source/WebKit2/NetworkProcess/NetworkProcess.messages.in:83
> +    ShouldPartitionCookiesForTopPrivatelyOwnedDomain(String topPrivatelyOwnedDomain, bool value)
> +    ShouldPartitionCookiesForTopPrivatelyOwnedDomains(Vector<String> topPrivatelyOwnedDomains, bool value)

Do we need both of these? Can't we just send a vector of length one for the first option?

> Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.h:66
> +    void registerSharedResourceLoadObserver(std::function<void(const String& primaryDomain, bool value)>&& shouldPartitionCookiesForDomainHandler, std::function<void(const Vector<String>& primaryDomain, bool value)>&& shouldPartitionCookiesForDomainsHandler);

Yeah, let's not add so many uses of std::function unless they really need to be copyable.

> Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp:1054
> +    for (auto& processPool : processPools())

This iterates just the process pools associated with this WebsiteDataStore, right?
Comment 11 John Wilander 2017-03-08 17:02:04 PST
Created attachment 303868 [details]
Patch
Comment 12 John Wilander 2017-03-08 17:09:11 PST
(In reply to comment #10)
> Comment on attachment 303844 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=303844&action=review
> 
> > Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:151
> > +void ResourceLoadStatisticsStore::setShouldPartitionCookiesCallback(std::function<void(const String& primaryDomain, bool value)>&& handler)
> 
> Function, not std::function
> Maybe in followup since this is done elsewhere in this code.

Will do in a follow-up. We have a couple as you noted.

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:147
> > +    for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {
> 
> This is going to be quite hot code if we call this for each network request.
> Is there some guarantee that the set is going to remain small?  Do we really
> need to iterate the set twice, or at all?  I would be ok with adding a hash
> lookup, but iterating a container with unlimited size will slow down
> browsing a lot.

The double iteration was a mistake. Thanks for spotting it. After a discussion I have now changed to calculating the host's top privately controlled domain and doing a .contains() in the hash set. Should be more performant.

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:152
> > +    for (HashSet<String>::iterator iter = m_cookiePartitioningList.begin(); iter != m_cookiePartitioningList.end(); ++iter) {
> 
> Use c++11 for loops:
> for (auto& topPrivatelyOwnedDomain : m_cookiePartitioningList) {

This loop is now gone.

> Also, m_cookiePartitioningList has a bad name.  I think it should be
> something with "hosts" in it.

Fixed.

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:164
> > +void NetworkStorageSession::shouldPartitionCookiesForHost(const String& host, bool value)
> 
> setShouldPartitionCookiesForHost
> 
> > Source/WebKit2/NetworkProcess/NetworkProcess.messages.in:83
> > +    ShouldPartitionCookiesForTopPrivatelyOwnedDomain(String topPrivatelyOwnedDomain, bool value)
> > +    ShouldPartitionCookiesForTopPrivatelyOwnedDomains(Vector<String> topPrivatelyOwnedDomains, bool value)
> 
> Do we need both of these? Can't we just send a vector of length one for the
> first option?

I did it that way to optimize the common case which is one domain at a time. After a discussion I now have just one message type.

> > Source/WebKit2/UIProcess/WebResourceLoadStatisticsStore.h:66
> > +    void registerSharedResourceLoadObserver(std::function<void(const String& primaryDomain, bool value)>&& shouldPartitionCookiesForDomainHandler, std::function<void(const Vector<String>& primaryDomain, bool value)>&& shouldPartitionCookiesForDomainsHandler);
> 
> Yeah, let's not add so many uses of std::function unless they really need to
> be copyable.

Yup. Commented above.

> > Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp:1054
> > +    for (auto& processPool : processPools())
> 
> This iterates just the process pools associated with this WebsiteDataStore,
> right?

AFAIK, yes.
Comment 13 Alex Christensen 2017-03-08 17:40:56 PST
Comment on attachment 303868 [details]
Patch

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

> Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:167
> +    Vector<String> domainVector;
> +    domainVector.reserveInitialCapacity(1);
> +    domainVector.uncheckedAppend(primaryDomain);
> +    fireShouldPartitionCookiesHandler(domainVector, value);

reserveInitialCapacity isn't worth it if the capacity is less that 16 or something.  Just do an append.  Or even better, use an initializer list:
fireShouldPartitionCookiesHandler({primaryDomain}, value);

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:152
> +    auto domain = topPrivatelyControlledDomain(host);
> +    if (domain.isEmpty())
> +        domain = host;

Is this for localhost?  I'm not sure this is a good idea.  Maybe just return false if there's no top privately controlled domain.

> Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:155
> +    if (m_topPrivatelyControlledDomainsForCookiePartitioning.contains(domain))
> +        return true;

just return the result of contains.

> Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp:1226
> +        [this] (const Vector<String>& topPrivatelyOwnedDomains, bool value) {

When the WebsiteDataStore gets destroyed, it tells the ResourceLoadStatistics that it is going to be destroyed, so this captured this pointer won't be used again, right?

> Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp:1354
> +    for (size_t i = 0; i < keys.size(); ++i) {

This could be a c++11-style for loop, too
Comment 14 John Wilander 2017-03-08 18:15:52 PST
Created attachment 303880 [details]
Patch
Comment 15 John Wilander 2017-03-08 18:23:55 PST
(In reply to comment #13)
> Comment on attachment 303868 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=303868&action=review
> 
> > Source/WebCore/loader/ResourceLoadStatisticsStore.cpp:167
> > +    Vector<String> domainVector;
> > +    domainVector.reserveInitialCapacity(1);
> > +    domainVector.uncheckedAppend(primaryDomain);
> > +    fireShouldPartitionCookiesHandler(domainVector, value);
> 
> reserveInitialCapacity isn't worth it if the capacity is less that 16 or
> something.  Just do an append.  Or even better, use an initializer list:
> fireShouldPartitionCookiesHandler({primaryDomain}, value);

Doing it with an initializer list confuses at least Xcode that the function will call itself. I did it with an explicit vector.

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:152
> > +    auto domain = topPrivatelyControlledDomain(host);
> > +    if (domain.isEmpty())
> > +        domain = host;
> 
> Is this for localhost?  I'm not sure this is a good idea.  Maybe just return
> false if there's no top privately controlled domain.

I can't test partitioning if localhost gets the empty partition. Instead filed follow-up bug for fixing this in ResourceRequestBase::partitionName() too: https://bugs.webkit.org/show_bug.cgi?id=169399

> > Source/WebCore/platform/network/cf/NetworkStorageSessionCFNet.cpp:155
> > +    if (m_topPrivatelyControlledDomainsForCookiePartitioning.contains(domain))
> > +        return true;
> 
> just return the result of contains.

Fixed.

> > Source/WebKit2/UIProcess/WebsiteData/WebsiteDataStore.cpp:1226
> > +        [this] (const Vector<String>& topPrivatelyOwnedDomains, bool value) {
> 
> When the WebsiteDataStore gets destroyed, it tells the
> ResourceLoadStatistics that it is going to be destroyed, so this captured
> this pointer won't be used again, right?

I cannot see any other use of it, no.

> > Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp:1354
> > +    for (size_t i = 0; i < keys.size(); ++i) {
> 
> This could be a c++11-style for loop, too

Can it? I'm using the index i for two different vectors, keys and values.

Thanks for the review and all the enhancements, Alex!
Comment 16 WebKit Commit Bot 2017-03-08 20:02:18 PST
Comment on attachment 303880 [details]
Patch

Clearing flags on attachment: 303880

Committed r213623: <http://trac.webkit.org/changeset/213623>
Comment 17 WebKit Commit Bot 2017-03-08 20:02:24 PST
All reviewed patches have been landed.  Closing bug.