Consider not doing sync IPC for document.cookie getter/setter

NEW
Assigned to

Status

()

Core
Networking: Cookies
5 months ago
2 days ago

People

(Reporter: Ehsan, Assigned: Amy)

Tracking

(Blocks: 3 bugs)

unspecified
Points:
---
Dependency tree / graph

Firefox Tracking Flags

(platform-rel +)

Details

(Whiteboard: [necko-active][platform-rel-Linkedin][qf:p1][necko-quantum])

Attachments

(8 attachments, 77 obsolete attachments)

1.85 MB, application/x-bzip2
Details
32.49 KB, patch
jdm
: review+
Details | Diff | Splinter Review
34.49 KB, patch
jdm
: review-
Details | Diff | Splinter Review
37.67 KB, patch
jdm
: review+
Details | Diff | Splinter Review
8.90 KB, patch
jdm
: review+
Details | Diff | Splinter Review
18.70 KB, patch
Details | Diff | Splinter Review
37.70 KB, patch
jdm
: review-
Details | Diff | Splinter Review
18.70 KB, patch
jdm
: review-
Details | Diff | Splinter Review
See this profile <https://clptr.io/2jkLOAZ> for example where we spend ~3.5 *seconds* on Msg_GetCookieString sync IPC messages.

Right now we delegate to the parent process to ensure that there is only one source of truth for cookie information, but this is extremely costly.  Perhaps it's time to rethink this decision?

My proposal is to replace these sync IPC messages with async messages from the parent to child letting it know when a cookie changes, and maintain a hashtable of cookies in the child process (similar to the one we maintain in the parent process) and use that to implement the document.cookie getter.

For the document.cookie setter, I suggest that we should update the hashtable immediately (so that |document.cookie = "foo=bar"; document.cookie == "foo=bar"| would hold true) and send an async message to the parent process to let it update the real cookie value.  This update will again asynchronously update the child process' notion of the cookie value.  This will correctly deal with the case where a cookie is set through an HTTP header in the parent, scheduling an async IPC message to update the child, and then the child immediately getting a call to document.cookie setter modifying the same cookie while the said IPC message is in flight.

This effectively means that the child process' notion of the current cookie value will lag behind the parent's.  I think the only case where this matters in practice is the case I mentioned at the end of the last paragraph, and even there the behavior is already racy in the sense that the said HTTP header can be processed either before or after the document.cookie setter being called.

Needinfoing jdm as the cookie peer and Patrick and Jason as Necko peers for feedback on this plan.
Flags: needinfo?(mcmanus)
Flags: needinfo?(josh)
Flags: needinfo?(jduell.mcbugs)
(In reply to :Ehsan Akhgari from comment #0)
> See this profile <https://clptr.io/2jkLOAZ> for example where we spend ~3.5
> *seconds* on Msg_GetCookieString sync IPC messages.

based on your description I couldn't bear to look at the profile.

absent content setter (which you address), but present an http setter on the document would document.cookie be racy or do we just apply those in the child-table along with OnStart?
Flags: needinfo?(mcmanus)
(In reply to Patrick McManus [:mcmanus] from comment #1)
> absent content setter (which you address), but present an http setter on the
> document would document.cookie be racy or do we just apply those in the
> child-table along with OnStart?

Yes.  More specifically, consider this:

1. We load doc A which sets cookie foo=bar.
2. We load doc B which sets cookie foo=baz.
3. We read document.cookie from doc A.

At this point, if the Set-Cookie header from step 2 is processed, we return "foo=baz", otherwise we return "foo=bar".  This is an existing race which is present even in non-e10s mode.

With this proposal, the race still exists, except that we'll return "foo=bar" until the async job that updates the child's hashtable is processed (as opposed to the Set-Cookie header in the parent), so the timings change but the fact that the API is racy doesn't.
> My proposal is to replace these sync IPC messages with async messages from the
> parent to child letting it know when a cookie changes, and maintain a
> hashtable of cookies in the child process (similar to the one we maintain in
> the parent process) and use that to implement the document.cookie getter.

An alternative architecture could be to keep the cookie hashtable in IPDL shared memory (but that would require that our shmem implementation have shared memory mutexes--I don't know if it does).  This would take up less memory, use less IPDL traffic, and might be a little less racy (thought that seems mostly moot--we've got some innate raciness as described in previous comments).  OTOH it might be harder to program and might have tricky edge cases (startup, shutdown).

I'm trying to think of whether there's a security advantage to be had here. Ehsan's architecture has the bonus that we could only ship a child the cookies "it needs to know about".  I'm not sure in practice how hard it would be for the parent to keep track of that, though (if we keep a list of all the domains that a child has loaded resources from, we'd know all the cookies it ought to be able to see, right?).  I don't know if this is practical, but not allowing a compromised child to read the entire cookie database would be a very nice feature.
Flags: needinfo?(jduell.mcbugs)
(In reply to Jason Duell [:jduell] (needinfo me) from comment #3)
> > My proposal is to replace these sync IPC messages with async messages from the
> > parent to child letting it know when a cookie changes, and maintain a
> > hashtable of cookies in the child process (similar to the one we maintain in
> > the parent process) and use that to implement the document.cookie getter.
> 
> An alternative architecture could be to keep the cookie hashtable in IPDL
> shared memory (but that would require that our shmem implementation have
> shared memory mutexes--I don't know if it does).

We have CrossProcessMutex for that purpose.

But the bigger problem with storing the hashtable in IPDL shared memory is that we don't have an existing way of doing this.  nsTHashtable doesn't support a user provided allocator, and a proper cross-process hashtable would want to deal with memory usage growth and whatnot.

> This would take up less
> memory, use less IPDL traffic, and might be a little less racy (thought that
> seems mostly moot--we've got some innate raciness as described in previous
> comments).  OTOH it might be harder to program and might have tricky edge
> cases (startup, shutdown).
> 
> I'm trying to think of whether there's a security advantage to be had here.
> Ehsan's architecture has the bonus that we could only ship a child the
> cookies "it needs to know about".  I'm not sure in practice how hard it
> would be for the parent to keep track of that, though (if we keep a list of
> all the domains that a child has loaded resources from, we'd know all the
> cookies it ought to be able to see, right?).  I don't know if this is
> practical, but not allowing a compromised child to read the entire cookie
> database would be a very nice feature.

In theory all we need to provide to the child process are the cookies that can be retrieved through document.cookie, which are the ones from the origins of the documents that exist in the child process.  So in theory for example if we had a way to tag a channel as the channel from which a document is going to be loaded, we could look up the cookies belonging to its final channel URI (considering the path) and only send down those...

Having been intrigued by this idea, I looked up the consumers of this API in the content process, and currently the only consumers are document.cookie and the NPNURLVCookie NPAPI.  Unfortunately the NPAPI allows you to retrieve arbitrary cookies by default, it seems.  As part of this, we can restrict it only to cookies that the document can access, which would be a privacy win as well.  Chris, should we check with Adobe about adding this restriction?

On the parent side, we need to keep track of the URIs of the active documents, and when dispatching cookie change notifications to the child, check to see if the base domain and path from the URL match one belonging to our actor.  We can store the base domain + path pair in a hashtable to ensure we don't regress the performance of cookie updates.

I'm really liking this plan the more I think about it.
Flags: needinfo?(cpeterson)
I went ahead and checked to see what Chrome does for document.cookie.  They also do a sync IPC call to another process.  This gives us an opportunity to be faster than them.  :-)
>  if we had a way to tag a channel as the channel from which a document 
> is going to be loaded

Channels with the LOAD_DOCUMENT_URI load flag === top-level document loads.
Sounds like we could skip sending http-only cookies to the child (and https-only ones if a domain was only loaded with http://).
(In reply to :Ehsan Akhgari from comment #4)
> Having been intrigued by this idea, I looked up the consumers of this API in
> the content process, and currently the only consumers are document.cookie
> and the NPNURLVCookie NPAPI.  Unfortunately the NPAPI allows you to retrieve
> arbitrary cookies by default, it seems.  As part of this, we can restrict it
> only to cookies that the document can access, which would be a privacy win
> as well.  Chris, should we check with Adobe about adding this restriction?

Flash has no ActionScript API to get or set browser cookies, other than making external calls to JavaScript. IIRC, the Flash plugin only uses NPNURLVCookie to get browser cookies that it will add to some HTTP requests it makes "behind the back" of NPN_GetURL and NPN_PostURL. So NPNURLVCookie should return any cookies we would expect to be in an HTTP request, including http-only cookies.

I can ask Adobe for more information. If returning all cookies complicates your async cookie IPC, we might have some flexibility in what you return.
Flags: needinfo?(cpeterson)

Comment 9

5 months ago
I really like the idea proposed in comment 4! I'm fine with the idea from comment 0, too; I could swear that was the original design of e10s cookies and we ended up moving to the current model to reduce complexity, but I can't find evidence of it in the history of nsCookieService.cpp.
Flags: needinfo?(josh)
Whiteboard: [necko-would-take]
(In reply to Chris Peterson [:cpeterson] from comment #8)
> (In reply to :Ehsan Akhgari from comment #4)
> > Having been intrigued by this idea, I looked up the consumers of this API in
> > the content process, and currently the only consumers are document.cookie
> > and the NPNURLVCookie NPAPI.  Unfortunately the NPAPI allows you to retrieve
> > arbitrary cookies by default, it seems.  As part of this, we can restrict it
> > only to cookies that the document can access, which would be a privacy win
> > as well.  Chris, should we check with Adobe about adding this restriction?
> 
> Flash has no ActionScript API to get or set browser cookies, other than
> making external calls to JavaScript. IIRC, the Flash plugin only uses
> NPNURLVCookie to get browser cookies that it will add to some HTTP requests
> it makes "behind the back" of NPN_GetURL and NPN_PostURL. So NPNURLVCookie
> should return any cookies we would expect to be in an HTTP request,
> including http-only cookies.

Ugh.  :(  And I assume the URL here can be any random URL?

> I can ask Adobe for more information. If returning all cookies complicates
> your async cookie IPC, we might have some flexibility in what you return.

It doesn't complicate it per se, it just means that we would have to provide all of the cookies we know about to the child process, which renders the privacy benefit suggested in comment 3 pointless (because if the child process is allowed to read all cookies for Flash, there's no point in locking down what we for document.cookie.

It would be really helpful if you can find out exactly what URLs they expect to be able to retrieve cookies for, and if they're OK with restricting it for example only to the cookies that the document.cookie getter for the containing document can see.
Flags: needinfo?(cpeterson)
(In reply to :Ehsan Akhgari from comment #10)
> It would be really helpful if you can find out exactly what URLs they expect
> to be able to retrieve cookies for, and if they're OK with restricting it
> for example only to the cookies that the document.cookie getter for the
> containing document can see.

I emailed Adobe about Flash's use of browser cookies.
Flags: needinfo?(cpeterson)
Depends on: 1334509
(In reply to Chris Peterson [:cpeterson] from comment #11)
> (In reply to :Ehsan Akhgari from comment #10)
> > It would be really helpful if you can find out exactly what URLs they expect
> > to be able to retrieve cookies for, and if they're OK with restricting it
> > for example only to the cookies that the document.cookie getter for the
> > containing document can see.
> 
> I emailed Adobe about Flash's use of browser cookies.

Adobe confirmed that they're not using this feature, so I am removing it in bug 1334509 so that we can proceed here with the proposal in comment 4.  \o/

I'm planning to post a more concrete implementation plan here soon.
Flags: needinfo?(ehsan)
LinkedIn saw this come up during page load. They use cookies to read their CSRF token 60+ times while rendering their single page app. LI is also looking at addressing this on their site, as the token doesn't change for 30min.
Depends on: 1339129
Here is a rough list of steps that I think we should take to fix this bug:

1. Come up with a way to communicate to Necko in the parent process that this channel load needs to send down cookies to the child process.  We can use a load flag similar to LOAD_DOCUMENT_URI, but we actually only need a subset of such loads.  Let's call the new flag LOAD_DOCUMENT_NEEDS_COOKIES.  We only set it if:
  a) LOAD_DOCUMENT_URI is being set.
  b) the document isn't sandboxed (to match the check in <http://searchfox.org/mozilla-central/rev/d3307f19d5dac31d7d36fc206b00b686de82eee4/dom/html/nsHTMLDocument.cpp#1259>.)

2. On the parent process side, before sending the OnStartRequest notification we need to send the matching cookies if needed.  A good place for doing this would be HTTPChannelParent::OnStartRequest().  What we need to do is to check if the load flags include both LOAD_DOCUMENT_URI and LOAD_DOCUMENT_NEEDS_COOKIES, and then look at the final channel URI and send down all of the cookies that match these criteria:
  a) If the cookie's host matches the final channel URI's using DomainMatches(),
  b) If the cookie's secure flag matches whether the final channel URI is https,
  c) If the cookie is not HTTP-only (since bug 1339129 is removing the access to HTTP-only cookies to the content process),
  d) If the cookie's path matches that of the final channel URI using PathMatches(),
  e) If the cookie has not expired yet.

These criteria match the ones used in this loop: <http://searchfox.org/mozilla-central/rev/d3307f19d5dac31d7d36fc206b00b686de82eee4/netwerk/cookie/nsCookieService.cpp#3275>.  This should guarantee that all cookies accessible by document.cookie are sent to the child process.

3. On the child process side, make CookieServiceChild maintain a cookies hash table.  This will probably be in the form of the following data structures:
  a) A hashtable similar to nsCookieService::hostTable.  This is the cookie store that the content process uses to implement CookieServiceChild::GetCookieString().  Note that nsCookieService uses a separate hashtable for storing cookies for private browsing windows, so I suggest we do the same here as well.  Everywhere that I talk about accessing any of these data structures, you should access the correct one depending on the privacy status of the document.
  b) A list of all of the document URIs that the child process knows about.  I think this can be a map of the host name to a struct containing an array of tuples of full URI path names, isSecure flags and refcounts.  Let's call this map DocumentCookieMap.

When receiving a cookie from step 2 above, do the following:
  a) Add the cookies to the hostTable maintained in the child process side.
  b) Add an entry to the map in (b) above for the host name from the document URI if one does not exist.
  c) Search the list of path names, isSecure flag and refcount tuple.  If one exists for the document URI, increment the refcount. Otherwise insert an entry with a refcount of 1.  Let's call this pair DocumentCookieInfo, and the list DocumentCookieInfoList.  Note that the isSecure flag is useful for checking secure-only cookies.

4. To maintain these data structures when a document is destroyed, in ~Document() notify the CookieServiceChild object that the document is going away.  When that happens, search DocumentCookieInfoList and decrement the refcount if it's greater than 1, or delete the entry from the list otherwise.  When deleting the entry, iterate over the cookies in hostTable matching our domain and delete the ones that aren't accessible to any of the existing paths.  When removing the last entry from this list, delete all cookies for the domain and remove the hostTable entry.

Be careful that the document URI might have changed since step 3 if the page uses things such as history.pushState(), so you should look at mOriginalURI as well.

5. The parent process also needs to know which content processes care about which cookies to be able to notify the right process when such a cookie changes.  The information that the parent process needs to know is actually the same as what DocumentCookieMap keeps track of, so we can use that data structure on the parent side as well.  Each CookieServiceParent object needs to have one of these maps.  Every time we send cookies in step 2, we need to also update this data structure in the parent side similar to the child side.  When a document goes away in step 4, we need to send an asynchronous notification to the parent process to update the DocumentCookieMap there similarly as well.

6. When a cookie changes on the parent process side, we need to notify the child.  For this purpose, I suggest handling the "cookie-changed" and "private-cookie-changed" notifications in CookieServiceParent, and for each cookie consult our DocumentCookieMap to see whether the cookie change can be visible to the child process, and in that case notify the child process about the cookie change asynchronously.  Note that due to the delay between things such as the child process' Document destructor notifying the parent process, on the child side you should always be prepared to deal with cookie change notifications that can arrive without matching anything in our DocumentCookieMap.  Such notifications should be ignored.  Also note that before sending the notifications you should again apply the criteria in step 2 (for example, you should never send any notification about HTTP-only cookies.)

This should give us a fully working document.cookie getter in the new world.

7. The current setter is asynchronous already so there isn't much to be done for it.  However we must ensure that a cookie set via the setter is immediately readable from the getter before all of the asynchronous IPC is finished.  So we need to parse the string and update our hostTable on the child process side.

Since this is a complicated change that can potentially break things, it would be nice to hide it all behind a new preference.  The preference should be able to revert us to the existing code path.

Amy, I was thinking perhaps you may be interested to take on this project?  This is a super nice performance win that we're tracking for Quantum Flow.

Please let me know if the plan above makes sense and if you have questions.  I have tried to note all of the complexities that you may want to watch out for, so I hope this will be relatively straightforward...  Thanks!
Flags: needinfo?(ehsan) → needinfo?(amchung)
Blocks: 1287730
(Assignee)

Comment 15

4 months ago
Hi Ehsan,
I will take a look at this bug and spend some time studying your suggestions.

Thanks again for your help!
Flags: needinfo?(amchung)
(Assignee)

Updated

4 months ago
Assignee: nobody → amchung
(Assignee)

Updated

4 months ago
Whiteboard: [necko-would-take] → [necko-active]
The plan in comment 14 looks good to me.  One nit: we're almost out of channel.loadFlag bits, so we should avoid using one.  I think it should be easy enough to add some new channel.sendE10sCookies() method or whatever.  Anything besides a loadFlag.
(In reply to Jason Duell [:jduell] (needinfo me) from comment #16)
> The plan in comment 14 looks good to me.  One nit: we're almost out of
> channel.loadFlag bits, so we should avoid using one.  I think it should be
> easy enough to add some new channel.sendE10sCookies() method or whatever. 
> Anything besides a loadFlag.

Hmm since this is restricted to HTTP channels, how about a boolean attribute on nsIHttpChannel that is backed by a bool in HttpChannelBase?
That sounds fine to me.
platform-rel: --- → +
Whiteboard: [necko-active] → [necko-active][platform-rel-Linkedin]

Updated

4 months ago
Duplicate of this bug: 1306928
(In reply to :Ehsan Akhgari from comment #0)
> See this profile <https://clptr.io/2jkLOAZ> for example where we spend ~3.5
> *seconds* on Msg_GetCookieString sync IPC messages.

For the record, that profile shows 2.5 seconds on Msg_GetCookieString (not saying that's ok!), and 1 second on Msg_GetGraphicsFeatureStatus for setting up WebGL. Is there a bug for the latter?
(In reply to Steve Fink [:sfink] [:s:] from comment #20)
> (In reply to :Ehsan Akhgari from comment #0)
> > See this profile <https://clptr.io/2jkLOAZ> for example where we spend ~3.5
> > *seconds* on Msg_GetCookieString sync IPC messages.
> 
> For the record, that profile shows 2.5 seconds on Msg_GetCookieString (not
> saying that's ok!), and 1 second on Msg_GetGraphicsFeatureStatus for setting
> up WebGL. Is there a bug for the latter?

A quick bugzilla search turns nothing up. Ehsan, maybe I didn't search for the right thing?
Flags: needinfo?(ehsan)
(In reply to Steve Fink [:sfink] [:s:] from comment #20)
> (In reply to :Ehsan Akhgari from comment #0)
> > See this profile <https://clptr.io/2jkLOAZ> for example where we spend ~3.5
> > *seconds* on Msg_GetCookieString sync IPC messages.
> 
> For the record, that profile shows 2.5 seconds on Msg_GetCookieString (not
> saying that's ok!), and 1 second on Msg_GetGraphicsFeatureStatus for setting
> up WebGL. Is there a bug for the latter?

I have fixed that in bug 1331676.  (I usually try to file one bug per issue, so I end up filing several bugs for a single bug often times.)
Flags: needinfo?(ehsan)
(Assignee)

Comment 23

4 months ago
Hi Ehsan,
I have some questions from your suggestions as below:
1. What’s the timing to set the LOAD_DOCUMENT_NEEDS_COOKIES?
2. Do the matching cookies from nsHttpRequestHead? If the matching cookies exist, I have to set the load flag LOAD_DOCUMENT_NEEDS_COOKIES?

And I organized the steps of implementation as below:
1. Create LOAD_DOCUMENT_NEEDS_COOKIES In nsIChannel.idl.
2. Set LOAD_DOCUMENT_NEEDS_COOKIES to load flags  when getting the matching cookies nsHttpRequestHead 
   If load flags include LOAD_DOCUMENT_NEEDS_COOKIES & LOAD_DOCUMENT_URI, have to send the matching cookie to child process and init hash talbe in CookieServiceParent.cpp.
   a. Create SetMatchingCookies() in PHttpChannel.ipdl.
   b. Create RecvSetMatchingCookies() in HttpChannelChild.cpp.
3. Init the hash table in CookieServiceChild.cpp.
4. Create the update, modify, destroy hash table function  in CookieServiceChild.cpp
5. Same as CookieServiceParent.cpp.

Would you help me to answer my question and confirm my steps of implementation?
Flags: needinfo?(ehsan)
(In reply to Amy Chung [:Amy] from comment #23)
> Hi Ehsan,
> I have some questions from your suggestions as below:
> 1. What’s the timing to set the LOAD_DOCUMENT_NEEDS_COOKIES?

If you mean when you should set it, I think we need it in places where we current set LOAD_DOCUMENT_URI for loading documents, which are:

<http://searchfox.org/mozilla-central/rev/b1044cf7c2000c3e75e8181e893236a940c8b6d2/docshell/base/nsDocShell.cpp#9233>
<http://searchfox.org/mozilla-central/rev/b1044cf7c2000c3e75e8181e893236a940c8b6d2/docshell/base/nsDocShell.cpp#11425>
<http://searchfox.org/mozilla-central/rev/b1044cf7c2000c3e75e8181e893236a940c8b6d2/dom/html/nsHTMLDocument.cpp#2346>

> 2. Do the matching cookies from nsHttpRequestHead? If the matching cookies
> exist, I have to set the load flag LOAD_DOCUMENT_NEEDS_COOKIES?

No, the idea here is that we want to set LOAD_DOCUMENT_NEEDS_COOKIES on the channels which should trigger Necko to send the cookies it has stored for the host for.  This means that we don't change how the Set-Cookie header is processed, just when we're about to send the OnStartRequest notification, we check to see if the LOAD_DOCUMENT_NEEDS_COOKIES load flag has been set, and if yes, we figure out which cookies the document can possibly see and send them to the content process before OnStartRequest is sent, so in the content process side as soon as we receive an OnStartRequest for a document that is being loaded, we should have its cookies cached and ready to use.

> And I organized the steps of implementation as below:
> 1. Create LOAD_DOCUMENT_NEEDS_COOKIES In nsIChannel.idl.
> 2. Set LOAD_DOCUMENT_NEEDS_COOKIES to load flags  when getting the matching
> cookies nsHttpRequestHead 

This part isn't accurate, please see my explanation above.

>    If load flags include LOAD_DOCUMENT_NEEDS_COOKIES & LOAD_DOCUMENT_URI,
> have to send the matching cookie to child process and init hash talbe in
> CookieServiceParent.cpp.
>    a. Create SetMatchingCookies() in PHttpChannel.ipdl.
>    b. Create RecvSetMatchingCookies() in HttpChannelChild.cpp.
> 3. Init the hash table in CookieServiceChild.cpp.
> 4. Create the update, modify, destroy hash table function  in
> CookieServiceChild.cpp
> 5. Same as CookieServiceParent.cpp.
> 
> Would you help me to answer my question and confirm my steps of
> implementation?

The rest of the steps sound good to me.  Thank you!
Flags: needinfo?(ehsan)
(Assignee)

Comment 25

4 months ago
Hi Ehsan,
I have some questions from your suggestions as below:
1. Does the method to figure out which cookies the document can possibly see as below:
   i.   Call GetCookie(URI, chan, getter_Copies(cookie)) and confirm the string cookie doesn't null.
   ii.  Send the cookie to HttpChannelChild.cpp by SendSetMatchingCookies().
   iii. Update the cookie to hash table by CookieServiceChild::UpdateDocumentHashTable().
2. The data structure of hash table as below:
   i.   Create struct DocumentCookieMap() in nsCookieService.cpp
        a. full URI path names, isSecure flags and refcounts
   ii.  Create a class DocumentCookieKey and DocumentCookieInfo() in nsCookieService.cpp
        a. Create DocumentCookieMap() in DocumentCookieKey. 
   iii. Create a hashtable in CookieServiceChild.cpp and CookieServerParent.cpp
        a. nsTHashTable<DocumentCookieInfo> DocumentHashTable;
 
Would you help me to answer my questions?
Flags: needinfo?(ehsan)
Hi Amy,

(In reply to Amy Chung [:Amy] from comment #25)
> Hi Ehsan,
> I have some questions from your suggestions as below:
> 1. Does the method to figure out which cookies the document can possibly see
> as below:
>    i.   Call GetCookie(URI, chan, getter_Copies(cookie)) and confirm the
> string cookie doesn't null.
>    ii.  Send the cookie to HttpChannelChild.cpp by SendSetMatchingCookies().

Yes, so far I agree.  This is step 2 of comment 14.

>    iii. Update the cookie to hash table by
> CookieServiceChild::UpdateDocumentHashTable().

There's more to do here inisde RcvSetMatchingCookies, according to step 3 of comment 14, in addition to the above, you should do (b) and (c) under the "When receiving a cookie from..." section.

> 2. The data structure of hash table as below:
>    i.   Create struct DocumentCookieMap() in nsCookieService.cpp
>         a. full URI path names, isSecure flags and refcounts

Note that this is a hashmap where the key is the host name and the value is an array of tuples of (fullURIPathName, isSecure, refCount).  Using real class names, this will probably be nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList>.  The reason is that you when a cookie changes for example, you want to quickly look up all of the tuples corresponding to the cookies for that host.  Also, this data structure needs to live on CookieServiceParent, because it tracks the list of the cookies for each content process, so each actor in the parent process needs to have its own map.

>    ii.  Create a class DocumentCookieKey and DocumentCookieInfo() in
> nsCookieService.cpp
>         a. Create DocumentCookieMap() in DocumentCookieKey. 

See above.

>    iii. Create a hashtable in CookieServiceChild.cpp and
> CookieServerParent.cpp
>         a. nsTHashTable<DocumentCookieInfo> DocumentHashTable;

Perhaps we are using different names for different things.  This is basically the data structures I had in my mind in comment 14.  It's easier to talk in terms of code.  :-)

struct DocumentCookieInfo {
  nsCString mPathName;
  bool mIsSecure;
  nsrefcnt mRefCnt; // (perhaps we should use a different name? since this is the name we use for XPCOM reference counting…)
};

typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;

typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;

class CookieServiceParent… {
  // cookies that have been sent to the content process for this actor
  DocumentCookieMap mContentProcessCookies;
};

class CookieServiceChild… {
  // cookies that our parent process has told us about
  DocumentCookieMap mKnownCookies;
};

Hope this helps!
Flags: needinfo?(ehsan)
(BTW the code above is a sample, it probably has mistakes and typos. :-)
Hey,

I'm working on a similar project for the permission manager in bug 1337056. In that bug I ran into the problem that documents which are loaded within <object> tags don't have the LOAD_DOCUMENT_URI flag set on them when they are being loaded, as whether or not they are a document depends on the MIME type of the response.

I work around this in parts 7/8/9 of bug 1337056. I imagine that once that lands you'll be able to use code very similar to what I wrote in part 9 for this bug as well.

Hopefully that's useful, and helps you not run into this problem as well :).
Whiteboard: [necko-active][platform-rel-Linkedin] → [necko-active][platform-rel-Linkedin][qf:p1]
(Assignee)

Comment 29

4 months ago
(In reply to Michael Layzell [:mystor] from comment #28)
> Hey,
> 
> I'm working on a similar project for the permission manager in bug 1337056.
> In that bug I ran into the problem that documents which are loaded within
> <object> tags don't have the LOAD_DOCUMENT_URI flag set on them when they
> are being loaded, as whether or not they are a document depends on the MIME
> type of the response.
> 
> I work around this in parts 7/8/9 of bug 1337056. I imagine that once that
> lands you'll be able to use code very similar to what I wrote in part 9 for
> this bug as well.
> 
> Hopefully that's useful, and helps you not run into this problem as well :).

Hi Michael,
Thanks for your suggestions, that's really helpful to me.
Duplicate of this bug: 1194761
(Assignee)

Comment 31

3 months ago
Created attachment 8847262 [details] [diff] [review]
implementation

Hi Ehsan,
I have finished part of the function in this bug as below:
1. Created a new loadflag "LOAD_DOCUMENT_NEEDS_COOKIES".
2. Also set the "LOAD_DOCUMENT_NEEDS_COOKIES" when loadflag set to "LOAD_DOCUMENT_URI".
3. Defined the structure "DocumentCookieInfo", "DocumentCookieInfoList" and "DocuemtnCookieMap"
4. Confirmed the cookies which have to set to child process.
   i. Finished the ipc function "SetMatchingCookies()"
   ii. Created some functions to confirm which cookie have to set on nsCookiseService.cpp
5. Finished the update hash table function on child process and parent process.

[Question] 
1. In update hash table function, mRefDocCnt have to increase the length of documentCookieInfoList or once?
2. Would I create the variable "DocumentCookieMap mContentProcessCookies" in nsCookieService.cpp and not in CookieServiceParent.cpp? 

[Not finish]
1. Destroy function
   i.  Create it on nsICookieService.idl.
   ii. Implement it on nsCookieService.cpp and CookieServiceChild.cpp.
   iii.Call it on ~nsDocShell() and ~nsHTMLDocument().

2. Modify function
   i.  Create it on nsICookieService.idl.
   ii. Implement it on nsCookieService.cpp and CookieServiceChild.cpp.
   iii. Call it on nsCookieService::NotifyChanged().

Would you give me some suggestions?
Thanks!
Attachment #8847262 - Flags: feedback?(ehsan)
Comment on attachment 8847262 [details] [diff] [review]
implementation

Thanks so much, Amy!  I have asked Josh to provide feedback instead, since I have proposed the implementation plan, I'd like to get another pair of eyes looking over the work being done here.  :-)
Attachment #8847262 - Flags: feedback?(ehsan) → feedback?(josh)
(Assignee)

Comment 33

3 months ago
Created attachment 8849038 [details] [diff] [review]
implementation

Hi Josh,
I have finished all functions as below:
[Created a new loadflag and new ipc set function]
1. Added a new loadflag LOAD_DOCUMENT_NEEDS_COOKIES on nsIChannel.idl.
2. Also set the "LOAD_DOCUMENT_NEEDS_COOKIES" when loadflag set to "LOAD_DOCUMENT_URI".
3. Created ipc function "setMatchingCookies".
4. When loadFlags include "LOAD_DOCUMENT_URI" and "LOAD_DOCUMENT_NEEDS_COOKIES", confirmed the cookies which got before the HttpChannelParent processed onStartRequest 
5. If the cookies meet the conditions from nsICookieService->GetCookieString(), call "SendSetMatchingCookie" to child process.
   And the conditions as below:
    a) If the cookie's host matches the final channel URI's using DomainMatches(),
   b) If the cookie's secure flag matches whether the final channel URI is https,
   c) If the cookie is not HTTP-only (since bug 1339129 is removing the access to HTTP-only cookies to the content process),
   d) If the cookie's path matches that of the final channel URI using PathMatches(),
   e) If the cookie has not expired yet.
   
[Created hash table]
1. Created struct DocumentCookieInfo , DocumentCookieInfoList and DocumentCookieMap.
2. Created hash table (DocumentCookieMap) on CookieServiceChild and nsCookieService.
3. Created the functions "updateCookieToTable" and "destrouCookieFromTable" which can process hash table on nsICookieService.idl.
4. Called nsICookieService->UpdateCookieToTable() when the situations as below:
   a) HttpChannelParent sent the matching cookies to HttpChannelChild.
   b) When new cookie set to db or the existing cookie be modified.
      i.  Called nsICookieService->UpdateCookieToTable() before called "NotifyChanged" on nsCookieService (Confirm !IsNeckoChild()).
      ii. Called nsICookieService->UpdateCookieToTable() when CookieServiceChild::Observe() got the new topics "cookie-doc-changed" and "private-doc-cookie-changed".
5. Called nsICookieService->DestroyCookieFromTable when called destructors ~docShell() and ~nsHTMLDocument().

[Question] 
1. In update hash table function, mRefDocCnt have to increase the length of documentCookieInfoList or once?
2. Would I create the variable "DocumentCookieMap mContentProcessCookies" in nsCookieService.cpp and not in CookieServiceParent.cpp (using IsNeckoChild() to distinguish between child process and parent process) ? 

Would you give me some suggestions, thanks!
Attachment #8847262 - Attachment is obsolete: true
Attachment #8847262 - Flags: feedback?(josh)
Attachment #8849038 - Flags: feedback?(josh)
(Assignee)

Comment 34

3 months ago
Hi Josh,
I tried to test the website: https://cswithandroid.withgoogle.com/course on gecko profiler, and confirmed the request header which included the cookie and the domain is "ssl.gstatic.com".
[Not imported the patch]
https://perf-html.io/public/ecff0f7083a45c5ee4dd6eeb2bab942387681264/calltree/?search=msg_get&thread=2

[imported the patch]
https://perf-html.io/public/6f29d65e6f28642a050aca8a82b7713c03114491/calltree/?search=msg_getCoo&thread=2

Would you gibe me some suggestions on the testing result?

Thanks!
Flags: needinfo?(josh)

Comment 35

3 months ago
The first profile does not appear to show any significant usage of document.cookie. Is that an unexpected result for that site?
Flags: needinfo?(josh)
(In reply to Amy Chung [:Amy] from comment #34)
> [imported the patch]
> https://perf-html.io/public/6f29d65e6f28642a050aca8a82b7713c03114491/
> calltree/?search=msg_getCoo&thread=2

This search is a little misleading because it finds the string msg_getCoo in the URL of a perf.html tab that was open while you took the profile. E.g. it includes callstacks that contain the frame "PresShell::Paint (https://perf-html.io/public/620068628011d815fa9ceb5348afedf23484dadf/calltree/?search=Msg_GetCookie&thread=2)".
If you search for msg_getCookieString instead then you'll see zero samples in the first content process and a few samples in the second content process: https://perfht.ml/2ne9sDf

(In reply to Josh Matthews [:jdm] from comment #35)
> The first profile does not appear to show any significant usage of
> document.cookie. Is that an unexpected result for that site?

The first profile spends a total of 943ms waiting for document.cookie, out of a total run of about one minute. That seems rather significant to me.

Comment 37

3 months ago
Ah, I got confused by the time spent in PresShell::Paint and didn't read the profile correctly.

Amy, the patch doesn't appear to change the behaviour of CookieServiceChild::GetCookieStringInternal at all, which means that the document.cookie will still perform a sync IPC operation. I expect the profiles to look very similar until this is changed.

Comment 38

3 months ago
Comment on attachment 8849038 [details] [diff] [review]
implementation

Review of attachment 8849038 [details] [diff] [review]:
-----------------------------------------------------------------

I've requested enough changes that require significant design changes that I'm not going to keep reviewing the rest of the patch. It's not clear to me how much of the current changes are actually being executed right now, so I think it's worth fixing that before I review the next version. I think the big improvements will be:
* rewriting CookieServiceChild::GetCookieStringInternal and CookieServiceChild::SetCookieStringInternal to interact with a hashtable stored in CookeiServiceChild
* sending a parsed structure over IPDL, rather than individual cookie strings
* receiving IPC notifications from the parent about changes to the cookie database

::: docshell/base/nsDocShell.cpp
@@ +9222,5 @@
>  
>      // Mark the channel as being a document URI...
>      aOpenedChannel->GetLoadFlags(&loadFlags);
> +    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
> +                 nsIChannel::LOAD_DOCUMENT_NEEDS_COOKIES;

We will need to check if the document is sandboxed here.

@@ +11391,5 @@
>    nsLoadFlags loadFlags = 0;
>    (void)aChannel->GetLoadFlags(&loadFlags);
>    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
>                 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
> +//  loadFlags |= nsIChannel::LOAD_DOCUMENT_NEEDS_COOKIES;

What's going on here?

::: dom/html/nsHTMLDocument.cpp
@@ +2347,5 @@
>  
>      nsLoadFlags loadFlags = 0;
>      channel->GetLoadFlags(&loadFlags);
> +    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
> +                 nsIChannel::LOAD_DOCUMENT_NEEDS_COOKIES;

We will need to check if the document is sandboxed here.

::: netwerk/base/nsIChannel.idl
@@ +285,5 @@
>       * Set to force bypass of any service worker interception of the channel.
>       */
>      const unsigned long LOAD_BYPASS_SERVICE_WORKER = 1 << 25;
>  
> +    const unsigned long LOAD_DOCUMENT_NEEDS_COOKIES = 1 << 26;

We will want documentation for this new flag.

@@ +287,5 @@
>      const unsigned long LOAD_BYPASS_SERVICE_WORKER = 1 << 25;
>  
> +    const unsigned long LOAD_DOCUMENT_NEEDS_COOKIES = 1 << 26;
> +
> +    // nsICachingChannel load flags begin at bit 27.

This change needs to be made in nsICachingChannel.idl, but those flags go up to 31 right now. We may have a problem here.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +68,5 @@
>    }
> +
> +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
> +  os->AddObserver(this, "cookie-doc-changed", true);
> +  os->AddObserver(this, "private-doc-cookie-changed", true);

I don't believe there is any code that emits these notifications in the content process in this patch. We will need IPC notifications on PCookieService.ipdl instead of observer service notifications.

::: netwerk/cookie/CookieServiceParent.cpp
@@ -63,3 @@
>  namespace mozilla {
>  namespace net {
> -

nit: revert this change.

::: netwerk/cookie/PCookieService.ipdl
@@ -24,5 @@
>   *
>   * @see nsICookieService
>   * @see nsICookiePermission
>   */
> -

nit: revert this change.

::: netwerk/cookie/nsCookieService.cpp
@@ +1973,4 @@
>    return NS_OK;
>  }
>  
> +

nit: revert this change.

@@ +2344,5 @@
> +            "private-doc-cookie-changed" : "cookie-doc-changed";
> +  } else {
> +    topic = mDBState == mPrivateDBState ?
> +            "private-cookie-changed" : "cookie-changed";
> +  }

I don't believe we want to send different notifications in certain circumstances. If I understand correctly, the goal of this change is to implement step 6, but these notifications are only observed in the parent process. Step 6 suggests adding cookie-changed/private-cookie-changed observers to CookieServiceParent; we can use that to send an IPC message instead.

@@ +3485,5 @@
>  
>    // create a stack-based nsCookieAttributes, to store all the
>    // attributes parsed from the cookie
>    nsCookieAttributes cookieAttributes;
> +  nsCookieAttributes cookieAttributes2;

Unused?

@@ +3848,5 @@
> +    UpdateCookieToTable(aHostURI, aChannel);
> +    NotifyChanged(aCookie, foundCookie ? u"changed" : u"added", true);
> +    return;
> +  }
> +  NotifyChanged(aCookie, foundCookie ? u"changed" : u"added", false);

I don't understand why these changes are necessary. Updating the list of documents that can know about cookies is only important when there is a channel performing a request. In cases where the cookie store is being updated, we don't need to update the list of documents at all, only query it to decide whether to notify the child process or not.

@@ +3984,5 @@
> +}
> +
> +
> +void
> +nsCookieService::GetMatchingCookies(const char          *aCookieHeader,

Instead of parsing the cookie header, we should make an API that retrieves the cookies that match directly from the cookie service's database.

::: netwerk/cookie/nsICookie.idl
@@ +94,5 @@
> +    [noscript, nostdcall, binaryname(GetOriginAttributes)]
> +    OriginAttributes binaryGetOriginAttributes();
> +
> +    [noscript, nostdcall, binaryname(SetOriginAttributes)]
> +    void binarySetOriginAttributes(in const_OriginAttributesRef aOriginAttrs);

Instead of making these changes, I would recommend casting from nsICookie* to nsCookie*.

::: netwerk/cookie/nsICookieService.idl
@@ +18,5 @@
> +    nsrefcnt mRefDocCnt;
> +  };
> +
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;

Why do these need to be declared in the IDL file?

@@ +205,5 @@
>    void setCookieStringFromHttp(in nsIURI aURI, in nsIURI aFirstURI, in nsIPrompt aPrompt, in string aCookie, in string aServerTime, in nsIChannel aChannel);
> +
> +  void updateCookieToTable(in nsIURI aHostURI, in nsIChannel aChannel);
> +
> +  void destroyCookiesFromTable(in nsIURI aHostrURI, in nsIChannel aChannel);

I think it would make more sense to cast the nsICookieService pointers into CookieServiceParent/CookieServiceChild and call methods on that, rather than exposing these through IDL.

::: netwerk/protocol/http/HttpBaseChannel.cpp
@@ +61,5 @@
>  #include "nsIXULRuntime.h"
>  #include "nsICacheInfoChannel.h"
>  #include "nsIDOMWindowUtils.h"
>  #include "nsIThrottlingService.h"
> +#include "nsCookieService.h"

Why is this necessary?

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +369,5 @@
>    nsCString mAltDataType;
>  };
>  
>  mozilla::ipc::IPCResult
> +HttpChannelChild::RecvSetMatchingCookies(nsTArray<nsCString>&& cookiesList)

Rather than sending strings that need to be parsed, we should send a structure that can be used to call nsCookie::Create without any parsing.

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1128,5 @@
>               "HttpChannelParent getting OnStartRequest from a different nsHttpChannel instance");
>  
> +  nsLoadFlags loadFlags;
> +  chan->GetLoadFlags(&loadFlags);
> +  if (loadFlags & nsIChannel::LOAD_DOCUMENT_NEEDS_COOKIES &&

We should only need to check LOAD_DOCUMENT_NEEDS_COOKIES here.

::: netwerk/protocol/http/PHttpChannel.ipdl
@@ +171,5 @@
>  
>    // Tell the child information of matched URL againts SafeBrowsing list
>    async SetClassifierMatchedInfo(ClassifierInfo info);
>  
> +  async SetMatchingCookies(nsCString[] cookiesList);

Add documentation like `// Provide the child with the list of existing cookies that can be accessed by a document loaded via this channel.`

@@ +181,5 @@
>    // Send__delete__() and complete the steps required to finish the redirect.
>    async FinishInterceptedRedirect();
>  
>    async SetPriority(int16_t priority);
> +

nit: revert this change.

::: netwerk/protocol/http/moz.build
@@ +126,5 @@
>  ]
> +
> +if CONFIG['NECKO_COOKIES']:
> +    LOCAL_INCLUDES += [
> +        '../../cookie',

Let's use /netwerk/cookie instead.
Attachment #8849038 - Flags: feedback?(josh) → feedback-
(Assignee)

Comment 39

3 months ago
Created attachment 8849675 [details] [diff] [review]
implementation

Hi Josh,
My modifications as below:
1. If load flags include LOAD_DOCUMENT_URI && LOAD_DOCUMENT_NEEDS_COOKIES, call CookieServiceChild::GetCookieStringFromDocumentCookieMap().
2. Added mCookieValue and mCookieNmae on DocumentCookieInfo.
3. Modified UpdateCookieToTable().

Would you help me to review my patch?
Thanks!
Attachment #8849038 - Attachment is obsolete: true
Attachment #8849675 - Flags: feedback?(josh)
(Assignee)

Comment 40

3 months ago
Comment on attachment 8849675 [details] [diff] [review]
implementation

Hi Josh,
Sorry for I didn't get the information of f-, I will modify my patch from your suggestions.
Thanks!
Attachment #8849675 - Flags: feedback?(josh)
(Assignee)

Comment 41

3 months ago
Created attachment 8852155 [details] [diff] [review]
implementation

Hi Josh,
I have finished to modify the patch from your suggestions as below:
[Load Flags]
1. Created a load flags LOAD_DOCUMENT_NEEDS_COOKIE on nsIRequest.idl.
2. Added SandBoxed when set the loadFlags to nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE.

[rewrote CookieServiceChild::SetCookieString & CookieServiceChild::GetCookieString]
1. Added some field on 
2. Modified SetCookieString & GetCookieString to interact with a hashtable stored in CookeiServiceChild

[Sent a parsed structure over IPDL]
1. Created a function SetUpdateCookieToTable on PCookieService.ipdl.
2. Let CookieServiceParent to inherit the nsICookieService.
3. CookieServiceParent called SendSetUpdateCookieToTable on CookieServiceParent::UpdateCookieToTable.
4. When nsCookieService found the cookie have to set or modify, called mCookiServiceParent->UpdateCookieToTable.
5. Created CookieServiceParent::GetSingleton().

[Others]
1. Modified the function argument from string list to string.

[Not finish]
1. Have to confirm the function nsCookieService::ConfirmMatchingCookie().
2. network/protocol/http/moz.build.

Would you give me some suggestion on my patch?

Thanks!
Attachment #8849675 - Attachment is obsolete: true
Attachment #8852155 - Flags: feedback?(josh)
(Assignee)

Comment 42

3 months ago
Created attachment 8852874 [details] [diff] [review]
bug-1331680-tmp.patch

Hi Josh,
I found the problem about my implementation when Parent have to notify Child to update hashtable.
And the attachment is my new implementation for fixing the above problem:
[Implementation]
1. Call AddObserver to get notifies "cookie-changed" and "private-cookie-changed" on CookieServiceParent.
2. When get the notify, CookieServiceParent have to confirm the aTopic & mDocNeedsCookie(Boolean flag to confirm the one of load flags which includes nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) then update hash table which in CookieServiceParent.
3. CookieServiceParent use ipc to notify child which have to update the DocumentCookieMap after CookieServiceParent updated the DocumentCookieMap.

[Data Structure]
1. Created attribute documentNeedsCookie on nsICookieService for confirming the one of LoadFlags which includes "nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE" on the channel.
Flags: needinfo?(josh)

Comment 43

3 months ago
Comment on attachment 8852874 [details] [diff] [review]
bug-1331680-tmp.patch

Review of attachment 8852874 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceParent.cpp
@@ +213,5 @@
> +  return NS_OK;
> +}
> +
> +NS_IMETHODIMP
> +CookieServiceParent::GetDocumentNeedsCookie(bool *aDocumentNeedsCookie)

I don't see how this will work; a CookieServiceParent represents many possible documents. What is the purpose of this flag?

Comment 44

3 months ago
Comment on attachment 8852155 [details] [diff] [review]
implementation

Review of attachment 8852155 [details] [diff] [review]:
-----------------------------------------------------------------

There is still a lot of code in this patch that I have not looked through closely because there are a number of problems I found that require significant changes. I am concerned by how much code is being duplicated for reasons that I do not understand. Here are some changes that will make it easier to review this patch:
* when sending cookies from the parent to the child, use an IPDL structure instead of a string. This should allow us to revert all of the changes to the parsing code, since the parent already stores parsed nsCookie values.
* avoid modifying the nsICookieService interface, and add APIs to nsCookieService or CookieServiceChild instead.
* remove many places that check the load flags and content policy type for channels. They should not be necessary, and then we do not need to create dummy channels in many places.

::: docshell/base/nsDocShell.cpp
@@ +5797,5 @@
>  
> +  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
> +  nsIChannel *channel = GetCurrentDocChannel();
> +  if (service && mLoadingURI && channel ) {
> +    service->DestroyCookiesFromTable(mLoadingURI, channel);

Note to self: we probably need to look at mOriginalURI here, per Ehsan's original comment.

@@ +8605,5 @@
> +    if (doc) {
> +      sandboxFlags = doc->GetSandboxFlags();
> +    }
> +  }
> +  aLoadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;

This function seems to be confused; we don't do anything with the knowledge of sandboxing. In particular, unless the sandbox flags include the "allow-same-origin" value then we do not need to set LOAD_DOCUMENT_NEEDS_COOKIE.

::: dom/html/nsHTMLDocument.cpp
@@ +2413,5 @@
>      nsLoadFlags loadFlags = 0;
>      channel->GetLoadFlags(&loadFlags);
>      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> +    if (mSandboxFlags) {
> +      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;

We should only be setting LOAD_DOCUMENT_NEEDS_COOKIE if there are no sandboxing flags or the "allow-same-origin" flag is set.

::: netwerk/base/nsIRequest.idl
@@ +131,5 @@
>       */
>      const unsigned long LOAD_HTML_OBJECT_DATA = 1 << 1;
>  
> +    /**
> +     * TODO: add comment to explain the function of this flag.

This comment isn't closed.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +139,5 @@
> +  if (docCookieInfoList && key.Length() > 0) {
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = (*docCookieInfoList)[i];
> +      if (docCookieInfo.mPathName.Equals(sourcePath)) {
> +        if (!docCookieInfo.mCookieName.IsEmpty() || !docCookieInfo.mCookieValue.IsEmpty()) {

I think we should be able to assert these conditions.

@@ +147,5 @@
> +          if (!docCookieInfo.mCookieName.IsEmpty()) {
> +            aCookieString.Append(docCookieInfo.mCookieName.get());
> +          } else {
> +            aCookieString.Append(docCookieInfo.mCookieValue.get());
> +          }

I don't understand this logic. Also, shouldn't there be "=" separating names and values?

@@ +198,3 @@
>    // Synchronously call the parent.
> +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> +  }

The point of these changes is to remove all synchronous IPC related to cookies. We don't need to check anything related to the channel that's being used; we should always have the necessary information available in the cookie map.

@@ +245,5 @@
> +  nsLoadFlags loadFlags;
> +  aChannel->GetLoadFlags(&loadFlags);
> +  if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
> +    UpdateCookieToTable(aHostURI, aChannel, aCookieString);
> +  }

We do not need to check anything about the channel here. We should always update the cookie map.

@@ +250,3 @@
>    // Synchronously call the parent.
>    SendSetCookieString(uriParams, !!isForeign, cookieString, serverTime,
>                        attrs);

This IPC message should be made asynchronous.

@@ +331,5 @@
> +CookieServiceChild::UpdateCookieToTable(nsIURI     *aHostURI,
> +                                        nsIChannel *aChannel,
> +                                        const char *aCookie)
> +{
> +  if (!aHostURI || !aChannel) {

There's a lot of duplicated code between the parent and the child implementation. Can we move this logic into the DocumentCookieMap type instead?

::: netwerk/cookie/CookieServiceParent.cpp
@@ +51,3 @@
>  }
>  
> +CookieServiceParent*

Let's return already_AddRefed<CookieServiceParent> instead.

@@ +66,5 @@
> +                             const char16_t  *aData)
> +{
> +  NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
> +                      "not a pref change topic!");
> +  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);

Why was this code added? This class did not add any preference observers.

@@ +172,5 @@
> +CookieServiceParent::GetCookieStringInternal(nsIURI *aHostURI,
> +                                             nsIChannel *aChannel,
> +                                             char **aCookieString)
> +{
> +  if (!mCookieService) {

As part of implementing nsICookieService, we appear to be duplicating a lot of code from CookieServiceChild. I don't understand why this is happening; I can't even find any code in this patch that calls these methods.

@@ +308,5 @@
> +  mContentProcessCookies.Get(key, &docCookieInfoList);
> +  nsCString baseDomain;
> +  aHostURI->GetAsciiHost(baseDomain);
> +  bool hostURIExist = false;
> +  if (docCookieInfoList && key.Length() > 0) {

Why do we have these checks for key length?

@@ +310,5 @@
> +  aHostURI->GetAsciiHost(baseDomain);
> +  bool hostURIExist = false;
> +  if (docCookieInfoList && key.Length() > 0) {
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = (*docCookieInfoList)[i];

docCookieInfo isn't a pointer, so this looks like it's copying the stored value. This means that modifying mRefDocCnt later will not update the value in the array.

@@ +313,5 @@
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = (*docCookieInfoList)[i];
> +      if (docCookieInfo.mPathName.Equals(baseDomain)) {
> +        hostURIExist = true;
> +        docCookieInfo.mRefDocCnt++;

We should probably break out of the loop for clarity.

@@ +326,5 @@
> +  if (!hostURIExist) {
> +    docCookieInfo.mPathName = baseDomain;
> +    docCookieInfo.mIsSecure = true;
> +    docCookieInfo.mRefDocCnt++;
> +    docCookieInfoList->InsertElementAt(docCookieInfoList->Length(), docCookieInfo);

Let's use AppendElement instead.

@@ +328,5 @@
> +    docCookieInfo.mIsSecure = true;
> +    docCookieInfo.mRefDocCnt++;
> +    docCookieInfoList->InsertElementAt(docCookieInfoList->Length(), docCookieInfo);
> +  }
> +  mContentProcessCookies.Put(key, docCookieInfoList);

Let's move this into the branch that creates a new list, sine it should not be necessary otherwise.

@@ +359,5 @@
> +  mContentProcessCookies.Get(key, &docCookieInfoList);
> +  nsCString sourcePath = nsCookieService::GetPathFromURI(aHostURI);
> +  if (docCookieInfoList && key.Length() > 0) {
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = (*docCookieInfoList)[i];

Same issue here about copying the value.

@@ +366,5 @@
> +          docCookieInfo.mRefDocCnt--;
> +        } else {
> +          docCookieInfoList->RemoveElementAt(i);
> +        }
> +      }

We should be breaking out of the loop here, or our loop index could be incorrect after removing an element.

@@ +371,5 @@
> +    }
> +  } else if (!docCookieInfoList || key.Length() <= 0) {
> +    return NS_ERROR_NOT_AVAILABLE;
> +  }
> +  mContentProcessCookies.Put(key, docCookieInfoList);

If the list is now empty, we should delete it and remove the hashtable entry to reclaim the memory we allocated.

::: netwerk/cookie/CookieServiceParent.h
@@ +7,5 @@
>  #define mozilla_net_CookieServiceParent_h
>  
>  #include "mozilla/net/PCookieServiceParent.h"
> +#include "nsICookieService.h"
> +#include "nsIobserver.h"

This should be nsIObserver.h.

@@ +17,5 @@
>  namespace mozilla {
>  namespace net {
>  
>  class CookieServiceParent : public PCookieServiceParent
> +                          , public nsICookieService

Why are we inheriting from and implementing this interface? I can't find any code that needs this.

@@ +29,3 @@
>    CookieServiceParent();
> +  static CookieServiceParent* GetSingleton();
> +  DocumentCookieMap mContentProcessCookies;

Let's not make this map public.

::: netwerk/cookie/PCookieService.ipdl
@@ +10,5 @@
>  
>  using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
> +using struct mozilla::net::DocumentCookieInfo from "mozilla/net/DocumentCookieStruct.h";
> +using mozilla::net::DocumentCookieInfoList from "mozilla/net/DocumentCookieStruct.h";
> +using mozilla::net::DocumentCookieMap from "mozilla/net/DocumentCookieStruct.h";

Why are these necessary?

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'DocumentCookieStruct.h',

This file is missing from this patch.

::: netwerk/cookie/nsCookieService.cpp
@@ +773,5 @@
>    }
>  
> +  // Get the nsCookieService instance directly, so we can call internal methods.
> +  mCookieServiceParent =
> +    already_AddRefed<CookieServiceParent>(CookieServiceParent::GetSingleton());

We should remove this cast when making the changes to CookieServiceParent::GetSingleton().

@@ +1978,4 @@
>    return NS_OK;
>  }
>  
> +

nit: revert this change.

@@ +3557,5 @@
>                               nsCookie                      *aCookie,
>                               int64_t                        aCurrentTimeInUsec,
>                               nsIURI                        *aHostURI,
>                               const char                    *aCookieHeader,
> +                             nsIChannel                    *aChannel,

This argument should not be necessary.

@@ +3767,5 @@
> +  if (aHostURI && aChannel && aCookieHeader) {
> +    nsLoadFlags loadFlags;
> +    aChannel->GetLoadFlags(&loadFlags);
> +    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
> +      mCookieServiceParent->UpdateCookieToTable(aHostURI, aChannel, aCookieHeader);

There are a couple issues here - this method should not require a channel, because what we do with the cookie should not depend on whether there's a channel trying to load a document that triggered this update. I don't believe this added code should be necessary at all - the cookie service parent will receive a notification via the observer service instead.

@@ +3916,5 @@
> +nsCookieService::ConfirmMatchingCookie(nsCookie           *aCookie,
> +                                       nsIURI             *aHostURI,
> +                                       const char         *aCookieHeader,
> +                                       nsIChannel         *aChannel,
> +                                       nsCookieAttributes *aCookieAttributes,

aCookie and aCookieAttributes are local pointers. The caller can't observe any change to them, but this code is allocating memory and storing it in them, causing memory leaks. I don't believe this code is doing what it is supposed to.

@@ +4019,5 @@
> +  NS_ENSURE_SUCCESS(rv, rv);
> +
> +  aKey.Append(spec);
> +  aKey.Append(NS_LITERAL_CSTRING("!"));
> +  aKey.AppendInt(aContentType);

I am concerned about this key. What is this code based on? Why do we need to include an nsContentPolicyType in it? The hash for hostTable in nsCookieService is based on the base domain and origin attributes; can we reuse nsCookieKey somehow instead for this new hashtable?

::: netwerk/cookie/nsCookieService.h
@@ +64,5 @@
> +                             nsrefcnt mRefDocCnt;
> +                               };
> +
> +    typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +      typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;*/

Let's remove this commented out code.

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +378,5 @@
> +  nsCOMPtr<nsIURI> uri;
> +  nsresult result;
> +  result = GetURI(getter_AddRefs(uri));
> +  nsICookieService *cs = gHttpHandler->GetCookieService();
> +  cs->SetCookieString(uri, nullptr, cookie.get(), this);

I believe we only need to call UpdateCookieToTable here.

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1144,5 @@
> +     nsCOMPtr<nsIURI> newURI;
> +     chan->GetURI(getter_AddRefs(newURI));
> +     nsICookieService *cs = mHttpHandler->GetCookieService();
> +     nsCString cookies;
> +     cs->GetCookieString(newURI, chan, getter_Copies(cookies));

Rather than getting the cookie string and splitting it into an array of strings, I believe the following design would be cleaner and involve less string parsing:
* declare an IPDL structure that represents a cookie, containing the same fields as nsCookie
* add a GetScriptAccessibleCookiesForURL API to nsCookieService that retrieves an array of this new structure
* send the array in a single IPDL message
* make a nsCookie constructor that accepts an instance of the new IPDL structure
Attachment #8852155 - Flags: feedback?(josh) → feedback-

Updated

3 months ago
Flags: needinfo?(josh)
Depends on: 1354349
re: comment 6 and comment 28:  we're going to add a IsDocumentChannel() method for determining whether a channel is for a document load.  See bug 1354349.  We should use that code in this bug.
(Assignee)

Comment 46

3 months ago
(In reply to Josh Matthews [:jdm] from comment #44)
> Comment on attachment 8852155 [details] [diff] [review]
> implementation
> 
> Review of attachment 8852155 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> There is still a lot of code in this patch that I have not looked through
> closely because there are a number of problems I found that require
> significant changes. I am concerned by how much code is being duplicated for
> reasons that I do not understand. Here are some changes that will make it
> easier to review this patch:
> * when sending cookies from the parent to the child, use an IPDL structure
> instead of a string. This should allow us to revert all of the changes to
> the parsing code, since the parent already stores parsed nsCookie values.
> * avoid modifying the nsICookieService interface, and add APIs to
> nsCookieService or CookieServiceChild instead.
> * remove many places that check the load flags and content policy type for
> channels. They should not be necessary, and then we do not need to create
> dummy channels in many places.
> 
Thanks for your suggestions.

> ::: docshell/base/nsDocShell.cpp
> @@ +5797,5 @@
> >  
> > +  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
> > +  nsIChannel *channel = GetCurrentDocChannel();
> > +  if (service && mLoadingURI && channel ) {
> > +    service->DestroyCookiesFromTable(mLoadingURI, channel);
> 
> Note to self: we probably need to look at mOriginalURI here, per Ehsan's
> original comment.
>  
> @@ +8605,5 @@
> > +    if (doc) {
> > +      sandboxFlags = doc->GetSandboxFlags();
> > +    }
> > +  }
> > +  aLoadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
> 
> This function seems to be confused; we don't do anything with the knowledge
> of sandboxing. In particular, unless the sandbox flags include the
> "allow-same-origin" value then we do not need to set
> LOAD_DOCUMENT_NEEDS_COOKIE.
> 
> ::: dom/html/nsHTMLDocument.cpp
> @@ +2413,5 @@
> >      nsLoadFlags loadFlags = 0;
> >      channel->GetLoadFlags(&loadFlags);
> >      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> > +    if (mSandboxFlags) {
> > +      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
> 
> We should only be setting LOAD_DOCUMENT_NEEDS_COOKIE if there are no
> sandboxing flags or the "allow-same-origin" flag is set.
> 
Ok, I will confirm the sandboxed flags which include the "allow-same-origin".

> ::: netwerk/base/nsIRequest.idl
> @@ +131,5 @@
> >       */
> >      const unsigned long LOAD_HTML_OBJECT_DATA = 1 << 1;
> >  
> > +    /**
> > +     * TODO: add comment to explain the function of this flag.
> 
> This comment isn't closed.
> 
I already closed the comment.

> ::: netwerk/cookie/CookieServiceChild.cpp
> @@ +139,5 @@
> > +  if (docCookieInfoList && key.Length() > 0) {
> > +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> > +      docCookieInfo = (*docCookieInfoList)[i];
> > +      if (docCookieInfo.mPathName.Equals(sourcePath)) {
> > +        if (!docCookieInfo.mCookieName.IsEmpty() || !docCookieInfo.mCookieValue.IsEmpty()) {
> 
> I think we should be able to assert these conditions.
> 
Ok, I will do it.
> @@ +147,5 @@
> > +          if (!docCookieInfo.mCookieName.IsEmpty()) {
> > +            aCookieString.Append(docCookieInfo.mCookieName.get());
> > +          } else {
> > +            aCookieString.Append(docCookieInfo.mCookieValue.get());
> > +          }
> 
> I don't understand this logic. Also, shouldn't there be "=" separating names
> and values?
> 
Sorry I forgot to add "=" and values.
> @@ +198,3 @@
> >    // Synchronously call the parent.
> > +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> > +  }
> 
> The point of these changes is to remove all synchronous IPC related to
> cookies. We don't need to check anything related to the channel that's being
> used; we should always have the necessary information available in the
> cookie map.
> 
> @@ +245,5 @@
> > +  nsLoadFlags loadFlags;
> > +  aChannel->GetLoadFlags(&loadFlags);
> > +  if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
> > +    UpdateCookieToTable(aHostURI, aChannel, aCookieString);
> > +  }
> 
> We do not need to check anything about the channel here. We should always
> update the cookie map.
> 
Ok, I understand.

> @@ +250,3 @@
> >    // Synchronously call the parent.
> >    SendSetCookieString(uriParams, !!isForeign, cookieString, serverTime,
> >                        attrs);
> 
> This IPC message should be made asynchronous.
> 
Ok, I will modify it.

> @@ +331,5 @@
> > +CookieServiceChild::UpdateCookieToTable(nsIURI     *aHostURI,
> > +                                        nsIChannel *aChannel,
> > +                                        const char *aCookie)
> > +{
> > +  if (!aHostURI || !aChannel) {
> 
> There's a lot of duplicated code between the parent and the child
> implementation. Can we move this logic into the DocumentCookieMap type
> instead?
> 
Ok, I will move UpdateCookieToTable to DocumentCookieStruct.h

> ::: netwerk/cookie/CookieServiceParent.cpp
> @@ +51,3 @@
> >  }
> >  
> > +CookieServiceParent*
> 
> Let's return already_AddRefed<CookieServiceParent> instead.
> 
I changed the method as below:
When CookieServiceParent receive the notification about modifying cookie, then call UpdateCookieToTable and ask CookieServiceChild to call UpdateCookieToTable too.

> @@ +66,5 @@
> > +                             const char16_t  *aData)
> > +{
> > +  NS_ASSERTION(strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0,
> > +                      "not a pref change topic!");
> > +  nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
> 
> Why was this code added? This class did not add any preference observers.
> 
I will remove these code.

> @@ +172,5 @@
> > +CookieServiceParent::GetCookieStringInternal(nsIURI *aHostURI,
> > +                                             nsIChannel *aChannel,
> > +                                             char **aCookieString)
> > +{
> > +  if (!mCookieService) {
> 
> As part of implementing nsICookieService, we appear to be duplicating a lot
> of code from CookieServiceChild. I don't understand why this is happening; I
> can't even find any code in this patch that calls these methods.
> 
> @@ +308,5 @@
> > +  mContentProcessCookies.Get(key, &docCookieInfoList);
> > +  nsCString baseDomain;
> > +  aHostURI->GetAsciiHost(baseDomain);
> > +  bool hostURIExist = false;
> > +  if (docCookieInfoList && key.Length() > 0) {
> 
> Why do we have these checks for key length?
> 
Ok, I will remove it.

> @@ +310,5 @@
> > +  aHostURI->GetAsciiHost(baseDomain);
> > +  bool hostURIExist = false;
> > +  if (docCookieInfoList && key.Length() > 0) {
> > +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> > +      docCookieInfo = (*docCookieInfoList)[i];
> 
> docCookieInfo isn't a pointer, so this looks like it's copying the stored
> value. This means that modifying mRefDocCnt later will not update the value
> in the array.
> 
Ok, I understand.

> @@ +313,5 @@
> > +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> > +      docCookieInfo = (*docCookieInfoList)[i];
> > +      if (docCookieInfo.mPathName.Equals(baseDomain)) {
> > +        hostURIExist = true;
> > +        docCookieInfo.mRefDocCnt++;
> 
> We should probably break out of the loop for clarity.
> 
Ok, I will modify it.

> @@ +326,5 @@
> > +  if (!hostURIExist) {
> > +    docCookieInfo.mPathName = baseDomain;
> > +    docCookieInfo.mIsSecure = true;
> > +    docCookieInfo.mRefDocCnt++;
> > +    docCookieInfoList->InsertElementAt(docCookieInfoList->Length(), docCookieInfo);
> 
> Let's use AppendElement instead.
> 
Ok, I will modify it.

> @@ +328,5 @@
> > +    docCookieInfo.mIsSecure = true;
> > +    docCookieInfo.mRefDocCnt++;
> > +    docCookieInfoList->InsertElementAt(docCookieInfoList->Length(), docCookieInfo);
> > +  }
> > +  mContentProcessCookies.Put(key, docCookieInfoList);
> 
> Let's move this into the branch that creates a new list, sine it should not
> be necessary otherwise.
> 
Ok, I will move it into the else branch.

> @@ +359,5 @@
> > +  mContentProcessCookies.Get(key, &docCookieInfoList);
> > +  nsCString sourcePath = nsCookieService::GetPathFromURI(aHostURI);
> > +  if (docCookieInfoList && key.Length() > 0) {
> > +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> > +      docCookieInfo = (*docCookieInfoList)[i];
> 
> Same issue here about copying the value.
> 
Ok, I will modify it.

> @@ +366,5 @@
> > +          docCookieInfo.mRefDocCnt--;
> > +        } else {
> > +          docCookieInfoList->RemoveElementAt(i);
> > +        }
> > +      }
> 
> We should be breaking out of the loop here, or our loop index could be
> incorrect after removing an element.
> 
Ok, I will modify it.

> @@ +371,5 @@
> > +    }
> > +  } else if (!docCookieInfoList || key.Length() <= 0) {
> > +    return NS_ERROR_NOT_AVAILABLE;
> > +  }
> > +  mContentProcessCookies.Put(key, docCookieInfoList);
> 
> If the list is now empty, we should delete it and remove the hashtable entry
> to reclaim the memory we allocated.
> 
Ok, I will use IsEmpty() to confirm the list and remove this entry from hashtable. 

> ::: netwerk/cookie/CookieServiceParent.h
> @@ +7,5 @@
> >  #define mozilla_net_CookieServiceParent_h
> >  
> >  #include "mozilla/net/PCookieServiceParent.h"
> > +#include "nsICookieService.h"
> > +#include "nsIobserver.h"
> 
> This should be nsIObserver.h.
> 
I already modified it before, thanks.

> @@ +17,5 @@
> >  namespace mozilla {
> >  namespace net {
> >  
> >  class CookieServiceParent : public PCookieServiceParent
> > +                          , public nsICookieService
> 
> Why are we inheriting from and implementing this interface? I can't find any
> code that needs this.
> 
Because I created a function UpdateCookieToTable() on nsICookieService, I will move UpdateCookieToTable() to DocumentCookieStruct.h and remove the inheritance from CookieServiceParent.h.

> @@ +29,3 @@
> >    CookieServiceParent();
> > +  static CookieServiceParent* GetSingleton();
> > +  DocumentCookieMap mContentProcessCookies;
> 
> Let's not make this map public.
> 
Ok, I will modify it.

> ::: netwerk/cookie/PCookieService.ipdl
> @@ +10,5 @@
> >  
> >  using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
> > +using struct mozilla::net::DocumentCookieInfo from "mozilla/net/DocumentCookieStruct.h";
> > +using mozilla::net::DocumentCookieInfoList from "mozilla/net/DocumentCookieStruct.h";
> > +using mozilla::net::DocumentCookieMap from "mozilla/net/DocumentCookieStruct.h";
> 
> Why are these necessary?
> 
Ok, I will use include "DocumentCookieStruct.h".

> ::: netwerk/cookie/moz.build
> @@ +22,5 @@
> >  if CONFIG['NECKO_COOKIES']:
> >      EXPORTS.mozilla.net = [
> >          'CookieServiceChild.h',
> >          'CookieServiceParent.h',
> > +        'DocumentCookieStruct.h',
> 
> This file is missing from this patch.
> 
Sorry for I didn't export this code on my patch.

> ::: netwerk/cookie/nsCookieService.cpp
> @@ +773,5 @@
> >    }
> >  
> > +  // Get the nsCookieService instance directly, so we can call internal methods.
> > +  mCookieServiceParent =
> > +    already_AddRefed<CookieServiceParent>(CookieServiceParent::GetSingleton());
> 
> We should remove this cast when making the changes to
> CookieServiceParent::GetSingleton().
> 
I already removed CookieServiceParent::GetSingleton().

> @@ +1978,4 @@
> >    return NS_OK;
> >  }
> >  
> > +
> 
> nit: revert this change.
> 
I will remove it.

> @@ +3557,5 @@
> >                               nsCookie                      *aCookie,
> >                               int64_t                        aCurrentTimeInUsec,
> >                               nsIURI                        *aHostURI,
> >                               const char                    *aCookieHeader,
> > +                             nsIChannel                    *aChannel,
> 
> This argument should not be necessary.
> 

> @@ +3767,5 @@
> > +  if (aHostURI && aChannel && aCookieHeader) {
> > +    nsLoadFlags loadFlags;
> > +    aChannel->GetLoadFlags(&loadFlags);
> > +    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
> > +      mCookieServiceParent->UpdateCookieToTable(aHostURI, aChannel, aCookieHeader);
> 
> There are a couple issues here - this method should not require a channel,
> because what we do with the cookie should not depend on whether there's a
> channel trying to load a document that triggered this update. I don't
> believe this added code should be necessary at all - the cookie service
> parent will receive a notification via the observer service instead.
> 
I already modified to let CookieServiceParent receive the notification from the observer.
If CookieServiceParent get "cookie-changed" or "private-cookie-changed", it will call UpdateCookieToTable() and ask CookieServiceChild to call UpdateCookieToTable() too.

> @@ +3916,5 @@
> > +nsCookieService::ConfirmMatchingCookie(nsCookie           *aCookie,
> > +                                       nsIURI             *aHostURI,
> > +                                       const char         *aCookieHeader,
> > +                                       nsIChannel         *aChannel,
> > +                                       nsCookieAttributes *aCookieAttributes,
> 
> aCookie and aCookieAttributes are local pointers. The caller can't observe
> any change to them, but this code is allocating memory and storing it in
> them, causing memory leaks. I don't believe this code is doing what it is
> supposed to.
> 
> @@ +4019,5 @@
> > +  NS_ENSURE_SUCCESS(rv, rv);
> > +
> > +  aKey.Append(spec);
> > +  aKey.Append(NS_LITERAL_CSTRING("!"));
> > +  aKey.AppendInt(aContentType);
> 
> I am concerned about this key. What is this code based on? Why do we need to
> include an nsContentPolicyType in it? The hash for hostTable in
> nsCookieService is based on the base domain and origin attributes; can we
> reuse nsCookieKey somehow instead for this new hashtable?
> 
Ok, I understand.

> ::: netwerk/cookie/nsCookieService.h
> @@ +64,5 @@
> > +                             nsrefcnt mRefDocCnt;
> > +                               };
> > +
> > +    typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> > +      typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;*/
> 
> Let's remove this commented out code.
> 
Ok, I will remove it.

> ::: netwerk/protocol/http/HttpChannelChild.cpp
> @@ +378,5 @@
> > +  nsCOMPtr<nsIURI> uri;
> > +  nsresult result;
> > +  result = GetURI(getter_AddRefs(uri));
> > +  nsICookieService *cs = gHttpHandler->GetCookieService();
> > +  cs->SetCookieString(uri, nullptr, cookie.get(), this);
> 
> I believe we only need to call UpdateCookieToTable here.
> 
Ok, I will modify it.

> ::: netwerk/protocol/http/HttpChannelParent.cpp
> @@ +1144,5 @@
> > +     nsCOMPtr<nsIURI> newURI;
> > +     chan->GetURI(getter_AddRefs(newURI));
> > +     nsICookieService *cs = mHttpHandler->GetCookieService();
> > +     nsCString cookies;
> > +     cs->GetCookieString(newURI, chan, getter_Copies(cookies));
> 
> Rather than getting the cookie string and splitting it into an array of
> strings, I believe the following design would be cleaner and involve less
> string parsing:
> * declare an IPDL structure that represents a cookie, containing the same
> fields as nsCookie
I will create a new ipdl struct as "PCookie.ipdl" and discuss with you.

> * add a GetScriptAccessibleCookiesForURL API to nsCookieService that
> retrieves an array of this new structure
I will add this API.

> * send the array in a single IPDL message
> * make a nsCookie constructor that accepts an instance of the new IPDL
> structure


Thanks for your feedback.
(Assignee)

Comment 47

2 months ago
Hi Josh,
The implementation about crating ipdl struct to represent a cookie as below:
1. Create a ipdl struct naming CookieStruct on PCookieService.ipdl.
2. Add a new argument nsTArry<CookieStruct> in GetCookieStringInteranl().
3. Create a function naming ConfirmMatchingCookies() for confirming the cookie is matching the conditions on CookieServiceParent.
4. Move SetMatchCookie from PHttpChannel.ipdl to PCookieService.ipdl.
5. If the cookie matches the conditions, call SendSetMatchCookie() on CookieServiceParent.
6. CookieServiceChild call UpdateCookieToTable() when get ipc msg.
Would you give me your suggestions?

Thanks!
Flags: needinfo?(josh)
Duplicate of this bug: 1266275

Comment 49

2 months ago
(In reply to Amy Chung [:Amy] from comment #47)
> 2. Add a new argument nsTArry<CookieStruct> in GetCookieStringInteranl().

I don't think this sounds like the best way to go about it. We should extract https://dxr.mozilla.org/mozilla-central/rev/c697e756f738ce37abc56f31bfbc48f55625d617/netwerk/cookie/nsCookieService.cpp#3209-3308 into a separate method that takes the AutoTArray<nsCookie*> as an argument; then we can transform this array into the CookieStruct values we need for IPDL.

> 4. Move SetMatchCookie from PHttpChannel.ipdl to PCookieService.ipdl.
> 5. If the cookie matches the conditions, call SendSetMatchCookie() on
> CookieServiceParent.

I'm not sure if these steps are necessary. It seems like it could be difficult to get the CookieServiceParent actor here.

Otherwise those steps sound good.
Flags: needinfo?(josh)
(Assignee)

Comment 50

2 months ago
Hi Josh,
In my view, the implementations can divide into four part as below:
1. Set LoadFlags.
2. Init Hash Table.
3. Update Hash Table.
4. Destroy Hash Table
I can separate my patch to four parts for reviewing clear.
Would you give me your suggestions? 

Thanks!
Flags: needinfo?(josh)
Created attachment 8859465 [details] [diff] [review]
Add CookieStruct IPDL representation and send a list of CookieStructs to the child in OnStartRequest

* Added CookieStruct to NeckoChannelParams.ipdlh
* Added GetCookieListInternal that appends cookies to a nsTArray<nsCookie*>
* Change GetCookieStringInternal to use GetCookieListInternal
* Add CookieList[] argument to PHttpChannel::OnStartRequest to pass it to the child
* Add nsCookieService::GetCookieStructList to get a list of CookieStructs to send to the child

MozReview-Commit-ID: GJkvEz7mWW0
The problem which I ran into with the permission manager work, might be relevant here for you as well. See bug 1355608 comment 15. Basically the problem is that a document loaded due to a service worker fetch interception doesn't go to the parent process at any point during the load. This is problematic for sending down cookies/permissions when the document is loaded, as it means that the document can be loaded before the data is requested.

I'm not sure what the best solution will be for this bug, but I thought I should let you know ahead of time. For the service worker work, we're planning to send down permissions for all registered service worker scopes at startup, and as the scopes are registered.

Comment 53

2 months ago
(In reply to Amy Chung [:Amy] from comment #50)
> Hi Josh,
> In my view, the implementations can divide into four part as below:
> 1. Set LoadFlags.
> 2. Init Hash Table.
> 3. Update Hash Table.
> 4. Destroy Hash Table
> I can separate my patch to four parts for reviewing clear.
> Would you give me your suggestions? 
> 
> Thanks!

If the result is smaller patches, I am in favour of this :)
Flags: needinfo?(josh)
(Assignee)

Comment 54

2 months ago
Created attachment 8862104 [details] [diff] [review]
implementation part1--create & set load flag.

Hi Josh,
My modification as below:
[Set LoadFlags]
* nsDocShell.cpp
    * If CofirmBoxedForSettingLoadFlags return true, add loadflags LOAD_DOCUMENT_NEEDS_COOKIE when load flag set LOAD_DOCUMENT_URI.
* nsDocShell.h
    * Create a function "CofirmBoxedForSettingLoadFlags" about confirming the sandboxed flags include "SANDBOXED_ORIGIN".
* nsHTMLDocument.cpp
    * If sandboxed flags include "SANDBOXED_ORIGIN", add loadflags LOAD_DOCUMENT_NEEDS_COOKIE when load flag set LOAD_DOCUMENT_URI.
* nsIRequest.idl
    * Create load flag LOAD_DOCUMENT_NEEDS_COOKIE.

Would you give me your suggestions?

Thanks!
Attachment #8862104 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8862104 - Attachment description: implementation--create & set load flag. → implementation part1--create & set load flag.
(Assignee)

Comment 55

2 months ago
Created attachment 8862117 [details] [diff] [review]
implementation part2 -- init & update on hash table, and get cookie from hahs table

Hi Josh,
My modification as below:
[Init Hash Table]
 - Data struct
   1. netwerk/cookie/DocumentCookieOperation.h
   2. netwerk/cookie/DocumentCookieoperation.cpp
      * Define DocumentCookieInfo、 DocumentCookieList and DocumentCookieMap.
      * Define the function about updating, destroy and get cookie from hash table.
      * Define GetBaseDomain and GetBaseDomainFromHost.
   3. netwerk/cookie/nsCookieKey
      * Define the key on DocumentCookieMap and DBState.
   4. netwerk/ipc/NeckoChannelParams.ipdlh
      * Create new ipdl strcut which naming cookie struct.
 - HttpChannel
   1. netwerk/protocol/http/HttpChannelChild.h
      * Add new argument cookiesList on RecvOnStartRequest()
   2. netwerk/protocol/http/HttpChannelChild.cpp
      * If cookiesList which in  RecvOnStartRequest() is not empty, call Init hash table from CookieServiceChild.
   3. netwerk/protocol/http/HttpChannelParent.cpp
      * Add a expression to confirm the cookies which is matching cookie.
   4. netwerk/protocol/http/PHttpChannel.ipdl
      * Add a argument cookiesList on OnStartRequest()
 - CookieService
   1. netwerk/cookie/CookieServiceChild.cpp
      * Init hash table
      * When recv the ipc msg about have to update cookie in hash table,
        call DocumentCookieOperation->UpdateHashTable()
      * Remove SendGetCookieStringInternal, use DocumentCookieOperation->GetCookieStringFromHashTable() instead.
   2. netwerk/cookie/CookieServiceChild.h
      * Create DocumentCookieOperation object naming mDocCookieOperation.
   3. netwerk/cookie/CookieServiceParent.cpp
      * Add observer on Constructor.
      * Create Observer for processing the notifies which already registered.
        * cookie-changed
        * private-cookie-changed
        * init-hash-table
        * destroy-hash-table
      * Remove observer on ActorDestroy()
   4. netwerk/cookie/CookieServiceParent.h
      * Inherit nsIObserver
      * Create DocumentCookieOperation object naming mDocCookieOperation.
   5. netwerk/cookie/nsCookieService.cpp
      * Create GetCookieStructList & GetCookieStructListInternal.
      * Remove GeBaseDomain & GetBaseDomainFromHost.
      * Create ConfirmMatchingCookie
      * Create new notify "init-hash-table" and "destroy-hash-table"
   6. netwerk/cookie/nsCookieService.h
      * Remove nsCookieKey class.
      * Remove mTLDService.
      * Create DocumentCookieOperation object naming mDocCookieOperation for calling GetBaseDomain.
   7. netwerk/cookie/moz.build
      * Add DocumentCookieOperation & nsCookieKey
   8. netwerk/ipc/NeckoParent.cpp
      * Use AddRef when calling CookieServiceParent Constructor
      * Use Release when calling CookieServiceParent Destructor

[Not Finish]  
   1. Sort DocumentCookieList by patch length when get cookie string.
      * Add new field "path" in DocumentCookieInfo.

Would you give me your suggestions?

Thanks!
Attachment #8862117 - Flags: feedback?(josh)
(Assignee)

Comment 56

2 months ago
Created attachment 8862119 [details] [diff] [review]
implementation part5 --- Destroy cookie

Hi Josh,
My modification as below:
* netwerk/cookie/nsCookieService.cpp
    * Implement xpcom interface DestroyHashTableFromDocument.
* netwerk/cookie/nsICookieService.idl
    * Add new interface destroyHashTableFromDocument
* netwerk/cookie/CookieServiceChild.cpp
    * Implement xpcom interface DestroyHashTableFromDocument.
* docshell/base/nsDocShell.cpp
    * Call Destroy hash table when this object be released.
* dom/html/nsHTMLDocument.cpp
    * Call Destroy hash table when this object be released.

Would you give me your suggestions?
Thanks!
Attachment #8862119 - Flags: feedback?(josh)
(Assignee)

Comment 57

2 months ago
Created attachment 8862121 [details] [diff] [review]
implementation -- full version
(Assignee)

Comment 58

2 months ago
Created attachment 8862378 [details] [diff] [review]
implementation part2 -- data struct

Hi Josh,
I found the part2 patch still too large, so I divided the function on init & update on hash table and get cookie from hash table into three part -- data struct, http channel and cookie service.
 [Data struct]
   1. netwerk/cookie/DocumentCookieOperation.h
   2. netwerk/cookie/DocumentCookieoperation.cpp
      * Define DocumentCookieInfo、 DocumentCookieList and DocumentCookieMap.
      * Define the function about updating, destroy and get cookie from hash table.
      * Define GetBaseDomain and GetBaseDomainFromHost.
   3. netwerk/cookie/nsCookieKey
      * Define the key on DocumentCookieMap and DBState.
   4. netwerk/ipc/NeckoChannelParams.ipdlh
      * Create new ipdl strcut which naming cookie struct.

Would you give me your suggestions?
Thanks!
Attachment #8862117 - Attachment is obsolete: true
Attachment #8862117 - Flags: feedback?(josh)
Attachment #8862378 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8862119 - Attachment description: implementation part3 --- Destroy cookie → implementation part5 --- Destroy cookie
(Assignee)

Comment 59

2 months ago
Created attachment 8862380 [details] [diff] [review]
implementation part3 -- http channel

Hi Josh,
My modification as below:
[HttpChannel]
   1. netwerk/protocol/http/HttpChannelChild.h
      * Add new argument cookiesList on RecvOnStartRequest()
   2. netwerk/protocol/http/HttpChannelChild.cpp
      * If cookiesList which in  RecvOnStartRequest() is not empty, call Init hash table from CookieServiceChild.
   3. netwerk/protocol/http/HttpChannelParent.cpp
      * Add a expression to confirm the cookies which is matching cookie.
   4. netwerk/protocol/http/PHttpChannel.ipdl
      * Add a argument cookiesList on OnStartRequest().

Would you give me your suggestions?
Thanks!
Attachment #8862380 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8862380 - Attachment description: implementation part2 -- http channel → implementation part3 -- http channel
(Assignee)

Comment 60

2 months ago
Created attachment 8862382 [details] [diff] [review]
implementation part4 -- cookie service

Hi Josh,
My modification as below:
 [CookieService]
   1. netwerk/cookie/CookieServiceChild.cpp
      * Init hash table
      * When recv the ipc msg about have to update cookie in hash table,
        call DocumentCookieOperation->UpdateHashTable()
      * Remove SendGetCookieStringInternal, use DocumentCookieOperation->GetCookieStringFromHashTable() instead.
   2. netwerk/cookie/CookieServiceChild.h
      * Create DocumentCookieOperation object naming mDocCookieOperation.
   3. netwerk/cookie/CookieServiceParent.cpp
      * Add observer on Constructor.
      * Create Observer for processing the notifies which already registered.
        * cookie-changed
        * private-cookie-changed
        * init-hash-table
        * destroy-hash-table
      * Remove observer on ActorDestroy()
   4. netwerk/cookie/CookieServiceParent.h
      * Inherit nsIObserver
      * Create DocumentCookieOperation object naming mDocCookieOperation.
   5. netwerk/cookie/nsCookieService.cpp
      * Create GetCookieStructList & GetCookieStructListInternal.
      * Remove GeBaseDomain & GetBaseDomainFromHost.
      * Create ConfirmMatchingCookie
      * Create new notify "init-hash-table" and "destroy-hash-table"
   6. netwerk/cookie/nsCookieService.h
      * Remove nsCookieKey class.
      * Remove mTLDService.
      * Create DocumentCookieOperation object naming mDocCookieOperation for calling GetBaseDomain.
   7. netwerk/cookie/moz.build
      * Add DocumentCookieOperation & nsCookieKey
   8. netwerk/ipc/NeckoParent.cpp
      * Use AddRef when calling CookieServiceParent Constructor
      * Use Release when calling CookieServiceParent Destructor

Would you give me your suggestions?
Thanks!
Attachment #8862382 - Flags: feedback?(josh)
(Assignee)

Comment 61

2 months ago
Created attachment 8862384 [details] [diff] [review]
implementation -- full version
Attachment #8862121 - Attachment is obsolete: true

Comment 62

2 months ago
Comment on attachment 8862104 [details] [diff] [review]
implementation part1--create & set load flag.

Review of attachment 8862104 [details] [diff] [review]:
-----------------------------------------------------------------

::: docshell/base/nsDocShell.cpp
@@ -1,1 @@
> -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */

nit: revert this.

@@ +8587,5 @@
>  
>  } // namespace
>  
> +void
> +nsDocShell::ConfirmSandBoxedForSettingLoadFlags(nsLoadFlags &aLoadFlags)

Let's call this `RequireCookiesIfNecessary`.

@@ +8599,5 @@
> +      sandboxFlags = doc->GetSandboxFlags();
> +    }
> +  }
> +
> +  if (sandboxFlags & SANDBOXED_ORIGIN) {

We need to set the LOAD_DOCUMENT_NEEDS_COOKIE flag if:
* there are no sandbox flags (ie. SANDBOXED_NONE), or
* there are sandbox flags, but both SANDBOXED_ORIGIN and SANDBOXED_SCRIPT are not present (ie. the sandboxed iframe is not using a unique origin, and it is allowed to execute JS)

::: dom/html/nsHTMLDocument.cpp
@@ +2410,5 @@
>      channel->GetLoadFlags(&loadFlags);
>      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> +    // If sandboxed flags included SANBOXED_ORIGIN,
> +    // add LOAD_DOCUMENT_NEEDS_COOKIE to load flag
> +    if (mSandboxFlags & SANDBOXED_ORIGIN) {

This check will need to match the one in nsDocShell.cpp.

::: netwerk/base/nsIRequest.idl
@@ +131,5 @@
>       */
>      const unsigned long LOAD_HTML_OBJECT_DATA = 1 << 1;
>  
> +    /**
> +     * TODO: add comment to explain the function of this flag.

"This flag marks the request as belonging to a document that requires access to the document.cookies API."
Attachment #8862104 - Flags: feedback?(josh) → feedback-

Comment 63

2 months ago
Testcases that we need to verify work after these changes:
* the response for a document provides cookies; the page can observe those cookies using document.cookies
* the response for a document provides cookies, including some that are httponly; the page cannot observe the httponly cookies using document.cookies
* a page sets document.cookies then immediately gets the value of document.cookies; the new cookies should be observed
* a page sets cookies, then loads a sandboxed iframe that does not use allow-same-origin but uses allow-scripts; document.cookies in the iframe should not observe the original cookies, and setting document.cookies in the iframe should not be observable in the original page
* a page sets cookies, then loads a sandboxed iframe that uses allow-same-origin; document.cookies in the iframe should observe the original cookies, and setting document.cookies in the iframe should be observable in the original page
* a page is loaded with no cookies present, then performs a same-origin XHR that provides a response with cookies; document.cookies should observe the new cookies after the XHR is complete
* a page is loaded and has cookies set, then loads a same-origin iframe with sets more cookies, then removes the iframe; all of the cookies that were set are still visible from the original page using document.cookies

Reading through these patches, I am concerned that many of these testcases look like they will fail in the current implementation.

Comment 64

2 months ago
One other question - does this patch pass tests on the tryserver right now?
Flags: needinfo?(amchung)
(Assignee)

Comment 65

2 months ago
No, I don't push my patch to try server yet.
I will test my patch on try server after finish the test cases.
Thanks!
Flags: needinfo?(amchung)
(Assignee)

Comment 66

2 months ago
Hi Josh,
I have pushed my patch to try server, I found some problems as below:
1. CookieServiceChild didn’t verify some rules about the cookie can save to db when server sets cookies.
    i.  Separate the part of confirming the cookies which match the rules to other static public function in nsCookieService, maybe naming ConfirmCookieCanSave()
    ii.  Use CookieStruct to replace nsCookieAttributes
    iii. CookieServiceChild update the cookie to hash table after call ConfirmCookieCanSave(). 
2. CookieServiceChild didn’t verify the cookie whether expired when server gets cookies.
    i. Confirm the expiry time.

Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
(Assignee)

Comment 67

2 months ago
Created attachment 8865288 [details] [diff] [review]
implementation part1 -- created loadflags.

Hi,
I have modified my patch from Josh's suggestions and pass the test cases.
My modification as below:
1. Rename ConfirmSandBoxedForSettingLoadFlags to RequireCookiesIfNecessary.
2. Modified the rules of setting LOAD_DOCUMENT_NEEDS_COOKIE.
3. Added the comment of LOAD_DOCUMENT_NEEDS_COOKIE on nsIRequest.idl.

Would you give me your suggestion?
Thanks!
Attachment #8862104 - Attachment is obsolete: true
Attachment #8865288 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8865288 - Flags: feedback?(juhsu)
(Assignee)

Comment 68

2 months ago
Created attachment 8865290 [details] [diff] [review]
implementation part2 -- data struct

Hi,
I have tested my testing web page and modified my patch as below:
[DocumentCookieInfo]
1. Modified the field of DocumentCookieInfo() to CookieStruct.
2. Added a constructor on DocuemntCookieInfo().

[DocumentCookieOperation]
1. Added a OperationFlag.
2. Modified UpdateCookieToTable.
3. Modified DestroyCookieFromTable

Would you give me your suggestions?
Thanks!
Attachment #8862378 - Attachment is obsolete: true
Attachment #8862378 - Flags: feedback?(josh)
Attachment #8865290 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8865290 - Flags: feedback?(juhsu)
(Assignee)

Comment 69

2 months ago
Created attachment 8865291 [details] [diff] [review]
implementation part4 -- http channel

Hi,
I changed this patch from part 3 to part 4, and the code doesn't modify anything.
The last modifications as below:
[HttpChannel]
   1. netwerk/protocol/http/HttpChannelChild.h
      * Add new argument cookiesList on RecvOnStartRequest()
   2. netwerk/protocol/http/HttpChannelChild.cpp
      * If cookiesList which in  RecvOnStartRequest() is not empty, call Init hash table from CookieServiceChild.
   3. netwerk/protocol/http/HttpChannelParent.cpp
      * Add a expression to confirm the cookies which is matching cookie.
   4. netwerk/protocol/http/PHttpChannel.ipdl
      * Add a argument cookiesList on OnStartRequest().

Would you give me your suggestions?
Attachment #8862382 - Attachment is obsolete: true
Attachment #8862382 - Flags: feedback?(josh)
Attachment #8865291 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8865291 - Flags: feedback?(juhsu)
(Assignee)

Comment 70

2 months ago
Created attachment 8865293 [details] [diff] [review]
implementation part5 -- Destroy cookie

Hi,
The modifications as below:
1. Moved the parts of Implement xpcom interface DestroyHashTableFromDocument to part3 patch.
2. docshell/base/nsDocShell.cpp
    * Call Destroy hash table when this object be released.
   dom/html/nsHTMLDocument.cpp
    * Call Destroy hash table when this object be released.

Would you give me your suggestion?
Thanks!
Attachment #8862119 - Attachment is obsolete: true
Attachment #8862119 - Flags: feedback?(josh)
Attachment #8865293 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8865293 - Attachment description: implementation part5 --- Destroy cookie → implementation part5 -- Destroy cookie
Attachment #8865293 - Flags: feedback?(juhsu)

Comment 71

2 months ago
Comment on attachment 8865288 [details] [diff] [review]
implementation part1 -- created loadflags.

Review of attachment 8865288 [details] [diff] [review]:
-----------------------------------------------------------------

You have an unnecessary warning message at the beginning of your patch.

::: docshell/base/nsDocShell.cpp
@@ +8601,5 @@
> +  }
> +
> +  if (sandboxFlags & SANDBOXED_NONE ||
> +      (! (sandboxFlags & SANDBOXED_ORIGIN) &&
> +       ! (sandboxFlags & SANDBOXED_SCRIPTS))) {

nit: no space after !

::: dom/html/nsHTMLDocument.cpp
@@ +2409,5 @@
>      nsLoadFlags loadFlags = 0;
>      channel->GetLoadFlags(&loadFlags);
>      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> +    // If sandboxed flags included SANBOXED_ORIGIN,
> +    // add LOAD_DOCUMENT_NEEDS_COOKIE to load flag

Change the annotation to match your code, or remove it if you think it is unnecessary.

@@ +2411,5 @@
>      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> +    // If sandboxed flags included SANBOXED_ORIGIN,
> +    // add LOAD_DOCUMENT_NEEDS_COOKIE to load flag
> +    if (mSandboxFlags & SANDBOXED_NONE ||
> +        (!(mSandboxFlags &SANDBOXED_ORIGIN) &&

nit: space after &
Attachment #8865288 - Flags: feedback?(juhsu) → feedback+

Comment 72

2 months ago
Comment on attachment 8865290 [details] [diff] [review]
implementation part2 -- data struct

Review of attachment 8865290 [details] [diff] [review]:
-----------------------------------------------------------------

I'd like to see another round

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +72,5 @@
> +// 'co.uk', or the empty string, aBaseDomain will be the exact host, and a
> +// leading dot will be treated as an error.
> +nsresult
> +DocumentCookieOperation::GetBaseDomainFromHost(const nsACString &aHost,
> +                                         nsCString        &aBaseDomain)

nit: indent

@@ +85,5 @@
> +  // get the base domain. this will fail if the host contains a leading dot,
> +  // more than one trailing dot, or is otherwise malformed.
> +  nsresult rv = mTLDService->GetBaseDomainFromHost(Substring(aHost, domain), 0, aBaseDomain);
> +  if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
> +    rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {

nit: indent

@@ +127,5 @@
> +                                                      nsAutoCString          &aCookieString)
> +{
> +  DocumentCookieInfoList *docCookieInfoList;
> +  DocumentCookieInfo *docCookieInfo;
> +  // Create nsCookieKey

remove this annotation

@@ +130,5 @@
> +  DocumentCookieInfo *docCookieInfo;
> +  // Create nsCookieKey
> +  nsCookieKey key(aBaseDomain, aOriginAttrs);
> +  aDocCookieMap.Get(key, &docCookieInfoList);
> +  if (docCookieInfoList) {

bail-out first,
i.e.,
if (!docCookieInfoList) {
  return true;
}

@@ +184,5 @@
> +          docCookieInfo->mCookieStruct.lastAccessed() = aCookieStruct.lastAccessed();
> +          docCookieInfo->mCookieStruct.creationTime() = aCookieStruct.creationTime();
> +          docCookieInfo->mCookieStruct.isSession() = aCookieStruct.isSession();
> +          docCookieInfo->mCookieStruct.isSecure() = aCookieStruct.isSecure();
> +          docCookieInfo->mCookieStruct.isHttpOnly() = aCookieStruct.isHttpOnly();

could we simply use operation= ?

@@ +216,5 @@
> +DocumentCookieOperation::DestroyCookieFromTable(DocumentCookieMap &aDocCookieMap,
> +                                                nsIURI            *aHostURI,
> +                                                const OriginAttributes  &aOriginAttrs)
> +{
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;

sync the style for the initial behavior
I suggest set them to nullptr like this

@@ +227,5 @@
> +  nsCookieKey key(baseDomain, aOriginAttrs);
> +  aDocCookieMap.Get(key, &docCookieInfoList);
> +  if (docCookieInfoList) {
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = &(docCookieInfoList->ElementAt(i));

move the declaration here

@@ +240,5 @@
> +        }
> +      }
> +   }
> +  } else if (!docCookieInfoList) {
> +    return false;

bail-out first

@@ +251,5 @@
> +  }
> +  return true;
> +}
> +} //net
> +} //mozilla

nit: space after //

::: netwerk/cookie/DocumentCookieOperation.h
@@ +16,5 @@
> +{
> +  DocumentCookieInfo()
> +  {}
> +
> +  DocumentCookieInfo(CookieStruct aCookieStruct)

any reason to call-by-value?

@@ +27,5 @@
> +     mCookieStruct.lastAccessed() = aCookieStruct.lastAccessed();
> +     mCookieStruct.creationTime() = aCookieStruct.creationTime();
> +     mCookieStruct.isSession() = aCookieStruct.isSession();
> +     mCookieStruct.isSecure() = aCookieStruct.isSecure();
> +     mCookieStruct.isHttpOnly() = aCookieStruct.isHttpOnly();

could we simply use operation= ?

@@ +29,5 @@
> +     mCookieStruct.isSession() = aCookieStruct.isSession();
> +     mCookieStruct.isSecure() = aCookieStruct.isSecure();
> +     mCookieStruct.isHttpOnly() = aCookieStruct.isHttpOnly();
> +  }
> +public:

It's a struct, default public

@@ +45,5 @@
> +  nsCOMPtr<nsIEffectiveTLDService> mTLDService;
> +  DocumentCookieOperation();
> +  NS_INLINE_DECL_REFCOUNTING(DocumentCookieOperation);
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;

possible a line-break here
I had hard time to find the definition of DocumentCookieMap

@@ +48,5 @@
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;
> +  bool UpdateCookieToTable(DocumentCookieMap &aDocCookieMap, const CookieStruct &aCookieStruct, const OriginAttributes &aOriginAttrs, OperationFlag aOperationFlag);
> +  bool DestroyCookieFromTable(DocumentCookieMap &aDocCookieMap, nsIURI *aHostURI, const OriginAttributes &aOriginAttrs);
> +  nsresult GetCookieStringFromHashTable(DocumentCookieMap &aDocCookieMap, const nsCString aBaseDomain, const OriginAttributes &aOriginAttrs, nsAutoCString &aCookieString);

nit: line-break for those with length apparently larger than 80
nit: &aBaseDomain
nit: GetCookieStringFromTable

@@ +58,5 @@
> +};
> +} //net
> +} //mozilla
> +
> +#endif //DocumentCookieStruct_h

nit: space after // for the three

::: netwerk/cookie/nsCookieKey.h
@@ +4,5 @@
> +#include "nsTHashtable.h"
> +
> +namespace mozilla {
> +namespace net {
> +class nsCookieKey : public PLDHashEntryHdr

You move the code from nsCookieService.h
Keep all the change of movement (i.e, those removal of nsCookieKey in nsCookieService.h) in the same patch.

Also the #include "nsCookieKey.h" to make this patch able to pass the build

@@ +66,5 @@
> +
> +} // net
> +} // mozilla
> +
> +#endif //mozilla_net_nsCookieKey_h

nit: space after //
Attachment #8865290 - Flags: feedback?(juhsu) → feedback-

Comment 73

2 months ago
Comment on attachment 8865291 [details] [diff] [review]
implementation part4 -- http channel

Review of attachment 8865291 [details] [diff] [review]:
-----------------------------------------------------------------

Generally good.
Put some nits first and hold the f+ until part 3 uploaded since I need to know the changes of nsCookieService

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +49,5 @@
>  #include "nsIDOMWindowUtils.h"
>  #include "nsIEventTarget.h"
>  #include "nsStreamUtils.h"
>  #include "nsThreadUtils.h"
> +#include "mozilla/net/DocumentCookieOperation.h"

I know it's not alphabetical now, but I'll suggest to align with those "mozilla/net/*"

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1228,5 @@
> +  // Confirm it's document.cookie
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {
> +    nsCOMPtr<nsIURI> newURI;
> +    chan->GetURI(getter_AddRefs(newURI));
> +    //Get nsCookieService

remove this

@@ +1233,5 @@
> +    RefPtr<nsCookieService> cs = static_cast<nsCookieService *>(mHttpHandler->GetCookieService());
> +    NS_ASSERTION(mCookieService, "couldn't get nsICookieService");
> +    OriginAttributes attrs;
> +    NS_GetOriginAttributes(chan, attrs);
> +    nsCOMPtr<mozIThirdPartyUtil>  thirdPartyUtil;

nit: one space

@@ +1245,5 @@
> +    // Confirm the cookies which match the conditions to save to hash table.
> +    for (uint32_t i = 0; i < cookiesList.Length(); i++) {
> +      if (cs->ConfirmMatchingCookie(nullptr, newURI, &(cookiesList[i]), chan)) {
> +         if (mIPCClosed) {
> +           return NS_ERROR_UNEXPECTED;

move the logic above the loop
Attachment #8865291 - Flags: feedback?(juhsu)

Comment 74

2 months ago
Comment on attachment 8865288 [details] [diff] [review]
implementation part1 -- created loadflags.

Review of attachment 8865288 [details] [diff] [review]:
-----------------------------------------------------------------

::: docshell/base/nsDocShell.cpp
@@ +8599,5 @@
> +      sandboxFlags = doc->GetSandboxFlags();
> +    }
> +  }
> +
> +  if (sandboxFlags & SANDBOXED_NONE ||

SANDBOXED_NONE is 0x0, so this condition will never be true. I guess it's unnecessary, since the other conditions will be true if there are no sandboxing flags present.

::: dom/html/nsHTMLDocument.cpp
@@ +2410,5 @@
>      channel->GetLoadFlags(&loadFlags);
>      loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
> +    // If sandboxed flags included SANBOXED_ORIGIN,
> +    // add LOAD_DOCUMENT_NEEDS_COOKIE to load flag
> +    if (mSandboxFlags & SANDBOXED_NONE ||

Could we add a function that encapsulates this check that both this code and nsDocShell could use?
Attachment #8865288 - Flags: feedback?(josh) → feedback+

Comment 75

2 months ago
Comment on attachment 8865290 [details] [diff] [review]
implementation part2 -- data struct

Review of attachment 8865290 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/DocumentCookieOperation.h
@@ +31,5 @@
> +     mCookieStruct.isHttpOnly() = aCookieStruct.isHttpOnly();
> +  }
> +public:
> +  CookieStruct mCookieStruct;
> +  nsrefcnt mRefDocCnt;

This seems like we're merging two different concepts here. A CookieStruct represents a single cookie. The reference count is supposed to be the count of the number of documents that need access to all of the cookies for a particular domain. I would not expect both of these values to be part of the same struct.

Comment 76

2 months ago
Comment on attachment 8865291 [details] [diff] [review]
implementation part4 -- http channel

Review of attachment 8865291 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1240,5 @@
> +    thirdPartyUtil->IsThirdPartyChannel(chan, newURI, &isForeign);
> +    bool isPrivate = chan && NS_UsePrivateBrowsing(chan);
> +    nsTArray<CookieStruct> cookiesList;
> +    // Get cookie struct list
> +    cs->GetCookieStructList(newURI, isForeign, false, attrs, isPrivate, cookiesList);

I suspect that this implementation does not properly separate private and non-private cookies. I can't confirm that right now because the changes to the cookie service are missing.

Comment 77

2 months ago
Comment on attachment 8865293 [details] [diff] [review]
implementation part5 -- Destroy cookie

Review of attachment 8865293 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/html/nsHTMLDocument.cpp
@@ +189,5 @@
>  nsHTMLDocument::~nsHTMLDocument()
>  {
> +  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
> +  if (service && mDocumentURI && mChannel) {
> +    service->DestroyHashTableFromDocument(mDocumentURI, mChannel);

We probably need to call this with mOriginalURI too. We should have a testcase that uses pushState() so that the URLs are different; when the document is destroyed then it should still end up removing the reference count for the appropriate cookie domain.
(Assignee)

Comment 78

2 months ago
Created attachment 8865993 [details] [diff] [review]
test case -- part6

Hi,
This test cases can divide to four parts as below:
1. document.cookie
   * Created observer, and added "cookie-changed" to observer.
   * Set cookie by "document.cookie"; the page can observe those cookies using document.cookies
   * Set cookie which includes "httponly" by "document.cookie"; the page cannot observe the httponly cookies using document.cookies.
   * Set document.cookies then immediately gets the value of document.cookies; the new cookies should be observed.
2. xhr
   * A page is loaded with no cookies present, then performs a same-origin XHR that provides a response with cookies; document.cookies should observe the new cookies after the XHR is complete.
3. iframe
   * Load a sandboxed iframe which does not use allow-same-origin but uses allow-scripts; document.cookies in the iframe should not observe.
   * Load a sandboxed iframe that uses allow-same-origin; document.cookies in the iframe should observe.
   * Loads a same-origin iframe with sets more cookies, then removes the iframe; all of the cookies that were set are still visible from the original page using document.cookies.
4. clear all cookies
   * Confirmed the observer can get the deleted notify when clearing all cookies.

Would you give me your suggestions?
Thanks!
Attachment #8865993 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8865993 - Flags: feedback?(juhsu)
(Assignee)

Updated

2 months ago
Attachment #8862380 - Flags: feedback?(josh)

Comment 79

2 months ago
Comment on attachment 8865993 [details] [diff] [review]
test case -- part6

Review of attachment 8865993 [details] [diff] [review]:
-----------------------------------------------------------------

I'm not through all the patch.
f- because of lack of decent comment and not covering all the cases in comment 63

::: netwerk/cookie/test/browser/browser.ini
@@ +6,5 @@
> +  test_1331680.html
> +  test_1331680_iframe.html
> +  user_agent.sjs
> +  test_1331680_xhr.html
> +  test_1331680_clear_cookies.html

alphabetical

::: netwerk/cookie/test/browser/browser_1331680.js
@@ +13,5 @@
> +  "http://example.com/browser/netwerk/cookie/test/browser/test_1331680_iframe.html";
> +const TEST_CLEAR_COOKIES_URL =
> +  "http://example.com/browser/netwerk/cookie/test/browser/test_1331680_clear_cookies.html";
> +
> +let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);

is it unused?

@@ +16,5 @@
> +
> +let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
> +var testNum = 0;
> +var numOfCookies = 0;
> +var testItem;

nit: I suggest g- prefix for these global variables.
e.g., gTestNum

@@ +107,5 @@
> +    }
> +  }
> +};
> +
> +// opens `uri' in a new tab with the provided userContextId and focuses it.

Fix the annotation since you remove the userContextId parameter.
Also the function name and the following annotation

@@ +128,5 @@
> +function* test_set_cookie() {
> +  let {tab, browser} = yield* openTabInUserContext(TEST_URL);
> +  gBrowser.removeTab(tab);
> +}
> +

So, IIUC
The flow is
1) load the TEST_URL to the new tab
2) set document.cookie in TEST_URL
3) observed "cookie-changed" and check the cookie
4) close the tab and continue to next tab

Is it guarantee that 4) will triggered after 3)?
i.e., cookie-changed is sent sync

If not, I'm afraid of intermittent since the order would be changed.

@@ +132,5 @@
> +
> +function* test_xhr() {
> +  let {tab, browser} = yield* openTabInUserContext(TEST_XHR_URL);
> +  gBrowser.removeTab(tab);
> +}

Also, after the TEST_XHR_URL is loaded, the xhr is sent.
However, response is possibly on-the-air and close the tab.

@@ +137,5 @@
> +
> +function* confirm_iframe_cookies(cookieString, cookieName) {
> +  var if_name = cookieName + "=";
> +  var ca = cookieString.split(';');
> +  for(var i = 0; i <ca.length; i++) {

nit: space after |for| and <

@@ +168,5 @@
> +  gBrowser.removeTab(tab);
> +}
> +
> +add_task(function* test() {
> +  waitForExplicitFinish();

Seems |yield| already doing this.

@@ +169,5 @@
> +}
> +
> +add_task(function* test() {
> +  waitForExplicitFinish();
> +  // Add observer

Remove this

@@ +172,5 @@
> +  waitForExplicitFinish();
> +  // Add observer
> +  Services.obs.addObserver(observer, "cookie-changed");
> +
> +  // Start to test setting cookie.

The annotation should be informative.
And it's not the case for
 "the response for a document provides cookies; the page can observe those cookies using document.cookies"
The cookie here is from document.cookie instead of HTTP response

This annotation rule also applied to the following annotations.
I'd like to understand what you want to test after I read the annotation.

@@ +176,5 @@
> +  // Start to test setting cookie.
> +  testItem = TestItemsEnum.SetCookie;
> +  yield test_set_cookie();
> +
> +  // Start to test XHR ( set response header)

nit: no space after (

@@ +182,5 @@
> +  yield test_xhr();
> +
> +  // Start to test setting iframes.
> +  testItem = TestItemsEnum.Iframe;
> +  // Rest the number of tests.

ambiguous comment

@@ +192,5 @@
> +  testNum = 0;
> +  yield clear_cookies();
> +
> +  is(numOfCookies, 0, "Clear all cookies");
> +  // Remove observer.

remove this

::: netwerk/cookie/test/browser/test_1331680.html
@@ +1,4 @@
> +<!DOCTYPE HTML>
> +<html>
> +<!--
> +https://bugzilla.mozilla.org/show_bug.cgi?id=643051

nit: change to correct bug number

@@ +8,5 @@
> +  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
> +  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
> +</head>
> +<body>
> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1331680">Mozilla Bug 643051</a>

same here

::: netwerk/cookie/test/browser/test_1331680_iframe.html
@@ +1,4 @@
> +<!DOCTYPE HTML>
> +<html>
> +<!--
> +https://bugzilla.mozilla.org/show_bug.cgi?id=643051

same here

@@ +8,5 @@
> +  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
> +  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
> +</head>
> +<body>
> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1331680">Mozilla Bug 643051</a>

same here

@@ +14,5 @@
> +<div id="content" style="display: none">
> +<script type="application/javascript">
> +const ID = ["if_1", "if_2"];
> +function create_iframe(id, src, sandbox_flags) {
> +   var iframeEl = null;

nit: two space indentation for this file

::: netwerk/cookie/test/browser/user_agent.sjs
@@ +1,4 @@
> +
> +function handleRequest(request, response)
> +{
> +    // avoid confusing cache behaviors

same here
Attachment #8865993 - Flags: feedback?(juhsu) → feedback-

Comment 80

2 months ago
Comment on attachment 8865293 [details] [diff] [review]
implementation part5 -- Destroy cookie

Review of attachment 8865293 [details] [diff] [review]:
-----------------------------------------------------------------

Suspend the feedback process for the similar reason with comment 73

::: docshell/base/nsDocShell.cpp
@@ +5794,5 @@
>                 "Unexpected item type in docshell");
>  
> +  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
> +  nsIChannel *channel = GetCurrentDocChannel();
> +  if (service && mLoadingURI && channel ) {

nit: no space before )
Attachment #8865293 - Flags: feedback?(juhsu)
(Assignee)

Updated

2 months ago
Attachment #8862380 - Attachment is obsolete: true
(Assignee)

Comment 81

2 months ago
Created attachment 8866439 [details] [diff] [review]
implementation part1 -- created loadflags.

Hi,
I have modified my patch from your suggestions as below:
1. Fixed nit.
2. Set RequireCookiesIfNecessary() to static for nsHTMLDocument can use.

Would you review my patch?
Thanks!
Attachment #8865288 - Attachment is obsolete: true
Attachment #8866439 - Flags: review?(josh)
(Assignee)

Updated

2 months ago
Attachment #8866439 - Flags: review?(juhsu)
(Assignee)

Comment 82

2 months ago
Created attachment 8866442 [details] [diff] [review]
implementation part2 -- data struct

Hi,
I have modified this patch from your suggestions as below:
1. Fixed nit.
2. Removed nsCookieKey from nsCookieService.h on this patch.
3. Create a structure naming DocumentCookieListInfo which includes DocumentCookieInfoList and RefCount.

Would you give me your suggestions?
Thanks!
Attachment #8865290 - Attachment is obsolete: true
Attachment #8865290 - Flags: feedback?(josh)
Attachment #8866442 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8866442 - Flags: feedback?(juhsu)
(Assignee)

Comment 83

2 months ago
Created attachment 8866445 [details] [diff] [review]
implementation part3 --- cookie service (WIP patch)

Hi,
This patch is WIP patch, hope this patch can help you to review the part 4 patch clearer.
[Finish]
Confirm the setting rules of cookie on child.

[To Do]
Fixed fail tests on try server.
https://treeherder.mozilla.org/#/jobs?repo=try&revision=8ca9c855c71cf0479346fe22109a24d2068fce0c&selectedJob=97672928
Flags: needinfo?(josh)
(Assignee)

Updated

2 months ago
Attachment #8852155 - Attachment is obsolete: true
(Assignee)

Updated

2 months ago
Attachment #8852874 - Attachment is obsolete: true
(Assignee)

Updated

2 months ago
Attachment #8859465 - Attachment is obsolete: true
(Assignee)

Updated

2 months ago
Attachment #8862384 - Attachment is obsolete: true

Comment 84

2 months ago
Comment on attachment 8866439 [details] [diff] [review]
implementation part1 -- created loadflags.

Review of attachment 8866439 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good to me. Thanks!

::: docshell/base/nsDocShell.cpp
@@ +8599,5 @@
>  
> +void
> +nsDocShell::RequireCookiesIfNecessary(nsLoadFlags      &aLoadFlags,
> +                                      uint32_t         &aSandboxFlags,
> +                                      nsIContentViewer *aContentViewer)

const
Attachment #8866439 - Flags: review?(juhsu) → review+

Comment 85

2 months ago
Comment on attachment 8865993 [details] [diff] [review]
test case -- part6

Review of attachment 8865993 [details] [diff] [review]:
-----------------------------------------------------------------

This test is currently relying on observing cookie notifications that occur in the parent process. I've left comments about places where we need to verify that the effects are visible in the content process too.

::: netwerk/cookie/test/browser/browser_1331680.js
@@ +11,5 @@
> +  "http://example.com/browser/netwerk/cookie/test/browser/test_1331680_xhr.html";
> +const TEST_IFRAME_URL =
> +  "http://example.com/browser/netwerk/cookie/test/browser/test_1331680_iframe.html";
> +const TEST_CLEAR_COOKIES_URL =
> +  "http://example.com/browser/netwerk/cookie/test/browser/test_1331680_clear_cookies.html";

This file is missing.

@@ +21,5 @@
> +
> +function confirm_cookie(isUpdate, data, cookieName, confirmName) {
> +  if (isUpdate) {
> +    numOfCookies++;
> +    ok(true, data == "added");

is(data, "added");

@@ +23,5 @@
> +  if (isUpdate) {
> +    numOfCookies++;
> +    ok(true, data == "added");
> +  } else {
> +    if (numOfCookies > 0)

ok(numCookies > 0);

@@ +25,5 @@
> +    ok(true, data == "added");
> +  } else {
> +    if (numOfCookies > 0)
> +      numOfCookies--;
> +    ok(true, data == "deleted");

is(data, "deleted");

@@ +48,5 @@
> +}
> +
> +function result_iframe_cookie(data, cookie) {
> +  switch (testNum) {
> +    case 0:

I don't understand why we don't observe the first cookie being set. As far as I know, the cookie service should still be able to observe it.

::: netwerk/cookie/test/browser/file_iframe_allow_same_origin.html
@@ +1,5 @@
> +<!DOCTYPE html>
> +<html>
> +<script>
> +document.cookie = "if2_1=123";
> +document.cookie = "if2_2=123";

We should verify that document.cookie can observe these new cookies and communicate that to browser_1331680.js somehow.

::: netwerk/cookie/test/browser/file_iframe_allow_scripts.html
@@ +1,4 @@
> +<!DOCTYPE html>
> +<html>
> +<script>
> +document.cookie = "if1=123";

We should verify that document.cookie can observe this change and communicate that back to browser_1331680.js somehow.

::: netwerk/cookie/test/browser/test_1331680_iframe.html
@@ +15,5 @@
> +<script type="application/javascript">
> +const ID = ["if_1", "if_2"];
> +function create_iframe(id, src, sandbox_flags) {
> +   var iframeEl = null;
> +   iframeEl      = document.createElement("iframe");

Let's merge this with the previous line.

@@ +27,5 @@
> +function delete_iframes(id) {
> +   var ifr = document.getElementById(id);
> +   ifr.onload = function() {
> +      ifr.parentNode.removeChild(ifr);
> +      delete window.frames.id;

What is this delete supposed to do?

::: netwerk/cookie/test/browser/test_1331680_xhr.html
@@ +10,5 @@
> +<script type="text/javascript">
> +  var xhr = new XMLHttpRequest();
> +  xhr.open("GET", 'user_agent.sjs ', false); // sync request
> +  xhr.send();
> +</script>

We need to verify that document.cookie can see the cookie that was set in the response for the XHR. We will also need to communicate that result back to browser_1331680.js somehow.

::: netwerk/cookie/test/browser/user_agent.sjs
@@ +3,5 @@
> +{
> +    // avoid confusing cache behaviors
> +    response.setHeader("Cache-Control", "no-cache", false);
> +    response.setHeader("Content-Type", "text/html", false);
> +    response.setHeader("Set-Cookie", "test=123", false);

We should set a second cookie that is httponly as part of this test, and verify that test_1331680_xhr.html cannot observe it through document.cookies.

@@ +15,5 @@
> +            nav: navigator.userAgent\
> +          };\
> +          self.parent.postMessage(msg, '*');\
> +        </script>\
> +      </body></html>"

All of the bits that are copied from the previous file should be removed, since they are confusing when trying to understand this test.
Attachment #8865993 - Flags: feedback?(josh) → feedback-

Comment 86

2 months ago
Comment on attachment 8866439 [details] [diff] [review]
implementation part1 -- created loadflags.

Review of attachment 8866439 [details] [diff] [review]:
-----------------------------------------------------------------

::: docshell/base/nsDocShell.cpp
@@ +8604,5 @@
> +{
> +  if (aContentViewer) {
> +    nsCOMPtr<nsIDocument> doc = aContentViewer->GetDocument();
> +    if (doc) {
> +      aSandboxFlags = doc->GetSandboxFlags();

This overrides the sandbox flags from nsDocShell, which is scary.

@@ +11496,5 @@
>    (void)aChannel->GetLoadFlags(&loadFlags);
>    loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
>                 nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
>  
> +  RequireCookiesIfNecessary(loadFlags, mSandboxFlags, mContentViewer);

I propose we do this instead:
if (SandboxFlagsImplyCookies(mSandboxFlags)) {
    loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
}

where SandboxFlagsImplyCookies is just a small function that checks SANDBOXED_ORIGIN and SANDBOXED_SCRIPTS flags and returns a boolean.
Attachment #8866439 - Flags: review?(josh) → review-

Comment 87

2 months ago
Comment on attachment 8866442 [details] [diff] [review]
implementation part2 -- data struct

Review of attachment 8866442 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +1,3 @@
> +#include "DocumentCookieOperation.h"
> +#include "nsIEffectiveTLDService.h"
> +#include "nsCookieService.h"

Do we need nsCookieService here?

@@ +3,5 @@
> +#include "nsCookieService.h"
> +
> +using mozilla::OriginAttributes;
> +
> +

nit: one empty line only

@@ +168,5 @@
> +{
> +  DocumentCookieListInfo *docCookieListInfo = nullptr;
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +
> +  // Create nsCookieKey

remove this

@@ +226,5 @@
> +}
> +
> +bool
> +DocumentCookieOperation::DestroyCookieFromTable(DocumentCookieMap &aDocCookieMap,
> +                                                nsIURI            *aHostURI,

const

@@ +234,5 @@
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +  bool requireHostMatch;
> +  nsCString baseDomain;
> +  GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
> +  // Create nsCookieKey

nit: remove this

@@ +239,5 @@
> +  nsCookieKey key(baseDomain, aOriginAttrs);
> +  aDocCookieMap.Get(key, &docCookieListInfo);
> +  if (!docCookieListInfo) {
> +    return false;
> +  } else {

no need for |else|

::: netwerk/cookie/DocumentCookieOperation.h
@@ +5,5 @@
> +#include "nsHashKeys.h"
> +#include "nsTHashtable.h"
> +#include "nsCookieKey.h"
> +#include "nsAutoPtr.h"
> +#include "mozilla/net/NeckoChannelParams.h"

alphabetical please

@@ +17,5 @@
> +{
> +  DocumentCookieInfo()
> +  {}
> +
> +  DocumentCookieInfo(CookieStruct aCookieStruct)

|&aCookieStruct| if aCookieStruct is not possible to be nullptr
Sorry for not asking this before, is it able to use initialization list?
i.e., 
:mCookieStruct(aCookieStruct)

@@ +40,5 @@
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef
> +  nsClassHashtable<nsCookieKey, DocumentCookieListInfo> DocumentCookieMap;
> +
> +

nit: one empty line

@@ +52,5 @@
> +  DestroyCookieFromTable(DocumentCookieMap &aDocCookieMap, nsIURI *aHostURI,
> +                         const OriginAttributes &aOriginAttrs);
> +
> +  nsresult
> +  GetCookieStringFromHashTable(DocumentCookieMap &aDocCookieMap,

1. Any reason to use |HashTable| name here and |Table| in the previous two?
Make the same style if possible.

2. make the first argument const

@@ +66,5 @@
> +
> +  nsresult Init();
> +
> +protected:
> +  nsCOMPtr<nsIEffectiveTLDService> mTLDService;

I don't think of a reason to make it |protected|.
Correct me if I'm wrong

::: netwerk/cookie/nsCookieKey.h
@@ +35,5 @@
> +
> +  bool KeyEquals(KeyTypePointer other) const
> +  {
> +    return mBaseDomain == other->mBaseDomain &&
> +    mOriginAttributes == other->mOriginAttributes;

indentation

@@ +68,5 @@
> +} // net
> +} // mozilla
> +
> +#endif //mozilla_net_nsCookieKey_h
> +

nit: remove this empty line
nit: space after //

::: netwerk/ipc/NeckoChannelParams.ipdlh
@@ +189,5 @@
>    PFTPChannel;
>  };
>  
> +// For OnStartRequest Parent -> Child
> +

nit: remove the empty line
Attachment #8866442 - Flags: feedback?(juhsu) → feedback+
(Assignee)

Comment 88

2 months ago
Created attachment 8866619 [details] [diff] [review]
implementation part4 -- http channel

Hi,
I have modified this patch from your suggestions as below:
1. Fixed nit.
2. Removed isPrivate, because the latest nsCookieService already removed the Private checking.
   The patch of removing Private checking as below:
https://bug1284579.bmoattachments.org/attachment.cgi?id=8864007

Would you give me your suggestions?
Thanks!
Attachment #8865291 - Attachment is obsolete: true
Attachment #8865291 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8866619 - Flags: feedback?(josh)
(Assignee)

Updated

2 months ago
Attachment #8866619 - Flags: feedback?(juhsu)
(Assignee)

Comment 89

2 months ago
(In reply to Josh Matthews [:jdm] from comment #77)
> Comment on attachment 8865293 [details] [diff] [review]
> implementation part5 -- Destroy cookie
> 
> Review of attachment 8865293 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/html/nsHTMLDocument.cpp
> @@ +189,5 @@
> >  nsHTMLDocument::~nsHTMLDocument()
> >  {
> > +  nsCOMPtr<nsICookieService> service = do_GetService(NS_COOKIESERVICE_CONTRACTID);
> > +  if (service && mDocumentURI && mChannel) {
> > +    service->DestroyHashTableFromDocument(mDocumentURI, mChannel);
> 
> We probably need to call this with mOriginalURI too. We should have a
> testcase that uses pushState() so that the URLs are different; when the
> document is destroyed then it should still end up removing the reference
> count for the appropriate cookie domain.

Thanks, I will add a checking of OriginURI on this destructor and create a new test case for testing destroy function.

Comment 90

2 months ago
Comment on attachment 8866619 [details] [diff] [review]
implementation part4 -- http channel

Review of attachment 8866619 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +49,5 @@
>  #include "nsIDOMWindowUtils.h"
>  #include "nsIEventTarget.h"
>  #include "nsStreamUtils.h"
>  #include "nsThreadUtils.h"
> +#include "mozilla/net/DocumentCookieOperation.h"

move this under |#include "mozilla/net/DNS.h"|

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1225,5 @@
> +  mChannel->GetLoadFlags(&newLoadFlags);
> +  bool isDocument = false;
> +  chan->GetIsDocument(&isDocument);
> +  // Confirm it's document.cookie
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {

it's a little crowded in the following code snippet.
Please add appropriate line break

@@ +1240,5 @@
> +    nsTArray<CookieStruct> cookiesList;
> +    // Get cookie struct list
> +    cs->GetCookieStructList(newURI, isForeign, false, attrs, cookiesList);
> +    // Confirm the cookies which match the conditions to save to hash table.
> +    if (!mIPCClosed) {

is it expected of not calling |requestHead->Exit();|?
Attachment #8866619 - Flags: feedback?(juhsu) → feedback+
Whiteboard: [necko-active][platform-rel-Linkedin][qf:p1] → [necko-active][platform-rel-Linkedin][qf:p1][necko-quantum]
Sorry for the silence. I spent a bunch of time reading through the patches (especially part 2 and part 4) and re-reading comment 14 and comment 26. I then asked Ehsan and Michael Layzell a bunch of questions, and we realized that we need to make some changes to the design. I am going to explain the design that should address the problems; I am in the middle of writing it up and making sure I understand all the complicated parts before proposing that you implement it.
(Assignee)

Comment 92

a month ago
(In reply to Josh Matthews [:jdm] from comment #91)
> Sorry for the silence. I spent a bunch of time reading through the patches
> (especially part 2 and part 4) and re-reading comment 14 and comment 26. I
> then asked Ehsan and Michael Layzell a bunch of questions, and we realized
> that we need to make some changes to the design. I am going to explain the
> design that should address the problems; I am in the middle of writing it up
> and making sure I understand all the complicated parts before proposing that
> you implement it.

Thanks your help!
I have finished the test cases from comment 63 and fixed part2 and part3 patch.
I will upload the patches first, thanks!
(Assignee)

Comment 93

a month ago
Created attachment 8867881 [details] [diff] [review]
implementation part2 -- data struct

Hi,
I have modified as below:
1. Fixed the nits.
2. Added a variable names docListExist for deciding the DocumentCookieListInfo already puts to DocumentCookieMap or not.
3. Modified the function name GetCookieStringFromHashTable to GetCookieStringFromTable.

Would you give me your suggestions?
Thanks!
Attachment #8866442 - Attachment is obsolete: true
Attachment #8866442 - Flags: feedback?(josh)
Attachment #8867881 - Flags: feedback?(josh)
(Assignee)

Comment 94

a month ago
Created attachment 8867884 [details] [diff] [review]
implementation part1 -- created loadflags.

Hi,
I have modified this patch as below:
1. Modified the RequireCookiesIfNecessary to SandboxFlagsImplyCookies.
   i. Modified the arguments of this function to uint32_t aSandboxFlags.
   ii.Return boolean value when mSandboxFlags not set to SANDBOXED_ORIGIN and
      SANDBOXED_SCRIPTS, return true.

Would you help me to review my patch, thanks!
Attachment #8866439 - Attachment is obsolete: true
Attachment #8867884 - Flags: review?(josh)
(Assignee)

Comment 95

a month ago
Created attachment 8867888 [details] [diff] [review]
bug-1331680-cookie-wip-part3.patch

I have modified the WIP patch as below:
1. Added a new variable which names IsFromHttp on nsCookie.h.
   i.  Created a setting function names SetFromHttp().
   ii. Created a getting function names IsFromHttp().
2. Called nsCookie->SetFromHttp(aFromHttp) before the cookie which have to save to DB.
3. If the cookie which is from http, the CookieServiceParent have to notify the msg about the cookie must set to hash table to child.

[ToDo]
Fix the fail tests on try server.
(Assignee)

Comment 96

a month ago
Created attachment 8867889 [details] [diff] [review]
bug-1331680-channel-part4.patch

Hi,
I have fixed the patch as below:
1. Fixed nits.
2. Added "requestHead->Exit();" when the mIPCClosed is true.

Would you give me your suggestions?
Thanks!
Attachment #8866619 - Attachment is obsolete: true
Attachment #8866619 - Flags: feedback?(josh)
Attachment #8867889 - Flags: feedback?(josh)
(Assignee)

Comment 97

a month ago
Created attachment 8867893 [details] [diff] [review]
implementation part5 --- Destroy cookie

Hi,
I have modified the patch as below:
1. Added a condition "mOriginalURI" when nsHTMLDocument calls destructor.

Would you give me your suggestions?
Thanks!
Attachment #8865293 - Attachment is obsolete: true
Attachment #8865293 - Flags: feedback?(josh)
Attachment #8867893 - Flags: feedback?(josh)
(Assignee)

Comment 98

a month ago
Created attachment 8867898 [details] [diff] [review]
implementation part1 -- created loadflags.

Hi,
Sorry for the wrong patch file.
I have modified this patch as below:
1. Modified the RequireCookiesIfNecessary to SandboxFlagsImplyCookies.
   i. Modified the arguments of this function to uint32_t aSandboxFlags.
   ii.Return boolean value when mSandboxFlags not set to SANDBOXED_ORIGIN and
      SANDBOXED_SCRIPTS, return true.

Would you help me to review my patch, thanks!
Attachment #8867884 - Attachment is obsolete: true
Attachment #8867884 - Flags: review?(josh)
Attachment #8867898 - Flags: review?(josh)
(Assignee)

Comment 99

a month ago
Created attachment 8867902 [details] [diff] [review]
test cases part6

Hi,
[Modification]
I have modified the patch as below:
1. Modified browser mochitest to general mochtest.
2. Added a communication between the iframe (id=if2) and test_1331680.html.
   i.  Used postMessage on file_iframe_allow_same_origin.html.
   ii. Set Listener on test_1331680.html.
3. Used the "document.cookie" to compare the cookie string which be get from file_1331680.js on test_1331680.html after called xhr.
   i.  file_1331680.js be called by loadChromeScirpt().
   ii. This compare can confirm the cookie strings be get from parent and child are same.

[Can't Modify]
1. If sandbox flags of iframe only set to allow_scripts, can't set cookie.
   Because the domain of iframe is not same to parent page, the behavior of set cookie contravenes the same-origin policy.
2. The XHR can set the cookie which set http-only, because the cookie set cookie through SetCookieStringFromHttp() in parent process.

[To-Do]
1. Have to create a test case for destroy function.

Would you give me your suggestions?
Thanks!
Attachment #8865993 - Attachment is obsolete: true
Attachment #8867902 - Flags: feedback?(josh)
High level:

We want to remove the need for the content process to retrieve cookies from the parent process synchronously. We also do not want to duplicate all known cookies in each content process, because this is a privacy risk and it is unnecessary memory usage. We choose to send only the cookies that are accessible to a document right before that document is loaded. In order to avoid sending duplicate cookies we also track what documents are loaded in the parent process, so we only send cookies to the child when the child is loading a document and does not have cookies for that document's domain yet.

To simplify the model, the child process will need to notify the parent when a document is destroyed (nsDocument::Destroy), as well as when a document is created in a way that does not interact with the parent (such as about:blank (which inherits its principal from the document which created it, blob URIs) and navigations intercepted by a ServiceWorker). The parent will track the set of active domains for a particular content process, and when there are no documents in existence for a known domain the parent will instruct the content process to remove all cookies for that domain. 


Now, the low-level details:

We need two separate data structures; one for the child, and one for both the parent and child. The child will store a hashtable keyed on nsCookieKey with nsTArray<RefPtr<nsCookie>> values; this will be a member of CookieServiceChild.

The other data structure is a hashtable keyed on a string and stores nsTArray<DocumentCookieInfo> values, where DocumentCookieInfo looks like:
struct DocumentCookieInfo {
  nsCString mHostName;
  nsCString mPathName;
  bool mSecure;
  bool mCookiesPresent;
  nsrefcnt mActiveDocuments;
};

This second hashtable is a map of base domains, where each array of values is the list of documents loaded in the content process with a particular URL path. If the same document is loaded multiple times (ie. same base domain, and an entry exists in the array that contains identical mHostName, mSecure and mPathName values), the mActiveDocuments member will count how many instances of that document exist. This second hashtable will exist as a member in both CookieServiceParent and CookieServiceChild.

This means if a content process loads:
1) http://google.com/index.html
2) http://docs.google.com/
3) http://docs.google.com/
4) https://docs.google.com/
5) http://docs.google.com/error.html
6) http://developers.facebook.com/dev/welcome.html

then the hashtable would look like this:

{
"google.com" => [
  ("google.com", "index.html", false, 1),
  ("docs.google.com", "", false, 2),
  ("docs.google.com". "", true, 1),
  ("docs.google.com", "error.html”, false, 1)
],
"facebook.com" => [
  ("developers.facebook.com", "dev/welcome.html", false, 1)
],
}

Instead of HttpChannelParent::OnStartRequest, we will add the code that sends cookies to ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild. This code should use the ManagedPCookieServiceParent() API to retrieve the appropriate cookie service parent and call a method on that. Using the principal that is available in AboutToLoadHttpFtpWyciwygDocumentForChild, this CookieServiceParent method should:
* check if the channel is a document channel and has the LOAD_DOCUMENT_COOKIES load flag present
* check if there is a matching entry in the known documents hashtable
* if there is a matching entry, check if there is a matching DocumentCookieInfo structure for that entry
* if there is a matching structure, increment the number of active documents, and send a message to the content process with the principal, and no cookies.
* otherwise, add a new DocumentCookieInfo value to that entry, with an initial count of 1
* otherwise if there is no matching entry, insert a new hashtable entry containing a single DocumentCookieInfo value for the new document with an initial count of 1
* if a new value was added to the hashtable (either as part of a new entry or to an existing entry), retrieve a list of cookies that match the principal and send them to the child along with the principal using PCookieService. Cookies match the principal if the rules given in step 2 of comment 14 apply.

NOTE(mrl): We need to send a message even if the content process already has the cookies to make sure that the number of active documents is incremented.

When CookieServiceChild receives the message from the parent containing cookies and the document principal:
* check if there is a matching entry in the known documents hashtable
* if there is a matching entry, check if there is a matching DocumentCookieInfo structure for that entry
* if there is a matching structure, and mCookiesPresent is true, increment the number of active documents
* if there is a matching structure, and mCookiesPresent is false, set mCookiesPresent to true
* otherwise, add a new DocumentCookieInfo value to that entry, with an initial count of 1 and mCookiesPresent=true
* otherwise, if there is no matching entry, insert a new hash table entry containing a single DocumentCookieInfo value for the new document with an initial count of 1 and mCookiesPresent=true
* create an nsCookieKey from the principal, then create nsCookie values from the IPDL cookie values and add all of the provided cookies to the cookies hashtable, overwriting any existing matching cookies

In order to convert a principal into a hashtable string key, we should use the following steps:
* start with the principal's baseDomain 
* append the principal's originSuffix

When nsDocument::SetPrincipal is called, we need to do the following if we are running in the content process:
* if mChannel QIs to nsIHttpChannelInternal and nsIHttpChannelInternal::GetResponseSynthesized returns false, then 
   * Assert that a matching entry exists in the known document hashtable, with an mActiveDocuments value of at least 1
   * skip the remaining steps
* Otherwise call a method on CookieServiceChild that:
  * sends an IPDL message announcing a new document load, including the document's principal. This should be able to share code with the AboutToLoadHttpFtpWyciwygDocumentForChild codepath.
  * follow the same steps as when CookieServiceChild receives a message from the parent containing cookies - we need to update the known document hashtable with the new document. However, the new hashtable value should contain mCookiesPresent=false.

When nsDocument::Destroy is called and we are running in the content process, call a method on CookieServiceChild:
* retrieve an entry from the document info hashtable based on the document’s principal. Assert that this entry is present.
* using this entry, find values that match the document’s URL and original URL (if they are different)
* if the number of active documents is >1, decrease the count by 1
* otherwise, remove that DocumentCookieInfo value from the hashtable entry and purge appropriate cookies for that domain (see steps below)
* send an IPDL message to announce a document being destroyed, including the document's principal and the document’s URL and original URL (if different)
  * when the cookie service parent receives this message, it should find an existing entry in the document cookie info map using the principal from the message
  * using this entry, find values that match the URLs provided
  * if the value’s count is >1, it should be decreased by one. Otherwise the DocumentCookieInfo value should be removed.

In order to purge appropriate cookies for a domain:
* for each of the cookies that exist for the given domain
* for each value in the document hashtable entry for the given domain
* if the cookie path-matches against the value’s path, ignore the cookie
* if the cookie does not path-match against any of the known document paths, remove the cookie

In order for the content process to see updates to the cookie store after the initial response:
* make CookieServiceParent observe the cookie-changed and private-cookie-changed observer notifications
* when this notification occurs, compare the host and path of the modified cookie against the known document hashtable
* if there is no entry for the cookie’s host, ignore the cookie
* if the cookie does not domain-match or does not path-match against one of the DocumentCookieInfo values, ignore the cookie
* otherwise, send the cookie update information to the CookieServiceChild

When CookieServiceChild receives a cookie update:
* if there is no entry for the cookie’s host in the document hashtable, ignore the cookie
* if the cookie does not domain-match or does not path-match against one of the DocumentCookieInfo values, ignore the cookie
* otherwise, modify the stored cookie according to the update that was received

When CookieServiceChild::GetCookieStringInternal is invoked:
* if the network.cookie.ipc.sync preference (should default to false) is true, use the existing synchronous IPDL code path and return the result
* assert there is an entry in the known documents hashtable for the host URI
* if the entry has mCookiesPresent=false, use the existing synchronous IPDL code path and return the result
* otherwise, create an nsCookieKey value from the host URI and channel’s origin attributes and retrieve the set of cookies that need to be serialized

Since nsCookieService::AddInternal and nsCookieService::SetCookieInternal can fail in many interesting ways, we need to make sure that we cannot store cookies in the child that the parent will reject. We must move all of the checks in SetCookieInternal and AddInternal (except for the eviction code, which only makes in the parent) into code that can be shared between CookieServiceChild and nsCookieService. The cases that log a failure and immediately return without updating the cookie storage are the dangerous ones.

When CookieServiceChild::SetCookieStringInternal is invoked:
* if the network.cookie.ipc.sync preference is true, use the existing synchronous IPDL code path and return the result
* assert there is an entry in the known documents hashtable for the host URI
* if the entry in the known documents hashtable for the host URI has mCookiesPresent=false, use the existing synchronous IPDL code path and return the result
* check whether the cookie will result in updating the cookie storage (using the new shared code from nsCookieService)
* update the cookies hashtable with the new cookie values
* Send an async message to the parent process, notifying it of the cookie change.

To allow easier measurement of the impact of the sync IPC fallback code paths in SetCookieStringInternal and GetCookieStringInternal, extract them into separate Get/SetCookieStringInternalSyncIPC methods.

Additional testcases:
* ensure document has cookies available, then load about:blank in an iframe, and verify that document.cookies in the iframe can see the parent document’s cookies
* ensure a cross-origin domain has cookies present and a service worker registered. load an iframe that is intercepted by the service worker, and have the final document verify that document.cookies sees the original cookies.


Recommended implementation strategy:
* patch #1:
  - track documents in parent and child
  - document tracking data structure
  - nsDocument::SetPrincipal, nsDocument::Destroy (without cookie purging)
  - ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild (without sending cookies)
* patch #2:
  - sync IPC fallback changes in CookieServiceChild
  - at this point any e10s test using document.cookies should fail
* patch #3:
  - send matching cookies in ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild
  - purge matching cookies from nsDocument::Destroy
* patch #4:
  - shared code for nsCookieService/CookieServiceChild
  - make CookieServiceChild::SetCookieStringInternal reject cookies that would be rejected by the parent
* patch #5:
  - turn CookieServiceParent into an observer
  - send updates for cookie changes to child
* patch #6:
  - tests (be sure to verify that they pass network.cookie.ipc.sync=true, as well)

Please ask questions about anything that is not clear! I have attempted to cover all of the places I expect changes, but I may have forgotten or missed something.
Comment on attachment 8867881 [details] [diff] [review]
implementation part2 -- data struct

I am clearing the feedback requests on these patches given my previous comment. It should be much easier to review the work when it's split up in the order I suggested.
Attachment #8867881 - Flags: feedback?(josh)

Updated

a month ago
Attachment #8867893 - Flags: feedback?(josh)

Updated

a month ago
Attachment #8867889 - Flags: feedback?(josh)
(Assignee)

Comment 102

a month ago
Hi Josh,
Thanks for your suggestions about the design detail, and I have tried to write down the steps I understand and questions as below:
[Data Struct]
* Modified the DocumentCookieInfo.
* [CookieServiceChild]
    * Create nsTArray<RefPtr<nsCookie>> CookiesList.
    * Create a CookiesMap<nsCookieKey, CookiesList>

[Init hash table]
* [Parent Process]
    * Create a function names InitHashTableOnParent(nsIPrincipal) on CookieServiceParent.
    * If principal is available on ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild.
        * If the DocumentCookieMap exists the DocumentCookieInfo, the mActiveDocuments++
            * Because the CookieServiceParent doesn’t exist the entry, maybe only have to confirm the DocumentCookieInfo.
        * If the DocumentCookieInfo doesn’t exist, create a DocumentCookieInfo and let mActiveDocuments =1.
        * If the document have to update to DocumentCookieInfo, get cookies list and find the cookie which is the matching cookie then send to cookie service child.
* [Child Process]
    * If receive the nsCookie from parent.
        * If CookiesMap can get the CookiesLIst from nsCookieKey(nsIChannel.OriginAttributes, BaseDomain). && nsCookie.Name().equals(the nsCookie from CookiesList.Name())
            * If DomainMatch(nsCookie, DocumentCookieInfo.mHostName) && PathMatch(nsCookie, DocumentCookieInfo.mPathName)
                * If the DocumentCookieInfo->mCookiesPresent is true
                    * mActiveDocuments++
                * If the DocumentCookieInfo->mCookiesPresent is false
                    * Set mCookiesPresent to true.
                    * mActiveDocuments++
        * If not equal
            * Init a DocumentCookieInfo and set the domain & path from the entry.
            * mActiveDocuments = 1.
            * mCookiesPresent=true.
    * If not exist the entry.
        * Create a nsCookie and set the value from the cookie which sends from parent.
        * Create a DocumentCookieInfo and set the domain & path from the nsCookie.
            * mActiveDocuments = 1.
            * mCookiesPresent=true.
    * If exist the entry
        * Overwrite all cookies.
* [nsCookieKey]
    * The originAttributes from the principal.

[Remove]
* SetPrincipal
    * Query a nsIHttpChannelInternal from mChannel.
    * If nsIHttpChannelInternal::GetResponseSynthesized returns false
        * [CookieServiceParent]
            * If DocumentCookieInfo which get from nsCookieKey(nsIPrincipal.OriginAttributes, HostName)
                * If mActiveDocuments >= 1 in the existing DocumentCookieInfo which in CookieServiceParent.
                    * mActiveDocuments—
                    * If mActiveDocuments == 0
                        * Remove the DocumentCookieInfo.
            * Send a ipc msg to notify CookeServiceChild to remove the document.
                * [CookieServiceChild]
                * If mActiveDocuments >= 1in the existing DocumentCookieInfo which in CookieServiceChild.
                    * mActiveDocuments—
                    * If mActiveDocuments == 0
                        * Remove the DocumentCookieInfo.
    * Else If nsIHttpChannelInternal::GetResponseSynthesized returns true
        * Call the function InitHashTableOnParent(nsIPrincipal) on CookieServiceParent.
        * Same steps as above.
* Destroy
    * Create a function names DestroyDocumentFromHashTable(DocumentURI, OriginURI, nsIPrincipal) in CookieServiceParent.
        * If the DocumentCookieMap exists a DocumentCookieInfo whose domain equals DocumentURI or OriginURI
            * If mActiveDocuments > 1
                * mActiveDocuments—
            * If mActiveDocuments <= 1
                * Remove the DocumentCookieInfo.
            * Send the msg to CookieServiceChild.
    * Create a function names DestroyDocumentFromHashTable(DocumentURI, OriginURI, nsIPrincipal) in CookieServiceChild.
        * If get the msg about have to destroy document.
            * If the DocumentCookieMap exists a DocumentCookieInfo whose domain equals DocumentURI or OriginURI
                * If mActiveDocuments > 1
                    * mActiveDocuments—
                * If mActiveDocuments <= 1
                    * Remove the DocumentCookieInfo.
                * Purge appropriate cookies
                    * If the cookie’s domain equals the domain from DocumentURI or OriginURI.
                        * If PathMatch(cookie, the path from the DocumentCookieInfo) return false
                            * Remove the cookie.

[Update]
* [CookieServiceParent]
    * CookieServiceParent adds a observer for getting the “cookie-changed” or “private-cookie-changed” from nsCookieService.
        * If CookieServiceParent gets the notify which names “cookie-changed”  or “private-cookie-changed” 
            * If DomainMatches(nsCookie, DocumentCookieInfo->mHostNam) && PathMathes(nsCookie, DocumentCookieInfo->mPathName)
                * Send the nsCookie to CookieServiceChild.
      [CookieServiceChild]
    * CookieServiceChild gets the nsCookie form CookieServiceParent
        * If DomainMatches(nsCookie, DocumentCookieInfo->mHostNam) && PathMathes(nsCookie, DocumentCookieInfo->mPathName)
            * Store the nsCookie.
    * SetCookieString
        * If the network.cookie.ipc.sync preference is false
            * Get the nsIprincipal from loadInfo.
                * If DocumentCookieMap can get DocumentCookieInfo from nsCookieKey(nsIprincipal, GetBaseDomian(nsCookie->Host())).
                    * If mCookiesPresent=false
                        * Don’t store the nsCookie on CookieServiceChild.
                    * If mCookiesPresent=true
                        * If nsCookieService::SetCookieRules(cookieHeader, setCookie) and setCookie is true
                            * Store the nsCookie.
            * If the nsCookie which can’t store to cookie hashtable, the CookieServiceChild still have to send ipc msg to CookieServiceParent for saving the nsCookie to DB.
        * Else if the network.cookie.ipc.sync preference is true
            * CookieServiceChild have to send ipc msg to CookieServiceParent for saving the nsCookie to DB.

[Get Cookies]
*  [CookieServiceChild]
    * If the network.cookie.ipc.sync is true
        * Call the origin function SendGetCookieString().
    * else
        * Get the nsIprincipal from loadInfo.
            * If DocumentCookieMap can get DocumentCookieInfo from nsCookieKey(nsIprincipal, GetBaseDomian(nsCookie->Host())).
                * If mCookiesPresent=false
                    * Call the origin function SendGetCookieString().
                * If mCookiesPresent=true
                    * Get the nsCookie from cookie hash table.

[Questions]
1. Do every notifies which includes init hash table or destroy hash table always send from CookieServiceParent to CookieServiceChild?
2. How do I know the nsIPrincipal when CookieServiceParent get the “cookie-changed” or “private-cookie-changed”?
3. I have tried to separate the rules of setting cookie to a independent function which names “SetCookieRules”. If we want to confirm the secure cookie which definitely set from secure website, do we set the perf “network.cookie.leave-secure-alone” on CookieServiceChild?

Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
The steps for "Init hash table" look ok, except that when we get the cookies (and a document principal) from the parent, we need to:
* check if there is an entry in the known document hashtable matching that principal
* if there is, check if any of the stored DocumentCookieInfo structures match the document principal's URL (host, path, and mIsSecure)

These steps do not involve comparing cookie names or doing domain/path matching. The known document hashtable stores the documents we know about; the cookie hashtable stores the cookies we know about. There is no connection between them.

The steps under SetPrincipal should be adding entries to the document hashmap, not removing them. Also, SetPrincipal and Destroy will be called in the child and then notify the parent.

Questions:
1. Sort of. Most HTTP requests for a document will go through AboutToLoadHttpFtpWyciwygDocumentForChild and require sending a message to the child from the parent. Some HTTP requests will not leave the child process (intercepted by a ServiceWorker, or about:blank) and will require sending a message from the child to the parent (which will then send a message from the parent to the child).
2. nsICookie includes a host attribute and originAttributes. You can use OriginAttributesDictionary::Init to extract it, then you can use OriginAttributesToSuffix (https://dxr.mozilla.org/mozilla-central/rev/b8e9b674033bcd1f3a4c59b9d0ee7619c1a17cc5/dom/base/ChromeUtils.h#82) to serialize it so it can be used in the key in the child.
3. I'm not sure exactly what you're asking. Our implementation has to support both leave-secure-alone=true and leave-secure-alone=false.

Does that make sense?
Flags: needinfo?(josh)
(Assignee)

Comment 104

a month ago
(In reply to Josh Matthews [:jdm] from comment #103)
> The steps for "Init hash table" look ok, except that when we get the cookies
> (and a document principal) from the parent, we need to:
> * check if there is an entry in the known document hashtable matching that
> principal
> * if there is, check if any of the stored DocumentCookieInfo structures
> match the document principal's URL (host, path, and mIsSecure)
> 
> These steps do not involve comparing cookie names or doing domain/path
> matching. The known document hashtable stores the documents we know about;
> the cookie hashtable stores the cookies we know about. There is no
> connection between them.
> 
> The steps under SetPrincipal should be adding entries to the document
> hashmap, not removing them. Also, SetPrincipal and Destroy will be called in
> the child and then notify the parent.
> 
> Questions:
> 1. Sort of. Most HTTP requests for a document will go through
> AboutToLoadHttpFtpWyciwygDocumentForChild and require sending a message to
> the child from the parent. Some HTTP requests will not leave the child
> process (intercepted by a ServiceWorker, or about:blank) and will require
> sending a message from the child to the parent (which will then send a
> message from the parent to the child).
> 2. nsICookie includes a host attribute and originAttributes. You can use
> OriginAttributesDictionary::Init to extract it, then you can use
> OriginAttributesToSuffix
> (https://dxr.mozilla.org/mozilla-central/rev/
> b8e9b674033bcd1f3a4c59b9d0ee7619c1a17cc5/dom/base/ChromeUtils.h#82) to
> serialize it so it can be used in the key in the child.
> 3. I'm not sure exactly what you're asking. Our implementation has to
> support both leave-secure-alone=true and leave-secure-alone=false.
> 
> Does that make sense?

Thanks for your reply, but I still have some questions on the implementation as below:
1. Does the CookieServiceParent have to set the mCookiePresent after it gets the cookies list? In my view, If the tab reload again, the CookieServiceParent doesn’t need to send the cookies list to child, so the CookieServiceParent can decide whether or not to send the cookie to child by mCookiePresent.

2. When the CookieServiceChild is called SetCookieString(), why does CookieServiceChild need to use the existing synchronous IPDL code path when the mCookiePresent = false from the DocumentCookieInfo which stores in DocumentCookieMap? In my view, CookieServiceChild have to set mCookiePresent to true and store the cookie to the cookie hash table.

3. When the CookieServiceChild is called GetCookieString(), Should CookieServiceChild call the assert when the mCookiePresent = false?

Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
Good questions!

1. The mCookiesPresent value is currently unused in CookieServiceParent in the design I described earlier. I don't think we should try to add special cases around it.

2. If CookieServiceChild sees mCookiesPresent=false, that means that it is still waiting to receive the initial set of cookies for that page. If we update the hashtable and set mCookiesPresent to true, not only will it later be overwritten by the initial set of cookies, but GetCookieString will also start using the non-sync IPC codepath before the set of initial cookies has been received.

3. I do not believe we can use the assert when mCookiePresent=false.

Does that make sense?
Flags: needinfo?(josh)
(Assignee)

Comment 106

a month ago
Created attachment 8869169 [details] [diff] [review]
implementation part1

Hi,
I have modified my patch as below:
[Data Struct]
* Modified the DocumentCookieInfo.
* [CookieServiceChild]
    * Create nsTArray<RefPtr<nsCookie>> CookiesList.
    * Create a CookiesMap<nsCookieKey, CookiesList>

[Init hash table]
* [Parent Process]
    * Create a function names InitHashTableOnParent(nsIPrincipal, nsIURI, nsIChannel) on CookieServiceParent.
    * If principal is available on ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild.
        * Call the new function ProcessHashTableOnCookieServiceParent(nsIChannel, nsIPrincipal, isUpdate=true).
          * On CookieServiceParent, call DocumentCookieOperation::UpdateCookieToHashTable(DocumentCookieMap, nsIURI, OriginAttributes).
          * Send ipc msg to Child.
* [Child Process]
    * If receive the msg about init hash table from parent.
        * call DocumentCookieOperation::UpdateCookieToHashTable(DocumentCookieMap, nsIURI, OriginAttributes).
* [SetPrincipal]
    * Query a nsIHttpChannelInternal from mChannel.
    * If nsIHttpChannelInternal::GetResponseSynthesized returns true
        * Call the function InitHashTableOnParent(nsIPrincipal, nsIURI, nsIChannel) on CookieServiceParent.
* [nsCookieKey]
    * The originAttributes from the principal.

[Remove]
* SetPrincipal
    * Query a nsIHttpChannelInternal from mChannel.
    * If nsIHttpChannelInternal::GetResponseSynthesized returns false
        * Call the new function ProcessHashTableOnCookieServiceParent(nsIChannel, nsIPrincipal, false).
        * [CookieServiceParent]
            * If DocumentCookieInfo which get from nsCookieKey(nsIPrincipal.OriginAttributes, HostName)
                * If mActiveDocuments >= 1 in the existing DocumentCookieInfo which in CookieServiceParent.
                    * mActiveDocuments—
                    * If mActiveDocuments == 0
                        * Remove the DocumentCookieInfo.
            * Send a ipc msg to notify CookeServiceChild to remove the document.
                * [CookieServiceChild]
                * If mActiveDocuments >= 1in the existing DocumentCookieInfo which in CookieServiceChild.
                    * mActiveDocuments—
                    * If mActiveDocuments == 0
                        * Remove the DocumentCookieInfo.
* Destroy
    * Create a function names DestroyDocumentFromHashTable(nsIPrincipal, DocumentURI) in CookieServiceParent.
        * If the DocumentCookieMap exists a DocumentCookieInfo whose domain equals DocumentURI or OriginURI
            * If mActiveDocuments > 1
                * mActiveDocuments—
            * If mActiveDocuments <= 1
                * Remove the DocumentCookieInfo.
            * Send the msg to CookieServiceChild.
    * Create a function names DestroyDocumentFromHashTable(DocumentURI, OriginURI, nsIPrincipal) in CookieServiceChild.
        * If get the msg about have to destroy document.
            * If the DocumentCookieMap exists a DocumentCookieInfo whose domain equals DocumentURI or OriginURI
                * If mActiveDocuments > 1
                    * mActiveDocuments—
                * If mActiveDocuments <= 1
                    * Remove the DocumentCookieInfo.

Would you give me your suggestions?
Thanks!
Attachment #8867898 - Attachment is obsolete: true
Attachment #8867898 - Flags: review?(josh)
Attachment #8869169 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8869169 - Flags: feedback?(juhsu)

Comment 107

a month ago
Comment on attachment 8869169 [details] [diff] [review]
implementation part1

Review of attachment 8869169 [details] [diff] [review]:
-----------------------------------------------------------------

If the movement of nsCookieKey is required, could you move those change to patch 0?
That will speed up the review.

::: dom/ipc/ContentParent.cpp
@@ +89,5 @@
>  #include "mozilla/media/MediaParent.h"
>  #include "mozilla/Move.h"
>  #include "mozilla/net/NeckoParent.h"
> +#include "mozilla/net/PCookieServiceParent.h"
> +#include "mozilla/net/CookieServiceParent.h"

nit: move this line above #include "mozilla/net/NeckoParent.h"

::: dom/ipc/ContentParent.h
@@ +198,5 @@
>  
> +  static void ProcessHashTableOnCookieServiceParent(nsIChannel *aChnnel,
> +                                                    nsIURI *aHostURI,
> +                                                    nsIPrincipal *aPrincipal,
> +                                                    bool aIsUpdate);

const if possible

::: netwerk/cookie/CookieServiceChild.cpp
@@ +93,5 @@
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child; thus we consider failure fatal.
> +  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
> +  // Update cookie to hash table

nit: s/Update/Destroy

::: netwerk/cookie/CookieServiceParent.h
@@ +28,5 @@
> +                                 nsIChannel *aChannel);
> +
> +  nsresult DestroyDocumentFromHashTable(nsIPrincipal *aPrincipal,
> +                                        nsIURI *aHostURI,
> +                                        nsIChannel *aChannel);

const the parameters if possible

::: netwerk/cookie/DocumentCookieOperation.h
@@ +24,5 @@
> +   :mHostName(aHostName)
> +   ,mPathName(aPathName)
> +   ,mSecure(aSecure)
> +   ,mCookiesPresent(aCookiesPresent)
> +   ,mActiveDocuments(1)

nit: space between :/, and members

::: netwerk/cookie/PCookieService.ipdl
@@ +103,5 @@
>    async __delete__();
> +
> +child:
> +  async StartToInitHashTableOnChild(URIParams host,
> +                                    OriginAttributes attrs);

IMO |StartTo| prefix is a little redundant

::: netwerk/cookie/nsCookieService.cpp
@@ +4205,5 @@
>    return path;
>  }
>  
> +
> +

revert this
Attachment #8869169 - Flags: feedback?(juhsu)
(Assignee)

Comment 108

a month ago
Created attachment 8869563 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Hi,
I have separated the movement of nsCookieKey to independent patch.
Would you give me your suggestions?
Attachment #8869563 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8869563 - Flags: feedback?(juhsu)
(Assignee)

Comment 109

a month ago
Created attachment 8869564 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I have modified my patch as below:
1. Modified nits.
2. Separated the movement of nsCookieKey to other patch.

Would you give me your suggestions?
Thanks!
Attachment #8869169 - Attachment is obsolete: true
Attachment #8869169 - Flags: feedback?(josh)
Attachment #8869564 - Flags: feedback?(josh)
(Assignee)

Comment 110

a month ago
Created attachment 8869571 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Hi,
I have modified the patch as below:
[Get Cookies]
*  [all.js]
    * Created a new pref id names "network.cookie.ipc.sync" and default value is false.
*  [CookieServiceChild]
    * If the network.cookie.ipc.sync is true
        * Call the origin function SendGetCookieString().
    * else
        * Get the nsIprincipal from loadInfo.
            * If DocumentCookieMap can get DocumentCookieInfo from nsCookieKey(nsIprincipal, GetBaseDomian(nsCookie->Host())).
                * If mCookiesPresent=false
                    * Call the origin function SendGetCookieString().
                * If mCookiesPresent=true
                    * Get the nsCookie from cookie hash table.

Would you give me your suggestions on my patch?
Thanks!

[Question]
1. Part 0 - Part 2 don't include any modification on mCookiesPresent which default value is false.
In my view, the mCookiesPresent always get false and this return value will use the existing synchronous IPDL code path and return the result.
So I think document.cookie should get the cookies string through SendGetCookieString().
would you give me your suggestion on my option?
Attachment #8867881 - Attachment is obsolete: true
Attachment #8869571 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8869571 - Flags: feedback?(juhsu)
(Assignee)

Comment 111

a month ago
(In reply to Josh Matthews [:jdm] from comment #105)
> Good questions!
> 
> 1. The mCookiesPresent value is currently unused in CookieServiceParent in
> the design I described earlier. I don't think we should try to add special
> cases around it.
Would you explain the reason about the mCookiePresent value is unused in CookieServiceParent?
Does any risk about the mCookiePresent is set in CookieServiceParent?
Whether it is for avoiding CookieServiceParent to miss to send any cookie to CookieServiceChild?
> 
> 2. If CookieServiceChild sees mCookiesPresent=false, that means that it is
> still waiting to receive the initial set of cookies for that page. If we
> update the hashtable and set mCookiesPresent to true, not only will it later
> be overwritten by the initial set of cookies, but GetCookieString will also
> start using the non-sync IPC codepath before the set of initial cookies has
> been received.
Ok, I understand.
> 
> 3. I do not believe we can use the assert when mCookiePresent=false.
Ok, I understand.
> 
> Does that make sense?
Flags: needinfo?(josh)
(Assignee)

Comment 112

a month ago
Created attachment 8869844 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Hi,
I have modified my patch as below:
[Init cookie hash table]
* [Parent Process]
    * If the document have to update to DocumentCookieInfo, get cookies list and find the cookie which is the matching cookie then send to cookie service child.
* [Child Process]
    * If receive the nsCookie from parent.
        * If CookiesMap can get the CookiesLIst from nsCookieKey(nsIChannel.OriginAttributes, BaseDomain). && nsCookie.Name().equals(the nsCookie from CookiesList.Name())
            * If DomainMatch(nsCookie, DocumentCookieInfo.mHostName) && PathMatch(nsCookie, DocumentCookieInfo.mPathName)
                * If the DocumentCookieInfo->mCookiesPresent is true
                    * mActiveDocuments++
                * If the DocumentCookieInfo->mCookiesPresent is false
                    * Set mCookiesPresent to true.
                    * mActiveDocuments++
        * If not equal
            * Init a DocumentCookieInfo and set the domain & path from the entry.
            * mActiveDocuments = 1.
            * mCookiesPresent=true.
    * If not exist the entry.
        * Create a nsCookie and set the value from the cookie which sends from parent.
        * Create a DocumentCookieInfo and set the domain & path from the nsCookie.
            * mActiveDocuments = 1.
            * mCookiesPresent=true.
    * If exist the entry
        * Overwrite the existing cookies.

[Remove]
* Destroy
    * Create a function names DestroyDocumentFromHashTable(DocumentURI, OriginURI, nsIPrincipal) in CookieServiceChild.
        * If get the msg about have to destroy document.
            * If the DocumentCookieMap exists a DocumentCookieInfo whose domain equals DocumentURI or OriginURI
                * Purge appropriate cookies
                    * If the DomainMathes
                        * If PathMatches(cookie, the path from the DocumentCookieInfo) return false
                            * Remove the cookie.


Would you give me your suggestions?
Thanks!
Attachment #8866445 - Attachment is obsolete: true
Attachment #8869844 - Flags: feedback?(josh)
(Assignee)

Comment 113

a month ago
Created attachment 8869845 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Sorry for uploading a wrong attachment, re-uploaded a correct one.
Attachment #8869844 - Attachment is obsolete: true
Attachment #8869844 - Flags: feedback?(josh)
Attachment #8869845 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8869845 - Flags: feedback?(juhsu)

Comment 114

a month ago
Comment on attachment 8869563 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Review of attachment 8869563 [details] [diff] [review]:
-----------------------------------------------------------------

f+ for one question left

::: netwerk/cookie/nsCookieService.cpp
@@ -611,5 @@
> -nsCookieKey::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
> -{
> -  return mBaseDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
> -}
> -

Why did you move this?
Attachment #8869563 - Flags: feedback?(juhsu) → feedback+

Comment 115

a month ago
> Why did you move this?
s/move/remove
Comment on attachment 8869563 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Review of attachment 8869563 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'nsCookieKey.h',

Do we use this from any files besides nsCookieService and CookieServiceChild? Since they are both already in this directory I don't think we need to export this header.

::: netwerk/cookie/nsCookieKey.h
@@ +2,5 @@
> +#define mozilla_net_nsCookieKey_h
> +#include "nsHashKeys.h"
> +#include "nsTHashtable.h"
> +
> +// This removal of nsCookieKey from nsCookieService.h

This comment is not necessary.

@@ +4,5 @@
> +#include "nsTHashtable.h"
> +
> +// This removal of nsCookieKey from nsCookieService.h
> +namespace mozilla {
> +namespace net {

Wait, how does all the code in nsCookieService.cpp only need to use mozilla::nsCookieKey?

::: netwerk/cookie/nsCookieService.cpp
@@ +63,5 @@
>  // TODO: When we figure out what the API will look like for nsICookieManager{2}
>  // on content processes (see bug 777620), change to use the appropriate app
>  // namespace.  For now those IDLs aren't supported on child processes.
>  #define DEFAULT_APP_KEY(baseDomain) \
> +        mozilla::nsCookieKey(baseDomain, OriginAttributes())

If we add `use mozilla::nsCookieKey` to the top of the file, lots of these changes should not be necessary.
Attachment #8869563 - Flags: feedback?(josh) → feedback-

Comment 117

a month ago
Comment on attachment 8869571 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Review of attachment 8869571 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceChild.cpp
@@ +173,5 @@
>    }
>  
>    // Synchronously call the parent.
>    nsAutoCString result;
> +

remove this

@@ +184,5 @@
> +      SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> +    }
> +  } else {
> +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> +  }

Use |&&| to avoid nested |if|
i.e.,
if (!mIPCSync &&
    mDoc...

Moreover, the annotation should be changed.

::: netwerk/cookie/CookieServiceChild.h
@@ +57,5 @@
>    nsresult GetCookieStringInternal(nsIURI *aHostURI,
>                                     nsIChannel *aChannel,
>                                     char **aCookieString);
>  
> +  nsresult GetCookieStringFromCookieHashTable(nsIURI *aHostURI,

const

@@ +59,5 @@
>                                     char **aCookieString);
>  
> +  nsresult GetCookieStringFromCookieHashTable(nsIURI *aHostURI,
> +                                              const OriginAttributes &aAttrs,
> +                                              nsAutoCString &aCookieString);

I don't see the implementation. Please put this in the appropriate patch

@@ +66,5 @@
>                                     nsIChannel *aChannel,
>                                     const char *aCookieString,
>                                     const char *aServerTime);
>  
> +

revert this change

::: netwerk/cookie/DocumentCookieOperation.h
@@ +69,5 @@
>  
> +  bool
> +  ConfirmDocumentExistsCookies(DocumentCookieMap &aDocCookieMap,
> +                               nsIURI *aHostURI,
> +                               const OriginAttributes &aOriginAttrs);

const the parameters if possible

::: netwerk/cookie/nsCookieService.cpp
@@ +3128,5 @@
>  // helper function for GetCookieList
>  static inline bool ispathdelimiter(char c) { return c == '/' || c == '?' || c == '#' || c == ';'; }
>  
> +bool
> +nsCookieService::DomainMatches(nsCookie* aCookie,

const and MOZ_ASSERT(aCookie)

@@ +3141,5 @@
>  
> +bool
> +nsCookieService::PathMatches(nsCookie* aCookie,
> +                             const nsACString& aPath)
> +{

same here
Attachment #8869571 - Flags: feedback?(juhsu) → feedback+

Comment 118

a month ago
Comment on attachment 8869845 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Review of attachment 8869845 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceChild.h
@@ +7,5 @@
>  #define mozilla_net_CookieServiceChild_h__
>  
>  #include "mozilla/net/DocumentCookieOperation.h"
>  #include "mozilla/net/PCookieServiceChild.h"
> +#include "mozilla/net/NeckoChannelParams.h"

move this one line up

::: netwerk/cookie/CookieServiceParent.cpp
@@ +113,5 @@
> +  thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
> +  bool isForeign = true;
> +  thirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
> +  nsTArray<CookieStruct> cookiesList;
> +  // Get cookie struct list

remove this

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +342,5 @@
> +    // for each of the cookies that exist for the given domain
> +    // for each value in the document hashtable entry for the given domain
> +    // if the cookie path-matches against the value's path, ignore the cookie
> +    // if the cookie does not path-match against any of the known document
> +    // paths, remove the cookie

The annotation is too long and seems a line-by-line translation. Please add a good comment instead, or remove it if it's intuitive.

::: netwerk/cookie/DocumentCookieOperation.h
@@ +72,5 @@
> +  UpdateCookieToTable(DocumentCookieMap &aDocCookieMap,
> +                      CookiesMap &aCookiesMap,
> +                      nsIURI *aHostURI,
> +                      CookieStruct &aCookie,
> +                      const OriginAttributes &aOriginAttrs);

const if possible

::: netwerk/cookie/nsCookieService.cpp
@@ +4186,5 @@
> +nsCookieService::ConfirmMatchingCookie(nsCookie                         *aCookie,
> +                                       nsIURI                           *aHostURI,
> +                                       mozilla::net::CookieStruct       *aCookieStruct,
> +                                       nsIChannel                       *aChannel)
> +{

I can't easily find out the purpose of the function.
aCookie could be changed, but it's on the first argument
No appropriate const prefix to let me know which argument could be changed.

So please add annotation and appropriate const to make the reader know
a. what's does the function do (if the function name can't reflex it)
b. what's the side effect such as aCookie would be generated by aCookie if you pass a nullptr

@@ +4194,5 @@
> +  nsAutoCString sourcePath, sourceHost;
> +  int64_t currentTimeInUsec = PR_Now();
> +  int64_t currentTime = currentTimeInUsec / PR_USEC_PER_SEC;
> +  if (!aCookie) {
> +    // Get originAttributes.

The code perfectly tells the story of the comment, so it is not necessary
Attachment #8869845 - Flags: feedback?(juhsu)
Comment on attachment 8869564 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Review of attachment 8869564 [details] [diff] [review]:
-----------------------------------------------------------------

Good start! This patch was much easier to review than previous ones because it was smaller and self-contained. I did not review some parts of it carefully because one of the IPDL messages is backwards, so a bunch of the logic will need to change in the next revision.

::: docshell/base/nsDocShell.cpp
@@ +8603,5 @@
> +  if (!(aSandboxFlags & SANDBOXED_ORIGIN) &&
> +      !(aSandboxFlags & SANDBOXED_SCRIPTS)) {
> +    return true;
> +  }
> +  return false;

return aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS) == 0;

::: dom/base/nsDocument.cpp
@@ +2980,5 @@
>  
>  void
>  nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
>  {
> +  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);

We should only execute this new code if we are running in the content process.

@@ +2981,5 @@
>  void
>  nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
>  {
> +  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
> +  if (internalChan) {

We also need to send a message to the parent if the QI fails.

@@ +2994,5 @@
> +    } else {
> +      // Remove the DocumentCookieInfo from DocumentCookieMap
> +      // which is a hash table in CookieServiceParent.
> +      nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> +      isUpdate = false;

This is incorrect. This method is only about notifying the parent about new documents. If this is not a synthesized response, we do not need to do anything.

@@ +2995,5 @@
> +      // Remove the DocumentCookieInfo from DocumentCookieMap
> +      // which is a hash table in CookieServiceParent.
> +      nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> +      isUpdate = false;
> +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mDocumentURI, aNewPrincipal, isUpdate);

We need to send a message to the parent using CookieServiceChild.

@@ +8764,5 @@
> +    nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
> +    if (chan && mDocumentURI && mOriginalURI) {
> +      bool isUpdate = false;
> +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mDocumentURI, principal, isUpdate);

We should only send mDocumentURI if it is different from mOriginalURI.

@@ +8765,5 @@
> +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
> +    if (chan && mDocumentURI && mOriginalURI) {
> +      bool isUpdate = false;
> +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mDocumentURI, principal, isUpdate);
> +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mOriginalURI, principal, isUpdate);

We need to send a message to the parent using CookieServiceChild.

::: dom/ipc/ContentParent.cpp
@@ +5058,5 @@
> +                                                     nsIURI *aHostURI,
> +                                                     nsIPrincipal *aPrincipal,
> +                                                     const bool &aIsUpdate)
> +{
> +  for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {

This should not be a static method. We can assert that the number of managed PCookieServiceParent objects is 1, then invoke the method on the only element of the array instead.

@@ +5116,5 @@
> +  aChannel->GetLoadFlags(&newLoadFlags);
> +  bool isDocument = false;
> +  aChannel->GetIsDocument(&isDocument);
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +      isDocument) {

I think this can go on the previous line.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +79,5 @@
> +CookieServiceChild::RecvInitHashTableOnChild(const URIParams &aHost,
> +                                             const OriginAttributes &aAttrs)
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child; thus we consider failure fatal.

The code does not actually match the comment. There's no fatal failure here.

::: netwerk/cookie/CookieServiceParent.h
@@ +8,5 @@
>  
>  #include "mozilla/net/PCookieServiceParent.h"
> +#include "DocumentCookieOperation.h"
> +#include "nsIPrincipal.h"
> +#include "nsIURI.h"

We can forward declare nsIPrincipal and nsIURI in this header instead.

@@ +34,2 @@
>  protected:
> +  DocumentCookieOperation::DocumentCookieMap mContentProcessDocuments;

We can store this inside the DocumentCookieOperation class instead.

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +6,5 @@
> +namespace mozilla {
> +namespace net {
> +DocumentCookieOperation::DocumentCookieOperation()
> +{
> +  Init();

This is not a safe pattern. However, my other comments recommend removing mTLDService, so we should not need an Init method anymore.

@@ +29,5 @@
> +// 'localhost', an eTLD such as 'co.uk', or the empty string, aBaseDomain will
> +// be the exact host, and aRequireHostMatch will be true to indicate that
> +// substring matches should not be performed.
> +nsresult
> +DocumentCookieOperation::GetBaseDomain(nsIURI    *aHostURI,

Let's make GetBaseDomain and GetBaseDomainFromHost static methods for nsCookieService, and add a nsITLDService* argument. Then we can call it from both nsCookieService and this class without having to move the code or store DocumentCookieOperation in nsCookieService.

@@ +108,5 @@
> +
> +  nsCString baseDomain;
> +  bool requireDomainMatch;
> +  GetBaseDomain(aHostURI, baseDomain, requireDomainMatch);
> +  nsCookieKey key(baseDomain, aOriginAttrs);

We need to use the "convert a principal into a hashtable string key" algorithm that I described here instead.

@@ +122,5 @@
> +  bool docListExist = false;
> +  if (docCookieInfoList) {
> +    docListExist = true;
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = &(docCookieInfoList->ElementAt(i));

Is the & necessary? At minimum we can remove the surrounding ().

@@ +123,5 @@
> +  if (docCookieInfoList) {
> +    docListExist = true;
> +    for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +      docCookieInfo = &(docCookieInfoList->ElementAt(i));
> +      NS_ASSERTION(docCookieInfo, "could't get DocumentCookieInfo");

This assertion doesn't make sense; there's no way we could get a null pointer here.

@@ +129,5 @@
> +          docCookieInfo->mPathName.Equals(pathFromURI) &&
> +          docCookieInfo->mSecure == isHTTPS) {
> +        hostURIExist = true;
> +        docCookieInfo->mActiveDocuments++;
> +        break;

We can return immediately here instead.

@@ +133,5 @@
> +        break;
> +      }
> +    }
> +  } else {
> +    docCookieInfoList = new DocumentCookieInfoList;

Let's add code that specifically adds a hashmap entry and appends an entry to it here, then return.

@@ +136,5 @@
> +  } else {
> +    docCookieInfoList = new DocumentCookieInfoList;
> +  }
> +
> +  if (!hostURIExist) {

This check is not necessary if we return earlier.

@@ +137,5 @@
> +    docCookieInfoList = new DocumentCookieInfoList;
> +  }
> +
> +  if (!hostURIExist) {
> +    docCookieInfo = new DocumentCookieInfo(hostFromURI, pathFromURI, isHTTPS, false);

This value is always leaked. With the earlier changes, we should be able to write clearer code here that avoids this.

@@ +159,5 @@
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +  bool requireHostMatch;
> +  nsCString baseDomain;
> +  GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
> +  nsCookieKey key(baseDomain, aOriginAttrs);

Same algorithm as earlier for a string key, not nsCookieKey.

@@ +161,5 @@
> +  nsCString baseDomain;
> +  GetBaseDomain(aHostURI, baseDomain, requireHostMatch);
> +  nsCookieKey key(baseDomain, aOriginAttrs);
> +  aDocCookieMap.Get(key, &docCookieInfoList);
> +  if (!docCookieInfoList) {

I believe we can assert that there is an entry here.

@@ +173,5 @@
> +  aHostURI->GetPath(pathFromURI);
> +  aHostURI->SchemeIs("https", &isHTTPS);
> +
> +  for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +    docCookieInfo = &(docCookieInfoList->ElementAt(i));

We can remove the () here.

@@ +174,5 @@
> +  aHostURI->SchemeIs("https", &isHTTPS);
> +
> +  for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +    docCookieInfo = &(docCookieInfoList->ElementAt(i));
> +    NS_ASSERTION(docCookieInfo, "couldn't get DocumentCookieInfo");

We can never have a null pointer here.

@@ +188,5 @@
> +    }
> +  }
> +
> +  if (docCookieInfoList->IsEmpty()) {
> +    docCookieInfoList = nullptr;

No need to null out this value.

@@ +190,5 @@
> +
> +  if (docCookieInfoList->IsEmpty()) {
> +    docCookieInfoList = nullptr;
> +    nsAutoPtr<DocumentCookieInfoList> rmDocList;
> +    aDocCookieMap.RemoveAndForget(key, rmDocList);

We can call Remove instead.

::: netwerk/cookie/DocumentCookieOperation.h
@@ +10,5 @@
> +
> +class nsIEffectiveTLDService;
> +
> +namespace mozilla{
> +namespace net{

nit: space before {

@@ +35,5 @@
> +  bool mCookiesPresent;
> +  nsrefcnt mActiveDocuments;
> +};
> +
> +class DocumentCookieOperation

Let's call this DocumentTracker.

@@ +39,5 @@
> +class DocumentCookieOperation
> +{
> +public:
> +  DocumentCookieOperation();
> +  NS_INLINE_DECL_REFCOUNTING(DocumentCookieOperation);

I don't think this class needs to be reference counted.

@@ +42,5 @@
> +  DocumentCookieOperation();
> +  NS_INLINE_DECL_REFCOUNTING(DocumentCookieOperation);
> +
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;

The key for this hashtable should be nsCStringHashKey, not nsCookieKey. This is a map of domains to known documents, not a map of known cookies.

@@ +45,5 @@
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;
> +
> +  bool
> +  UpdateDocumentToTable(DocumentCookieMap &aDocCookieMap,

Let's call this `TrackNewDocument`.

@@ +50,5 @@
> +                        nsIURI *aHostURI,
> +                        const OriginAttributes &aOriginAttrs);
> +
> +  bool
> +  DestroyDocumentFromTable(DocumentCookieMap &aDocCookieMap,

Let's call this `RemoveTrackedDocument`.

@@ +64,5 @@
> +
> +  nsresult Init();
> +
> +private:
> +  nsCOMPtr<nsIEffectiveTLDService> mTLDService;

This class should store an instance of DocumentCookieMap, rather than take one as an argument to all of the methods.

::: netwerk/cookie/PCookieService.ipdl
@@ +102,5 @@
>  
>    async __delete__();
> +
> +child:
> +  async InitHashTableOnChild(URIParams host,

We should call this TrackDocumentLoad instead.

@@ +105,5 @@
> +child:
> +  async InitHashTableOnChild(URIParams host,
> +                             OriginAttributes attrs);
> +
> +  async DestroyHashTableOnChild(URIParams host,

This message should be sent from the child to the parent. Let's call it UntrackDocument.

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'DocumentCookieOperation.h',

I don't think this header needs to be exported either? It should only be used by files in netwerk/cookie.

::: netwerk/cookie/nsCookieService.cpp
@@ +3194,5 @@
>    return true;
>  }
>  
>  void
> +nsCookieService::GetCookieListInternal(nsIURI *aHostURI,

These changes do not belong in this patch. This patch should only involve tracking documents, not sending cookies anywhere.

@@ +4203,5 @@
>    return path;
>  }
>  
> +
> +

nit: revert these extra newlines.

::: netwerk/cookie/nsCookieService.h
@@ +290,5 @@
>      nsCOMPtr<mozIThirdPartyUtil>     mThirdPartyUtil;
>      nsCOMPtr<nsIEffectiveTLDService> mTLDService;
>      nsCOMPtr<nsIIDNService>          mIDNService;
>      nsCOMPtr<mozIStorageService>     mStorageService;
> +    RefPtr<mozilla::DocumentCookieOperation>  mDocCookieOperation;

Why are we storing this in nsCookieService? Is it only to use GetBaseDomain?

::: netwerk/ipc/NeckoChannelParams.ipdlh
@@ +189,5 @@
>    PFTPChannel;
>  };
>  
> +// For OnStartRequest Parent -> Child
> +struct CookieStruct

This change does not belong in this patch.
Attachment #8869564 - Flags: feedback?(josh) → feedback-
Comment on attachment 8869571 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Review of attachment 8869571 [details] [diff] [review]:
-----------------------------------------------------------------

To answer your question, the default value for mCookiesPresent should be true, not false.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +166,5 @@
>      nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
>      if (loadInfo) {
>        attrs = loadInfo->GetOriginAttributes();
> +      nsCOMPtr<nsIPrincipal> triggeringPrincipal;
> +      loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));

We need to use GetChannelResultPrincipal instead, which will give us the principal for the final URL retrieved by the channel (ie. the document). The triggering principal gives us the principal that initiated the document load, instead.

@@ +175,5 @@
>    // Synchronously call the parent.
>    nsAutoCString result;
> +
> +  if (!mIPCSync) {
> +    if (mDocCookieOperation->

We can merge these two conditions into one if statement.

@@ +183,5 @@
> +    } else {
> +      SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> +    }
> +  } else {
> +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);

We need to extract this sync IPC into a separate method (GetCookieStringIPCSync) so it is easy to notice in performance profiles.

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +124,5 @@
> +      docCookieInfo = &(docCookieInfoList->ElementAt(i));
> +      NS_ASSERTION(docCookieInfo, "could't get DocumentCookieInfo");
> +      if (docCookieInfo->mHostName.Equals(hostFromURI) &&
> +          docCookieInfo->mPathName.Equals(pathFromURI) &&
> +          docCookieInfo->mSecure == isHTTPS) {

This pattern is duplicated in several methods now. We should make a GetMatchingDocument method that returns DocumentCookieInfo* and reduce the duplicated code.

@@ +133,5 @@
> +
> +  return false;
> +}
> +
> +nsresult

This can return void, since this method never returns anything except NS_OK.

@@ +137,5 @@
> +nsresult
> +DocumentCookieOperation::GetCookieStringFromCookieHashTable(CookiesMap             &aCookiesMap,
> +                                                            nsIURI                 *aHostURI,
> +                                                            const OriginAttributes &aOriginAttrs,
> +                                                            nsAutoCString          &aCookieString)

It is better to keep string outparameters as generic as possible - nsACString& is the abstract base class that we can use.

@@ +153,5 @@
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);
> +    NS_ASSERTION(cookie, "Get DocumentCookieInfo fail");

There is no need for this assertion. We can't get a null cookie here.

::: netwerk/cookie/DocumentCookieOperation.h
@@ +48,5 @@
>    typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;
>  
> +  // The cookie hash table.
> +  typedef nsTArray<RefPtr<nsCookie>> CookiesList;
> +  typedef nsClassHashtable<nsCookieKey, CookiesList> CookiesMap;

This should be defined in CookieServiceChild, since no other code needs this.

@@ +54,5 @@
> +  nsresult
> +  GetCookieStringFromCookieHashTable(CookiesMap &aCookieMap,
> +                                     nsIURI *aHostURI,
> +                                     const OriginAttributes &aOriginAttrs,
> +                                     nsAutoCString &aCookieString);

This should be defined in CookieServiceChild.

@@ +67,5 @@
>                             nsIURI *aHostURI,
>                             const OriginAttributes &aOriginAttrs);
>  
> +  bool
> +  ConfirmDocumentExistsCookies(DocumentCookieMap &aDocCookieMap,

This belongs in CookieServiceChild.

::: netwerk/cookie/nsCookieService.h
@@ +179,5 @@
>    RESULT_FAILURE
>  };
>  
> +// Comparator class for sorting cookies before sending to a server.
> +class CompareCookiesForSending

This belongs in nsCookie.h instead.
Attachment #8869571 - Flags: feedback?(josh) → feedback-
Thank you so much for following my suggestions for splitting up the patches. I am finding that reviewing these changes is much easier this time!
(In reply to Amy Chung [:Amy] from comment #111)
> (In reply to Josh Matthews [:jdm] from comment #105)
> > Good questions!
> > 
> > 1. The mCookiesPresent value is currently unused in CookieServiceParent in
> > the design I described earlier. I don't think we should try to add special
> > cases around it.
> Would you explain the reason about the mCookiePresent value is unused in
> CookieServiceParent?
> Does any risk about the mCookiePresent is set in CookieServiceParent?
> Whether it is for avoiding CookieServiceParent to miss to send any cookie to
> CookieServiceChild?

mCookiesPresent is used by the child to decide whether looking at the cookies hashtable is meaningful (otherwise we cannot distinguish between "this domain has not set any cookies" and "the parent has not told the child what cookies exist for this domain").

Since the parent cookie service actor does not store cookie values, only documents, mCookiesPresent does not mean anything in the parent. Since both the parent and the child track documents in the same way, it is most efficient to share the same code and data structure and ignore the unused member variable.
Flags: needinfo?(josh)
(Assignee)

Comment 123

a month ago
Hi Josh,
Thanks for your suggestion on the part1 patch.
I have some questions as below:
> ::: dom/base/nsDocument.cpp
> @@ +2980,5 @@
> >  
> >  void
> >  nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
> >  {
> > +  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
> 
> We should only execute this new code if we are running in the content
> process.
> 
> @@ +2981,5 @@
> >  void
> >  nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
> >  {
> > +  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
> > +  if (internalChan) {
> 
> We also need to send a message to the parent if the QI fails.
> 
> @@ +2994,5 @@
> > +    } else {
> > +      // Remove the DocumentCookieInfo from DocumentCookieMap
> > +      // which is a hash table in CookieServiceParent.
> > +      nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> > +      isUpdate = false;
> 
> This is incorrect. This method is only about notifying the parent about new
> documents. If this is not a synthesized response, we do not need to do
> anything.
> 
> @@ +2995,5 @@
> > +      // Remove the DocumentCookieInfo from DocumentCookieMap
> > +      // which is a hash table in CookieServiceParent.
> > +      nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> > +      isUpdate = false;
> > +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mDocumentURI, aNewPrincipal, isUpdate);
> 
> We need to send a message to the parent using CookieServiceChild.
In my view, in normal circumstances, CookieServiceParent will get the notify which have to update document info to hash table or destroy document info from hash table first, then CookieServiceParent will send ipc msg to CookieServiceChild in order to inform CookieServiceChild have to process appropriate operation on document hash table.
If here have to send a msg to CookieServiceParent using CookieServiceChild, does it mean that this situation is intercepted by a ServiceWorker, or about:blank?

> @@ +8765,5 @@
> > +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
> > +    if (chan && mDocumentURI && mOriginalURI) {
> > +      bool isUpdate = false;
> > +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mDocumentURI, principal, isUpdate);
> > +      ContentParent::ProcessHashTableOnCookieServiceParent(chan, mOriginalURI, principal, isUpdate);
> 
> We need to send a message to the parent using CookieServiceChild.
> 
Same question as above.

Looking forward your reply, thanks!
Flags: needinfo?(josh)
(Assignee)

Comment 124

a month ago
Thanks for your suggestions!
(In reply to Josh Matthews [:jdm] from comment #120)
> Comment on attachment 8869571 [details] [diff] [review]
> implementation part2 --- Created pref id, implemented the function about get
> cookies string
> 
> Review of attachment 8869571 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> To answer your question, the default value for mCookiesPresent should be
> true, not false.
> 
Ok, I will modify the default value.
> ::: netwerk/cookie/CookieServiceChild.cpp
> @@ +166,5 @@
> >      nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
> >      if (loadInfo) {
> >        attrs = loadInfo->GetOriginAttributes();
> > +      nsCOMPtr<nsIPrincipal> triggeringPrincipal;
> > +      loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
> 
> We need to use GetChannelResultPrincipal instead, which will give us the
> principal for the final URL retrieved by the channel (ie. the document). The
> triggering principal gives us the principal that initiated the document
> load, instead.
> 
Ok, I will modify to GetChannelResultPrincipal().
> @@ +175,5 @@
> >    // Synchronously call the parent.
> >    nsAutoCString result;
> > +
> > +  if (!mIPCSync) {
> > +    if (mDocCookieOperation->
> 
> We can merge these two conditions into one if statement.
> 
Ok, I will modify it.
> @@ +183,5 @@
> > +    } else {
> > +      SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> > +    }
> > +  } else {
> > +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);
> 
> We need to extract this sync IPC into a separate method
> (GetCookieStringIPCSync) so it is easy to notice in performance profiles.
> 
Ok, I will modify the method name.
> ::: netwerk/cookie/DocumentCookieOperation.cpp
> @@ +124,5 @@
> > +      docCookieInfo = &(docCookieInfoList->ElementAt(i));
> > +      NS_ASSERTION(docCookieInfo, "could't get DocumentCookieInfo");
> > +      if (docCookieInfo->mHostName.Equals(hostFromURI) &&
> > +          docCookieInfo->mPathName.Equals(pathFromURI) &&
> > +          docCookieInfo->mSecure == isHTTPS) {
> 
> This pattern is duplicated in several methods now. We should make a
> GetMatchingDocument method that returns DocumentCookieInfo* and reduce the
> duplicated code.
> 
Ok, I will separate a function to do the confirming works.
> @@ +133,5 @@
> > +
> > +  return false;
> > +}
> > +
> > +nsresult
> 
> This can return void, since this method never returns anything except NS_OK.
> 
Ok, I will modify to void.
> @@ +137,5 @@
> > +nsresult
> > +DocumentCookieOperation::GetCookieStringFromCookieHashTable(CookiesMap             &aCookiesMap,
> > +                                                            nsIURI                 *aHostURI,
> > +                                                            const OriginAttributes &aOriginAttrs,
> > +                                                            nsAutoCString          &aCookieString)
> 
> It is better to keep string outparameters as generic as possible -
> nsACString& is the abstract base class that we can use.
> 
Ok, I will do that.
> @@ +153,5 @@
> > +  }
> > +  cookiesList->Sort(CompareCookiesForSending());
> > +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> > +    cookie = cookiesList->ElementAt(i);
> > +    NS_ASSERTION(cookie, "Get DocumentCookieInfo fail");
> 
> There is no need for this assertion. We can't get a null cookie here.
> 
Ok, I will remove this assertion.
> ::: netwerk/cookie/DocumentCookieOperation.h
> @@ +48,5 @@
> >    typedef nsClassHashtable<nsCookieKey, DocumentCookieInfoList> DocumentCookieMap;
> >  
> > +  // The cookie hash table.
> > +  typedef nsTArray<RefPtr<nsCookie>> CookiesList;
> > +  typedef nsClassHashtable<nsCookieKey, CookiesList> CookiesMap;
> 
> This should be defined in CookieServiceChild, since no other code needs this.
> 
> @@ +54,5 @@
> > +  nsresult
> > +  GetCookieStringFromCookieHashTable(CookiesMap &aCookieMap,
> > +                                     nsIURI *aHostURI,
> > +                                     const OriginAttributes &aOriginAttrs,
> > +                                     nsAutoCString &aCookieString);
> 
> This should be defined in CookieServiceChild.
> 
> @@ +67,5 @@
> >                             nsIURI *aHostURI,
> >                             const OriginAttributes &aOriginAttrs);
> >  
> > +  bool
> > +  ConfirmDocumentExistsCookies(DocumentCookieMap &aDocCookieMap,
> 
> This belongs in CookieServiceChild.
Ok, I will move the defines to CookieServiceChild.
> ::: netwerk/cookie/nsCookieService.h
> @@ +179,5 @@
> >    RESULT_FAILURE
> >  };
> >  
> > +// Comparator class for sorting cookies before sending to a server.
> > +class CompareCookiesForSending
> 
> This belongs in nsCookie.h instead.
Ok, I will move this Class to nsCookie.h.
(Assignee)

Comment 125

a month ago
Hi Junior,
Thanks for your suggestion.
(In reply to Junior[:junior] from comment #114)
> Comment on attachment 8869563 [details] [diff] [review]
> implementation part0 --- The movement of nsCookieKey to other file.
> 
> Review of attachment 8869563 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> f+ for one question left
> 
> ::: netwerk/cookie/nsCookieService.cpp
> @@ -611,5 @@
> > -nsCookieKey::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
> > -{
> > -  return mBaseDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
> > -}
> > -
> 
> Why did you move this?
Because I moved this code to nsCookieKey.h.
In my view, this function is belong to nsCookieKey, so I would move this code to nsCookieKey.h.
(Assignee)

Comment 126

a month ago
(In reply to Josh Matthews [:jdm] from comment #121)
> Thank you so much for following my suggestions for splitting up the patches.
> I am finding that reviewing these changes is much easier this time!

Thanks for your detailed suggestions!
This method which splits up the patches is more clear.
Thanks again!
(Assignee)

Comment 127

a month ago
(In reply to Josh Matthews [:jdm] from comment #122)
> (In reply to Amy Chung [:Amy] from comment #111)
> > (In reply to Josh Matthews [:jdm] from comment #105)
> > > Good questions!
> > > 
> > > 1. The mCookiesPresent value is currently unused in CookieServiceParent in
> > > the design I described earlier. I don't think we should try to add special
> > > cases around it.
> > Would you explain the reason about the mCookiePresent value is unused in
> > CookieServiceParent?
> > Does any risk about the mCookiePresent is set in CookieServiceParent?
> > Whether it is for avoiding CookieServiceParent to miss to send any cookie to
> > CookieServiceChild?
> 
> mCookiesPresent is used by the child to decide whether looking at the
> cookies hashtable is meaningful (otherwise we cannot distinguish between
> "this domain has not set any cookies" and "the parent has not told the child
> what cookies exist for this domain").
> 
> Since the parent cookie service actor does not store cookie values, only
> documents, mCookiesPresent does not mean anything in the parent. Since both
> the parent and the child track documents in the same way, it is most
> efficient to share the same code and data structure and ignore the unused
> member variable.

OK, I understand.
Thank for your reply.
(Assignee)

Comment 128

a month ago
Created attachment 8870126 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Hi,
I have modified my patch as below:
1. Removed the modification on moz.build.
2. Added "using mozilla::nsCookieKey" on nsCookieService to avoid nsCookieService have to use "mozilla::nsCookieKey".

Would you give me your suggestions?
Thanks!
Attachment #8869563 - Attachment is obsolete: true
Attachment #8870126 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8870126 - Flags: feedback?(juhsu)
Comment on attachment 8870126 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Review of attachment 8870126 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/nsCookieKey.h
@@ +2,5 @@
> +#define mozilla_net_nsCookieKey_h
> +#include "nsHashKeys.h"
> +#include "nsTHashtable.h"
> +
> +// This removal of nsCookieKey from nsCookieService.h

This comment is not necessary. If someone needs to know this, they can look in VCS history.
Attachment #8870126 - Flags: feedback?(josh) → feedback+

Comment 130

a month ago
Comment on attachment 8870126 [details] [diff] [review]
implementation part0 --- The movement of nsCookieKey to other file.

Review of attachment 8870126 [details] [diff] [review]:
-----------------------------------------------------------------

LGTM, thanks!
Attachment #8870126 - Flags: feedback?(juhsu) → feedback+
(Assignee)

Comment 131

a month ago
Hi Josh,
I have a question on your suggestions of part1 patch as below:
1. Why do we have to use string key?
   The nsCookieKey also uses the base domain and origin suffix, could we use nsCookieKey as before?
   https://dxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsCookieService.h#97

Looking forward to your reply.
Thanks!
(Assignee)

Comment 132

a month ago
Created attachment 8870756 [details] [diff] [review]
implementation part1 -- Data Struct, implementation part1 --- Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I have modified the patch as below:
* nsDocShell::SandboxFlagsImplyCookies
    * Modified return condition.
* nsDocument::SetPrincipal()
    * Added if condition for confirming XRE_IsContentProcess()
    * If get the internalChannel faiil
        * Still notify parent using CookieServiceChild.
    * GetResponseSynthesized() == true.
        * Call init hash table on parent using CookieServiceChild.
    * Otherwise, do nothing.
* nsDocument::Destroy()
    * If mDocumentURI == mOriginURI
        * Have to only destroy the document which includes mDocumentURI.
* ContentParent.cpp
    * Removed static method calling.
    * Moved load flags condition to previous line.
* ContentChlid.cpp
    * Added a function ProcessHashTableOnCookieServiceChild() for calling the init hash table function and destroy function on CookieServiceChild.
* PCookieService.ipdl
    * Created TrackDocumentLoad and UntrackDocument on both.
* CookieServiceChild
    * Removed unnecessary comment.
    * Modified the argument on IPC function.
    * Override TrackDocumentLoa & UntrackDocumen.
    * Created init hash table function for sending notify to parent.
    * Created destroy function for sending notify to parent.
* CookieServiceParent
    * Override TrackDocumentLoa & UntrackDocumen.
    * Created init hash table function for sending notify to child.
    * Created destroy function for sending notify to child.
* DocumentCookieOperation=>DocumentTracker
    * Remove Init().
    * Revert the movement of  GetBaseDomain  and GetBaseDomainFromHttp
    * Created a function names “GenerateStringKey” to generate string key for document hash table.
    * Renamed UpdateDocumentToTable to TrackNewDocument
        * If found any existing  document info,
            * Overwrite it and mActiveDocuments++.
            * Return.
        * Otherwise, Call GenerateDocCookieInfo() then return.
        * If doesn’t exist any documents list
            * Create new one.
            * Create new documentCookieInfo
                * Assigned appropriate values to the fields.
            * Put documents list to hash table.
            * return.
    * Renamed DestroyDocumentFromTable to RemoveTrackedDocument
        * Added assertion when hash table can’t get documents list.
        * Modified RemoveAndForget to Remove().
    * NS_INLINE_DECL_REFCOUNTING(DocumentCookieOperation); still necessary.
        * If removed, the errors as below:
            * no member named 'AddRef' in 'mozilla::net::DocumentTracker.
            * No member named ‘Release’ in 'mozilla::net::DocumentTracker.
    * Created mDocMap for using it instead of mKnownCookies and mContentProcessCookies.
* moz.build
    * Removed DocumentCookieOperation
* nsCookieService
    * Removed GetCookieListInternal & GetCookieListStruct
    * Removed RefPtr<mozilla::DocumentCookieOperation>  mDocCookieOperation.
* NeckoChannelParams.ipdlh
    * Removed struct CookieStruct.

Would you give me your suggestions?
Thanks!
Attachment #8869564 - Attachment is obsolete: true
Flags: needinfo?(josh)
Attachment #8870756 - Flags: feedback?(josh)
(Assignee)

Updated

a month ago
Attachment #8870756 - Attachment description: bug-1331680-part1.patch → implementation part1 -- Data Struct, implementation part1 --- Update entry to Document HashTable, Remove entry from Document Hashtable.
(Assignee)

Comment 133

a month ago
Created attachment 8870772 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Sorry about forgetting to remove superfluous function.
Attachment #8870756 - Attachment is obsolete: true
Attachment #8870756 - Flags: feedback?(josh)
Attachment #8870772 - Flags: feedback?(josh)
(Assignee)

Comment 134

a month ago
Created attachment 8870827 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Hi,
I have modified my patch as below:
* Modified nits
* CookieServiceChild.cpp
    * loadInfo->GetTriggeringPrincipal =>GetChannelResultPrincipal()
    * Merged the if conditions  if (!mIPCSync) && if (mDocCookieOperation->
    * Created GetCookieStringIPCSync() for sending ipc function to parent.
    * Can’t use nsACString to instead of nsAutoCString
        * cannot initialize a parameter of type 'nsCString *' with an rvalue of type 'nsACString *'
* DocumentTracker
    * Created GetMatchingDocument to confirm the document already exists.
    * Removed assertion
    * Moved the CookiesList and CookiesMap to CookieServiceChild
    * GetCookieStringFromCookieHashTable have to move to CookieServiceChild.
* nsCookieService
    * CompareCookiesForSending moved to nsCookie.

Would you give me your suggestions?
Thanks!
Attachment #8869571 - Attachment is obsolete: true
Attachment #8870827 - Flags: feedback?(josh)
(Assignee)

Comment 135

a month ago
Created attachment 8870889 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
Sorry for uploading part1 patch again, I have modified the mCookiesPrent=false when the known document hashtable have to update the new document.

Thanks.
Attachment #8870772 - Attachment is obsolete: true
Attachment #8870772 - Flags: feedback?(josh)
Attachment #8870889 - Flags: feedback?(josh)
(Assignee)

Comment 136

a month ago
Created attachment 8870921 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I found I didn't confirm the TrackNewDocument be called by init or update document hash table, so I uploaded the part1 patch again.
I'm terribly sorry about that.
Update total modifications as below:
* nsDocShell::SandboxFlagsImplyCookies
    * Modified return condition.
* nsDocument::SetPrincipal()
    * Added if condition for confirming XRE_IsContentProcess()
    * If get the internalChannel faiil
        * Still notify parent using CookieServiceChild.
    * GetResponseSynthesized() == true.
        * Call init hash table on parent using CookieServiceChild.
    * Otherwise, do nothing.
* nsDocument::Destroy()
    * If mDocumentURI == mOriginURI
        * Have to only destroy the document which includes mDocumentURI.
* ContentParent.cpp
    * Removed static method calling.
    * Moved load flags condition to previous line.
* ContentChlid.cpp
    * Added a function ProcessHashTableOnCookieServiceChild() for calling the init hash table function and destroy function on CookieServiceChild.
* PCookieService.ipdl
    * Created TrackDocumentLoad and UntrackDocument on both.
* CookieServiceChild
    * Removed unnecessary comment.
    * Modified the argument on IPC function.
    * Override TrackDocumentLoa & UntrackDocumen.
    * Created init hash table function for sending notify to parent.
    * Created destroy function for sending notify to parent.
* CookieServiceParent
    * Override TrackDocumentLoa & UntrackDocumen.
    * Created init hash table function for sending notify to child.
    * Created destroy function for sending notify to child.
* DocumentCookieOperation=>DocumentTracker
    * Remove Init().
    * Revert the movement of  GetBaseDomain  and GetBaseDomainFromHttp
    * Created a function names “GenerateStringKey” to generate string key for document hash table.
    * Renamed UpdateDocumentToTable to TrackNewDocument
        * If found any existing  document info,
            * Overwrite it and mActiveDocuments++.
            * Return.
        * Otherwise, Call GenerateDocCookieInfo() then return.
        * If doesn’t exist any documents list
            * Create new one.
            * Create new documentCookieInfo
                * Assigned appropriate values to the fields.
                * If init
                    * mCookiesPresnet = true.
                * otherwise,
                    * mCookiesPresent = false.
            * Put documents list to hash table.
            * return.
    * Renamed DestroyDocumentFromTable to RemoveTrackedDocument
        * Added assertion when hash table can’t get documents list.
        * Modified RemoveAndForget to Remove().
    * NS_INLINE_DECL_REFCOUNTING(DocumentCookieOperation); still necessary.
        * If removed, the errors as below:
            * no member named 'AddRef' in 'mozilla::net::DocumentTracker.
            * No member named ‘Release’ in 'mozilla::net::DocumentTracker.
    * Created mDocMap for using it instead of mKnownCookies and mContentProcessCookies.
* moz.build
    * Removed DocumentCookieOperation
* nsCookieService
    * Removed GetCookieListInternal & GetCookieListStruct
    * Removed RefPtr<mozilla::DocumentCookieOperation>  mDocCookieOperation.
* NeckoChannelParams.ipdlh
    * Removed struct CookieStruct.
Attachment #8870889 - Attachment is obsolete: true
Attachment #8870889 - Flags: feedback?(josh)
Attachment #8870921 - Flags: feedback?(josh)
(Assignee)

Comment 137

a month ago
Created attachment 8870924 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Hi,
Because I uploaded part1 patch, the part2 patch have to modified the rejection part.
Sorry about the inconvenience.
Attachment #8870827 - Attachment is obsolete: true
Attachment #8870827 - Flags: feedback?(josh)
Attachment #8870924 - Flags: feedback?(josh)
(Assignee)

Comment 138

a month ago
Hi Josh,
I have a question about GetResponseSynthesized as below:
I found if we don't do anything when GetResponseSynthesized return false, when process destroy() then browser will crash.
Should we still set document to hash table using CookieServiceParent?
for example:
if (!synthesized && XRE_IsParentProcess()) {
  ContentParent::ProcessHashTableOnCookieServiceParent();
}

Looking forward to your answer, thanks!
Flags: needinfo?(josh)
Comment on attachment 8869845 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Review of attachment 8869845 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceParent.cpp
@@ +118,5 @@
> +  mCookieService->GetCookieStructList(aHostURI, isForeign, false, attrs, cookiesList);
> +  nsTArray<CookieStruct> matchingCookiesList;
> +  for (uint32_t i = 0; i < cookiesList.Length(); i++) {
> +    if (mCookieService->
> +          ConfirmMatchingCookie(nullptr, aHostURI, &(cookiesList[i]), aChannel)) {

No need for &().

::: netwerk/cookie/DocumentCookieOperation.cpp
@@ +170,5 @@
>    }
>    return NS_OK;
>  }
>  
>  bool

This method always returns true, so it can return void instead.

@@ +195,5 @@
> +    cookiesListExist = true;
> +    for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +      cookie = cookiesList->ElementAt(i);
> +      NS_ASSERTION(cookie, "could't get DocumentCookieInfo");
> +      if (cookie->Name().Equals(aCookie.name())) {

We need to check host, name, and path here.

@@ +205,5 @@
> +        cookie->SetLastAccessed(aCookie.lastAccessed());
> +        cookie->SetIsSession(aCookie.isSession());
> +        cookie->SetIsSecure(aCookie.isSecure());
> +        cookie->SetIsHttpOnly(aCookie.isHttpOnly());
> +        for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {

We only need to update a single entry from the known document list, and that is the one that matches aHostURI. We do not want to use the domain or path-matching algorithms here.

@@ +213,5 @@
> +            docCookieInfo->mCookiesPresent = true;
> +            break;
> +          }
> +        }
> +        break;

We should be able to return here avoid avoid checking cookieExist.

@@ +232,5 @@
> +                              aCookie.isSession(),
> +                              aCookie.isSecure(),
> +                              aCookie.isHttpOnly(),
> +                              aOriginAttrs);
> +    for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {

Same as above.

@@ +244,5 @@
> +    if (cookiesList) {
> +      cookiesList->AppendElement(cookie);
> +    }
> +    if (!cookiesListExist) {
> +      aCookiesMap.Put(key, cookiesList);

If we do this at the start of the function (create the hashtable entry if necessary), we don't need to have all of the if/else blocks later on.

@@ +343,5 @@
> +    // for each value in the document hashtable entry for the given domain
> +    // if the cookie path-matches against the value's path, ignore the cookie
> +    // if the cookie does not path-match against any of the known document
> +    // paths, remove the cookie
> +    if (cookiesList) {

We should only try to purge cookies if we are removing an element from the the list of documents. Otherwise we could end up removing cookies for documents that still exist.

@@ +347,5 @@
> +    if (cookiesList) {
> +      for (uint32_t j = 0; j < cookiesList->Length(); j++) {
> +        cookie = cookiesList->ElementAt(j);
> +        NS_ASSERTION(cookie, "could't get DocumentCookieInfo");
> +        if (docCookieInfo->mHostName.Equals(hostFromURI) &&

This is an odd condition to check every time.

@@ +349,5 @@
> +        cookie = cookiesList->ElementAt(j);
> +        NS_ASSERTION(cookie, "could't get DocumentCookieInfo");
> +        if (docCookieInfo->mHostName.Equals(hostFromURI) &&
> +            nsCookieService::DomainMatches(cookie, docCookieInfo->mHostName) &&
> +            !nsCookieService::PathMatches(cookie, docCookieInfo->mPathName)) {

This check is not enough - the cookie could match other entries in the list of known documents. We we remove an entry from the document table, we need to iterate over all of the domain's cookies and all of the documents and remove any cookies which do not match any of the known documents.

@@ +373,5 @@
>      aDocCookieMap.RemoveAndForget(key, rmDocList);
>    }
> +
> +  if (cookiesList && cookiesList->IsEmpty()) {
> +    cookiesList = nullptr;

No need to reset this pointer.

@@ +375,5 @@
> +
> +  if (cookiesList && cookiesList->IsEmpty()) {
> +    cookiesList = nullptr;
> +    nsAutoPtr<CookiesList> rmCookiesList;
> +    aCookiesMap->RemoveAndForget(key, rmCookiesList);

This can use Remove instead.

::: netwerk/cookie/DocumentCookieOperation.h
@@ +68,5 @@
> +                                    nsIURI *aHostURI,
> +                                    const OriginAttributes &aOriginAttrs);
> +
> +  bool
> +  UpdateCookieToTable(DocumentCookieMap &aDocCookieMap,

Let's call this RecordDocumentCookie.

::: netwerk/cookie/nsCookieService.cpp
@@ +4200,5 @@
> +    if (aChannel) {
> +      NS_GetOriginAttributes(aChannel, attrs);
> +    }
> +
> +    aCookie = nsCookie::Create(aCookieStruct->name(),

This is a memory leak. Let's make a separate method that accepts a CookieStruct argument and creates an nsCookie value, and make this method only accept |const nsCookie&| instead.

@@ +4223,5 @@
> +      !aCookie->IsHttpOnly()               &&
> +      aCookie->Expiry() > currentTime) {
> +    return true;
> +  }
> +  return false;

if (a && b) { return true; } else { return false }
is equivalent to
return a && b;

::: netwerk/cookie/nsCookieService.h
@@ +238,5 @@
>     */
>    static void AppClearDataObserverInit();
>    static bool DomainMatches(nsCookie* aCookie, const nsACString& aHost);
>    static bool PathMatches(nsCookie* aCookie, const nsACString& aPath);
> +  static bool ConfirmMatchingCookie(nsCookie *aCookie, nsIURI *aHostURI, mozilla::net::CookieStruct *aCookieStruct, nsIChannel *aChannel);

Let's call this IsCookieExposedToURI instead.
Attachment #8869845 - Flags: feedback?(josh) → feedback-
(Assignee)

Comment 140

a month ago
Created attachment 8871330 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi Josh,
I found some problems on two places of assertions:
1. RemoveTrackedDocument(), set a assertion on DocumentCookieListInfo can't find from document hash table.
   This assertion will occur on a document be destroyed but a website doesn't set a LOAD_DOCUMENT_NEEDS_COOKIE and GetResponseSynthesized().
   In my view, we can only confirm the DocumentCookieListInfo is null or not.
 
2. In AboutToLoadHttpFtpWyciwygDocumentForChild(), set a assertion on csParents.Length() == 1.
   I tried to load "cnn.com", and I found CookieServiceParent which doesn't create yet when AboutToLoadHttpFtpWyciwygDocumentForChild() be called.
   For the foregoing reasons, the csParents.Lenght() always equals 0 when the first time to call AboutToLoadHttpFtpWyciwygDocumentForChild(), and we can't update this document to hash table.
   Should we reserve the principal and document information when the timing of CookieServiceParent doesn't create yet and update this document information when CookieServiceParent already generates?

Would you give me your suggestions?
Thanks!
Attachment #8870921 - Attachment is obsolete: true
Attachment #8870921 - Flags: feedback?(josh)
Flags: needinfo?(josh)
Attachment #8871330 - Flags: feedback?(josh)
(Assignee)

Comment 141

a month ago
Created attachment 8871331 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Re-uploaded part 2 patch by rebasing.
The modification as below:
* Modified nits
* CookieServiceChild.cpp
    * loadInfo->GetTriggeringPrincipal =>GetChannelResultPrincipal()
    * Merged the if conditions  if (!mIPCSync) && if (mDocCookieOperation->
    * Created GetCookieStringIPCSync() for sending ipc function to parent.
    * Can’t use nsACString to instead of nsAutoCString
        * cannot initialize a parameter of type 'nsCString *' with an rvalue of type 'nsACString *'
* DocumentTracker
    * Created GetMatchingDocument to confirm the document already exists.
    * Removed assertion
    * Moved the CookiesList and CookiesMap to CookieServiceChild
    * GetCookieStringFromCookieHashTable have to move to CookieServiceChild.
* nsCookieService
    * CompareCookiesForSending moved to nsCookie.

Would you give me your suggestions?
Thanks!
Attachment #8870924 - Attachment is obsolete: true
Attachment #8870924 - Flags: feedback?(josh)
Attachment #8871331 - Flags: feedback?(josh)
(Assignee)

Comment 142

a month ago
Created attachment 8871375 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Hi,
I have modified part3 patch as below:
* DocumentTracker
    * UpdateCookieToTable =>RecordDocumentCookie
        * Modified the condition when the cookie exists.
            * Name is equal.
            * Path is equal.
            * Host is equal.
        *  If have a existing document and host name, path name which equal to the cookie and url schemes = cookie’s secure flag.
            * mCookiesPresent = true.
            * Return.
        * aCookiesMap.Put(key, cookiesList); => Moved to function start.
    * Separated a function about purging cookies which names RemoveTrackedCookie().
        * If the cookie which be stored in the cookie hash table doesn’t belong to any entry which be store in the document hash table.
            * Purge this cookie.
* CookieServiceParent
    * Modified nit and removed comment.
* nsCookieService
    * ConfirmMatchingCookie =>IsCookieExposedToURI
    * Modified the argument  of CookieStruct to call by reference.
    * Modified the return to “return a && b && cc…"

Would you give me your suggestions?
Thanks!
Attachment #8869845 - Attachment is obsolete: true
Attachment #8871375 - Flags: feedback?(josh)
(Assignee)

Comment 143

28 days ago
Created attachment 8872165 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Hi,
I have modified the RecordDcoumentCookie() when setting the existing cookie.
Would you give me your suggestions?
Thanks!
Attachment #8871375 - Attachment is obsolete: true
Attachment #8871375 - Flags: feedback?(josh)
Attachment #8872165 - Flags: feedback?(josh)
(Assignee)

Comment 144

28 days ago
Created attachment 8872169 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Hi,
I have finished the part 4 patch and modified the part 4 patch as below:
[nsCookieService]
    * Created a function names "SetCookieRules" which includes all the rules of SetCookieInternal and AddInternal.
    * Added some augments on CheckPref and modified CheckPref to static function. 
[CookieServiceChild]
    * SetCookieString
        * If the network.cookie.ipc.sync preference is false
            * If DocumentCookieMap can get DocumentCookieInfo from nsCookieKey(nsIprincipal, GetBaseDomian(nsCookie->Host())).
                * If mCookiesPresent=false
                    * Don’t store the nsCookie on CookieServiceChild.
                * If mCookiesPresent=true
                    * If nsCookieService::SetCookieRules(cookieHeader, setCookie) and setCookie is true
                        * Store the nsCookie.
            * If the nsCookie which can’t store to cookie hashtable, the CookieServiceChild still have to send ipc msg to CookieServiceParent for saving the nsCookie to DB.
        * Else if the network.cookie.ipc.sync preference is true
            * CookieServiceChild have to send ipc msg to CookieServiceParent for saving the nsCookie to DB.

Would you help me to review my patch?
Thanks!
Attachment #8872169 - Flags: feedback?(josh)
(Assignee)

Updated

28 days ago
Attachment #8872169 - Flags: feedback?(juhsu)
(Assignee)

Comment 145

28 days ago
Created attachment 8872177 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Hi,
Uploaded the part 4 patch for rebasing.
Attachment #8872169 - Attachment is obsolete: true
Attachment #8872169 - Flags: feedback?(juhsu)
Attachment #8872169 - Flags: feedback?(josh)
Attachment #8872177 - Flags: feedback?(josh)
(Assignee)

Updated

28 days ago
Attachment #8872177 - Flags: feedback?(juhsu)
(Assignee)

Comment 146

28 days ago
Created attachment 8872179 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

Hi,
I have modified my patch as below:
* [CookieServiceParent]
    * CookieServiceParent adds a observer for getting the “cookie-changed” or “private-cookie-changed” from nsCookieService.
        * If CookieServiceParent gets the notify which names “cookie-changed”  or “private-cookie-changed”  and aData == "delete"
            * Only have to confirm the aData = "delete", because the latest CookieServiceChild already set the http-only cookie. 
            * If DomainMatches(nsCookie, DocumentCookieInfo->mHostNam) && PathMathes(nsCookie, DocumentCookieInfo->mPathName)
                * Send the nsCookie to CookieServiceChild.

Would you give me your suggestions?
Thanks!
Attachment #8872179 - Flags: feedback?(josh)
(Assignee)

Updated

28 days ago
Attachment #8872179 - Flags: feedback?(juhsu)

Comment 147

28 days ago
Comment on attachment 8872177 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Review of attachment 8872177 [details] [diff] [review]:
-----------------------------------------------------------------

Two things make this patch a little mess
1. s/nsCookieAttributes/CookieStruct
2. send a CookieStruct in SendSetCookieString and nsCookieService::SetCookieStringInternal

It's up to :jdm and you.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +177,5 @@
>    if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefCookieIPCSync, &boolval)))
>      mIPCSync = !!boolval;
>  
> +  if (NS_SUCCEEDED(aPrefBranch->GetBoolPref(kPrefCookieIPCSync, &boolval)))
> +   mLeaveSecureAlone = boolval;

nit: indentation
not sure if we need to |!!boolval|, like the above cases

@@ +305,5 @@
>  
>  nsresult
> +CookieServiceChild::CountCookiesFromHashTable(nsIURI *aHostURI,
> +                                              const OriginAttributes &aOriginAttrs,
> +                                              int &aNumOfCookies)

We can not assume the caller should set aNumOfCookies = 0 before.
i.e., you should set it to 0 here

::: netwerk/cookie/nsCookieService.cpp
@@ +2062,5 @@
>  
>    nsDependentCString cookieString(aCookieHeader);
>    nsDependentCString serverTime(aServerTime ? aServerTime : "");
> +  SetCookieStringInternal(aHostURI, isForeign, nullptr, aCookieHeader,
> +                           serverTime, aFromHttp, attrs, aChannel);

indentation

@@ +2155,5 @@
> +                       aCookie->creationTime(),
> +                       aCookie->isSession(),
> +                       aCookie->isSecure(),
> +                       aCookie->isHttpOnly(),
> +                       key.mOriginAttributes);

Could you implement another nsCookie::Create(const CookieStruct&, const OA&)?

@@ +3415,5 @@
>  
>    if (!aCookieString.IsEmpty())
>      COOKIE_LOGSUCCESS(GET_COOKIE, aHostURI, aCookieString, nullptr, false);
>  }
>  

add comments

@@ +3433,2 @@
>  {
> +   NS_ASSERTION(aHostURI, "null host!");

indentation

@@ +3470,5 @@
>    nsresult rv = aHostURI->SchemeIs("https", &isHTTPS);
>    if (NS_SUCCEEDED(rv)) {
>      Telemetry::Accumulate(Telemetry::COOKIE_SCHEME_SECURITY,
> +                         ((aCookieStruct.isSecure())? 0x02 : 0x00) |
> +                           ((isHTTPS)? 0x01 : 0x00));

indentation

@@ +3647,5 @@
>    bool foundSecureExact = foundCookie && exactIter.Cookie()->IsSecure();
> +  bool isSecure = true;
> +  if (aHostURI && NS_FAILED(aHostURI->SchemeIs("https", &isSecure))) {
> +    isSecure = false;
> +  }

the local variable is unused

@@ +3974,5 @@
>  // Parses attributes from cookie header. expires/max-age attributes aren't folded into the
>  // cookie struct here, because we don't know which one to use until we've parsed the header.
>  bool
> +nsCookieService::ParseCookieStruct(nsDependentCString &aCookieHeader,
> +                                 CookieStruct &aCookie)

nit: indentation

::: netwerk/cookie/nsCookieService.h
@@ +263,2 @@
>      bool                          RequireThirdPartyCheck();
> +    static bool                          CheckDomain(CookieStruct &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch);

indentation

@@ +270,5 @@
>      bool                          FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
>      bool                          FindSecureCookie(const nsCookieKey& aKey, nsCookie* aCookie);
>      int64_t                       FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsIURI* aSource, const mozilla::Maybe<bool> &aIsSecure, nsListIter &aIter);
>      void                          TelemetryForEvictingStaleCookie(nsCookie* aEvicted, int64_t oldestCookieTime);
> +    static void                          NotifyRejected(nsIURI *aHostURI);

indentation
Attachment #8872177 - Flags: feedback?(juhsu) → feedback+

Comment 148

28 days ago
Comment on attachment 8872179 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

Review of attachment 8872179 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceParent.cpp
@@ +98,5 @@
> +                             const char      *aTopic,
> +                             const char16_t  *aData)
> +{
> +  if (!strcmp(aTopic, "cookie-changed")         ||
> +      !strcmp(aTopic, "private-cookie-changed")) {

bail-out first
question: no difference between cookie-changed and private-cookie-changed here?

@@ +131,5 @@
> +    bool requireHostMatch;
> +    nsCString baseDomain;
> +    mCookieService->GetBaseDomain(TLDService, uri, baseDomain, requireHostMatch);
> +    bool isUpdate = true;
> +    mozilla::OriginAttributes attrs = cookie->OriginAttributesRef();

is mozilla::OriginAttributes& better?

::: netwerk/cookie/DocumentTracker.cpp
@@ +109,5 @@
>    return;
>  }
>  
>  bool
> +DocumentTracker::ConfirmCookieDomainAndPathMatches(nsCookie          *aCookie,

const

@@ +119,5 @@
> +
> +  nsDependentCString key;
> +  GenerateStringKey(aBaseDomain, aOriginAttrs, key);
> +  mDocMap.Get(key, &docCookieInfoList);
> +  // Get the information from aHostURI

wrong annotation

::: netwerk/cookie/PCookieService.ipdl
@@ +104,5 @@
>    nested(inside_cpow) async TrackDocumentLoad(URIParams host,
>                                                nsCString baseDomain,
>                                                OriginAttributes attrs);
>  
> +  nested (inside_cpow) async UntrackDocument(URIParams host,

nested(inside_cpow)
Attachment #8872179 - Flags: feedback?(juhsu) → feedback+
Comment on attachment 8871330 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Review of attachment 8871330 [details] [diff] [review]:
-----------------------------------------------------------------

Getting close! My comments are now mostly about making the code more readable.

::: dom/base/nsDocument.cpp
@@ +2995,5 @@
> +      MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
> +    }
> +    bool isUpdate = true;
> +    if (synthesized) {
> +      // Init hash table on CookieServiceParent.

This comment isn't useful.

@@ +8760,5 @@
> +      bool isUpdate = false;
> +      ContentChild::ProcessHashTableOnCookieServiceChild(chan, mDocumentURI, principal, isUpdate);
> +
> +      // If mDocumentURI = mOriginalURI, we have to send mOriginalURI
> +      // to CookieServiceChild again.

This comment does not add anything to the code.

@@ +8763,5 @@
> +      // If mDocumentURI = mOriginalURI, we have to send mOriginalURI
> +      // to CookieServiceChild again.
> +      bool isURIEquals;
> +      mDocumentURI->Equals(mOriginalURI, &isURIEquals);
> +      if (!isURIEquals) {

I believe we only need to do a pointer comparison, not a string comparison.

::: dom/ipc/ContentChild.cpp
@@ +1835,5 @@
> +                                                   nsIPrincipal *aPrincipal,
> +                                                   const bool   &aIsUpdate)
> +{
> +  RefPtr<CookieServiceChild> csChild =
> +    already_AddRefed<CookieServiceChild>(CookieServiceChild::GetSingleton());

No need for the already_AddRefed here.

::: dom/ipc/ContentChild.h
@@ +142,5 @@
>    bool IsShuttingDown() const;
>  
>    static void AppendProcessId(nsACString& aName);
>  
> +  static void ProcessHashTableOnCookieServiceChild(nsIChannel *aChannel, nsIURI *aHostURI, nsIPrincipal *aPrincipal, const bool &aIsUpdate);

Let's call this `UpdateDocumentStatus`. Also, instead of `const bool &aIsUpdate`, let's have `bool aNewDocument`.

::: dom/ipc/ContentParent.cpp
@@ +5074,5 @@
>  }
>  
> +void
> +ContentParent::ProcessHashTableOnCookieServiceParent(nsIChannel *aChannel,
> +                                                     nsIURI *aHostURI,

Why do we accept a nsIURI argument if we only pass nullptr to it?

@@ +5076,5 @@
> +void
> +ContentParent::ProcessHashTableOnCookieServiceParent(nsIChannel *aChannel,
> +                                                     nsIURI *aHostURI,
> +                                                     nsIPrincipal *aPrincipal,
> +                                                     const bool &aIsUpdate)

Why do we accept a boolean argument if we only pass true to it?

@@ +5078,5 @@
> +                                                     nsIURI *aHostURI,
> +                                                     nsIPrincipal *aPrincipal,
> +                                                     const bool &aIsUpdate)
> +{
> +  for (auto* cp : AllProcesses(eLive)) {

We do not need to iterate over all processes. This is not a static method, so we already have a single ContentParent which will let us find an appropriate CookieServiceParent.

@@ +5087,5 @@
> +    nsTArray<PCookieServiceParent*> csParents;
> +    // Accumulate kids into a stable structure to iterate over
> +    neckoParent->ManagedPCookieServiceParent(csParents);
> +    for (auto& csParent : csParents) {
> +      auto *cs = static_cast<CookieServiceParent*>(csParent);

You should be able to use SingleManagedOrNull(neckoParent->ManagedPCookieServiceParent()) here instead of the loop.

@@ +5133,5 @@
> +  aChannel->GetLoadFlags(&newLoadFlags);
> +  bool isDocument = false;
> +  aChannel->GetIsDocument(&isDocument);
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +      isDocument) {

I think this can go on the previous line.

::: dom/ipc/ContentParent.h
@@ +200,5 @@
>  
> +  static void ProcessHashTableOnCookieServiceParent(nsIChannel *aChnnel,
> +                                                    nsIURI *aHostURI,
> +                                                    nsIPrincipal *aPrincipal,
> +                                                    const bool& aIsUpdate);

Let's call this `UpdateDocumentStatus`. Also, instead of `const bool& aIsUpdate`, use `bool aIsNewDocument`.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +78,5 @@
>  
> +nsresult
> +CookieServiceChild::TrackDocumentLoadOnChild(nsIPrincipal *aPrincipal,
> +                                             nsIURI       *aHostURI,
> +                                             nsIChannel   *aChannel)

aChannel is not used.

@@ +97,5 @@
> +
> +nsresult
> +CookieServiceChild::UntrackDocumentOnChild(nsIPrincipal *aPrincipal,
> +                                           nsIURI       *aHostURI,
> +                                           nsIChannel   *aChannel)

aChannel is not used.

@@ +120,5 @@
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child.
> +  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
> +  // Update document to hash table

These comments do not need to be here.

@@ +134,5 @@
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child.
> +  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
> +  // Destroy document from hash table

These comments do not need to be here.

::: netwerk/cookie/CookieServiceChild.h
@@ +37,5 @@
> +                           nsIURI *aHostURI,
> +                           nsIChannel *aChannel);
> +
> +  nsresult
> +  UntrackDocumentOnChild(nsIPrincipal *aPrincipal,

No code checks the return values of these methods, so they can return void.

@@ +75,5 @@
>    void PrefChanged(nsIPrefBranch *aPrefBranch);
>  
>    bool RequireThirdPartyCheck();
>  
> +  RefPtr<DocumentTracker> mDocCookieOperation;

This does not need to be a RefPtr.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +85,5 @@
>  
> +nsresult
> +CookieServiceParent::UntrackDocumentOnParent(nsIPrincipal *aPrincipal,
> +                                             nsIURI *aHostURI,
> +                                              nsIChannel *aChannel)

The channel is not used.

@@ +104,5 @@
> +
> +nsresult
> +CookieServiceParent::TrackDocumentLoadOnParent(nsIPrincipal *aPrincipal,
> +                                           nsIURI *aHostURI,
> +                                           nsIChannel *aChannel)

The channel is not used.

@@ +129,5 @@
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the parent.
> +  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
> +  // Update document to hash table

These comments are not necessary.

@@ +143,5 @@
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the parent.
> +  nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
> +  // Destroy document from hash table

These comments are not necessary.

::: netwerk/cookie/CookieServiceParent.h
@@ +25,5 @@
> +  nsresult TrackDocumentLoadOnParent(nsIPrincipal *aPrincipal,
> +                                     nsIURI *aHostURI,
> +                                     nsIChannel *aChannel);
> +
> +  nsresult UntrackDocumentOnParent(nsIPrincipal *aPrincipal,

No code checks the return values of these methods, so they can return void.

@@ +55,5 @@
>                                                        const nsCString& aServerTime,
>                                                        const OriginAttributes& aAttrs) override;
>  
>    RefPtr<nsCookieService> mCookieService;
> +  RefPtr<DocumentTracker> mDocCookieOperation;

This does not need to be a RefPtr. Also, let's call it `mDocumentTracker`.

::: netwerk/cookie/DocumentTracker.cpp
@@ +36,5 @@
> +
> +  nsDependentCString key;
> +  GenerateStringKey(aBaseDomain, aAttrs, key);
> +  mDocMap.Get(key, &docCookieInfoList);
> +  // Get the information from aHostURI

This comment is not necessary.

@@ +55,5 @@
> +      }
> +    }
> +  } else {
> +    docCookieInfoList = new DocumentCookieInfoList;
> +    docCookieInfo = new DocumentCookieInfo;

Instead of duplicating the code from below, we can remove all of it (except allocating docCookieInfoList and adding it to mDocMap) and allow the code to fall through to the next block.

@@ +76,5 @@
> +    mDocMap.Put(key, docCookieInfoList);
> +    return true;
> +  }
> +
> +  docCookieInfo = new DocumentCookieInfo;

This is a memory leak.

@@ +82,5 @@
> +  // mHostName have to set to the host of nsIURI;
> +  // mPathName have to set to the path of nsIRUI;
> +  // mCookiesPresnet default value is true;
> +  // Because docCookieInfoList is Newly established list, mActiveDocuments
> +  // of docCookieInfo should be set to initial value.

These comments don't tell me anything more than what the code is doing. Let's remove them.

@@ +92,5 @@
> +  } else {
> +    docCookieInfo->mCookiesPresent = false;
> +  }
> +  docCookieInfo->mActiveDocuments = 1;
> +  docCookieInfoList->AppendElement(*docCookieInfo);

Instead, let's do this:
docCookieInfo = docCookieInfoList->AppendElement();
docCookieInfo->mHostName = hostFromURI;
docCookieInfo->mPathName = pathFromURI;
...etc...

@@ +103,5 @@
> +                                       const nsCString        &aBaseDomain,
> +                                       const OriginAttributes &aAttrs)
> +{
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;
> +  DocumentCookieInfo *docCookieInfo = nullptr;

Let's declare this when we need it, instead.

@@ +107,5 @@
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +  nsDependentCString key;
> +  GenerateStringKey(aBaseDomain, aAttrs, key);
> +  mDocMap.Get(key, &docCookieInfoList);
> +  if (!docCookieInfoList) {

We should be able to assert that there is an entry here.

@@ +119,5 @@
> +  aHostURI->GetPath(pathFromURI);
> +  aHostURI->SchemeIs("https", &isHTTPS);
> +
> +  for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +    docCookieInfo = &docCookieInfoList->ElementAt(i);

DocumentCookieInfo* docCookieInfo = ...;

::: netwerk/cookie/DocumentTracker.h
@@ +16,5 @@
> +
> +struct DocumentCookieInfo
> +{
> +  DocumentCookieInfo()
> +  {}

We should initialize sensible defaults here for mActiveDocuments, mSecure, and mCookiesPresent.

@@ +41,5 @@
> +class DocumentTracker
> +{
> +public:
> +  DocumentTracker();
> +  NS_INLINE_DECL_REFCOUNTING(DocumentTracker);

This class does not need to be reference counted.

@@ +46,5 @@
> +
> +  typedef nsTArray<DocumentCookieInfo> DocumentCookieInfoList;
> +  typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;
> +
> +  DocumentCookieMap mDocMap;

This should not be a public member.

@@ +48,5 @@
> +  typedef nsClassHashtable<nsCStringHashKey, DocumentCookieInfoList> DocumentCookieMap;
> +
> +  DocumentCookieMap mDocMap;
> +
> +  bool

No code checks the return value, so this should return void.

@@ +54,5 @@
> +                   const nsCString &aBaseDomain,
> +                   const OriginAttributes &aAttrs,
> +                   const bool &aIsInit);
> +
> +  bool

No code checks the return value, so this should return void.

::: netwerk/cookie/PCookieService.ipdl
@@ +108,5 @@
> +                          OriginAttributes attrs);
> +
> +  async UntrackDocument(URIParams host,
> +                        nsCString baseDomain,
> +                        OriginAttributes attrs);

I don't believe we ever need to send UntrackDocument from parent->child.

::: netwerk/cookie/nsCookieService.h
@@ +211,5 @@
>     */
>    static void AppClearDataObserverInit();
>  
> +  static nsresult GetBaseDomain(nsIEffectiveTLDService *aTLDService, nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
> +  static nsresult GetBaseDomainFromHost(nsIEffectiveTLDService *aTLDService, const nsACString &aHost, nsCString &aBaseDomain);

I think this change belongs in a different patch.
Attachment #8871330 - Flags: feedback?(josh) → feedback-
Comment on attachment 8871331 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Review of attachment 8871331 [details] [diff] [review]:
-----------------------------------------------------------------

Most of the changes here are small ones to increase readability. I would like to see what the patch looks like after changing GetMatchingDocument, however.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +178,5 @@
>      mCookieBehavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN ||
>      mThirdPartySession;
>  }
>  
>  nsresult

This always returns NS_OK, so we can return void instead.

@@ +203,5 @@
> +    return NS_OK;
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);

Let's declare cookie here instead - `nsCookie* cookie = nullptr;`

@@ +204,5 @@
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);
> +    NS_ASSERTION(cookie, "Get DocumentCookieInfo fail");

This assertion is not necessary. We can't have a null pointer here.

@@ +223,5 @@
> +}
> +
> +nsresult
> +CookieServiceChild::GetCookieStringIPCSync(const URIParams &aHost,
> +                                           const bool &aIsForegin,

`bool aIsForeign`

@@ +269,5 @@
>      }
> +    nsCOMPtr<nsIPrincipal> principal;
> +    nsContentUtils::GetSecurityManager()->
> +              GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
> +    principalAttrs = principal->OriginAttributesRef();

We can use the existing attrs here instead.

::: netwerk/cookie/CookieServiceChild.h
@@ +76,5 @@
>    nsresult GetCookieStringInternal(nsIURI *aHostURI,
>                                     nsIChannel *aChannel,
>                                     char **aCookieString);
>  
> +  nsresult GetCookieStringFromCookieHashTable(CookiesMap &aCookiesMap,

This does not need to take the map as an argument.

::: netwerk/cookie/DocumentTracker.cpp
@@ +24,5 @@
>    aAttrs.CreateSuffix(suffix);
>    aKey.Append(suffix);
>  }
>  
> +void

My previous review said that this should return DocumentCookieInfo* to reduce the duplication of code for "get the existing matching document from the hashtable".

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'nsCookieKey.h',

I don't think this needs to be exported.

::: netwerk/cookie/nsCookie.h
@@ +169,5 @@
> +    // browser!  see bug 236772.
> +    return aCookie1->CreationTime() < aCookie2->CreationTime();
> +  }
> +};
> +

nit: remove this extra newline.
Attachment #8871331 - Flags: feedback?(josh) → feedback-
Comment on attachment 8872165 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Review of attachment 8872165 [details] [diff] [review]:
-----------------------------------------------------------------

Some reorganization of the code will make it more efficient and readable. Additionally, we are still not implementing the algorithm for purging correctly.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +31,5 @@
>  static const char kPrefThirdPartySession[] =
>    "network.cookie.thirdparty.sessionOnly";
>  static const char kPrefCookieIPCSync[] = "network.cookie.ipc.sync";
>  
> +DocumentTracker::CookiesMap mCookiesMap;

Why are we declaring a global variable here? We should be using a member variable of CookieServiceChild instead.

@@ +113,5 @@
>    OriginAttributes attrs = aPrincipal->OriginAttributesRef();
>    nsAutoCString baseDomain;
>    aPrincipal->GetBaseDomain(baseDomain);
>    mDocCookieOperation->RemoveTrackedDocument(aHostURI, baseDomain, attrs);
> +  mDocCookieOperation->RemoveTrackedCookies(mCookiesMap, baseDomain, attrs);

We should only need to purge cookies if we removed a DocumentCookieInfo entry from the table of known documents in RemoveTrackedDocument. Let's return a boolean value and use that to choose whether to call RemoveTrackedCookies.

@@ +124,5 @@
>  }
>  
>  mozilla::ipc::IPCResult
> +CookieServiceChild::RecvTrackDocAndCookiesLoad(const URIParams        &aHost,
> +                                               nsTArray<mozilla::net::CookieStruct>&& aCookiesList,

Let's add `use mozilla::net::CookieStruct;` at the top of the file.

::: netwerk/cookie/CookieServiceChild.h
@@ +5,5 @@
>  
>  #ifndef mozilla_net_CookieServiceChild_h__
>  #define mozilla_net_CookieServiceChild_h__
>  
> +#include "mozilla/net/NeckoChannelParams.h"

We should be able to forward-declare mozilla::net::CookieStruct instead of including this header.

::: netwerk/cookie/DocumentTracker.cpp
@@ +52,5 @@
> +  nsDependentCString docKey;
> +  GenerateStringKey(aBaseDomain, aAttrs, docKey);
> +  mDocMap.Get(docKey, &docCookieInfoList);
> +  if (!cookiesList) {
> +    cookiesList = new CookiesList;

This is a memory leak.

@@ +77,5 @@
> +        break;
> +      }
> +    }
> +
> +    cookie = nsCookie::Create(aCookie.name(),

To avoid the duplicated code here, let's do this:

if (!cookiesList) {
  cookiesList = aCookiesMap->PutEntry(key);
}

for (uint32_t i = 0; i < cookiesList->Length(); i++) {
  //...
}

RefPtr<nsCookie> cookie = nsCookie::Create(...);
cookiesList->AppendElement(cookie);

@@ +91,5 @@
> +                              aAttrs);
> +    cookiesList->AppendElement(cookie);
> +  }
> +
> +  if (!docCookieInfoList) {

If we do not know anything about this document, we should not store any cookies related to it. This check should happen much earlier.

@@ +96,5 @@
> +    return;
> +  }
> +
> +  for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {
> +    docCookieInfo = &(docCookieInfoList->ElementAt(j));

nit: no need for the surrounding ().

@@ +105,5 @@
> +      return;
> +    }
> +  }
> +
> +  return;

This is unnecessary.

@@ +223,5 @@
> +  nsDependentCString docKey;
> +  GenerateStringKey(aBaseDomain, aAttrs, docKey);
> +  mDocMap.Get(docKey, &docCookieInfoList);
> +  bool cookieExist = false;
> +  if (!cookiesList) {

Let's move this right after the operation that fetches cookiesList.

@@ +227,5 @@
> +  if (!cookiesList) {
> +    return;
> +  }
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);

nsCookie* cookie = cookiesList->ElementAt(i);

@@ +230,5 @@
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);
> +    if (docCookieInfoList) {
> +      for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {
> +        docCookieInfo = &(docCookieInfoList->ElementAt(j));

DocumentCookieInfo* docCookieInfo = &docCookieInfoList->ElementAt(j);

@@ +231,5 @@
> +    cookie = cookiesList->ElementAt(i);
> +    if (docCookieInfoList) {
> +      for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {
> +        docCookieInfo = &(docCookieInfoList->ElementAt(j));
> +        if (docCookieInfo->mHostName.Equals(cookie->Host()) &&

These conditions do not verify if a cookie is exposed to a given document. That check is equivalent to the new IsCookieExposedToURI that we added in nsCookieService. We should store a nsCOMPtr<nsIURI> in DocumentCookieInfo so we can perform this check properly.

@@ +240,5 @@
> +          break;
> +        }
> +      }
> +    }
> +    if (cookieExist) {

This is incorrect - for each cookie, if we find a document that can see that cookie, we want to ignore it. If there are no documents that can see the cookie, only then should we remove it.

@@ +241,5 @@
> +        }
> +      }
> +    }
> +    if (cookieExist) {
> +      cookiesList->RemoveElementAt(i);

This loop is dangerous because we are iterating from left to right while removing elements, which will cause us to skip the following cookie each time we remove one. We should iterate from cookiesList->Length() down to 0, and access the element at i-1 instead.

@@ +288,5 @@
>    if (docCookieInfoList->IsEmpty()) {
>      mDocMap.Remove(key);
>    }
> +
> +  return;

This is unnecessary now.

::: netwerk/cookie/DocumentTracker.h
@@ +59,5 @@
>                     const OriginAttributes &aAttrs,
>                     const bool &aIsInit);
>  
> +  void
> +  RecordDocumentCookie(CookiesMap &aCookiesMap,

This belongs in CookieServiceChild, since it is only used by the content process. It can accept a `const DocumentCookieMap&` argument instead.

::: netwerk/cookie/PCookieService.ipdl
@@ +99,5 @@
>                                              nsCString cookieString,
>                                              nsCString serverTime,
>                                              OriginAttributes attrs);
>  
> +  nested(inside_cpow) async TrackDocumentLoad(URIParams host,

Is nested(inside_cpow) necessary?

::: netwerk/cookie/nsCookieService.cpp
@@ +3320,5 @@
> +  aCookieList.Sort(CompareCookiesForSending());
> +}
> +
> +void
> +nsCookieService::GetCookieStructList(nsIURI *aHostURI,

Right now we get an array of nsCookie values, convert them to an array of CookieStruct values, then check each one by creating a new nsCookie value. Instead, let's rename GetCookieListInternal to GetCookiesForURI, and make CookieServiceParent wait to convert the nsCookie values into CookieStruct values until after we have a final list of cookies for the child.

@@ +4266,5 @@
>  
> +// If parent has to set a cookie on cookie hash table which includes in child.
> +// parent needs to confirm the cookie which matches all coditions as below:
> +// 1. DomainMatch, 2. PathMatch, 3. cookie's secure flag matches whether the
> +// final channel URI is https, 4. not http-only, 5. not expired yet.

I think a comment like this is more useful:
// Determine if a given cookie should be visible from a given URI loaded in a child process.
// This matches the usual checks performed on cookies, but also restricts httponly cookies from being exposed.

@@ +4268,5 @@
> +// parent needs to confirm the cookie which matches all coditions as below:
> +// 1. DomainMatch, 2. PathMatch, 3. cookie's secure flag matches whether the
> +// final channel URI is https, 4. not http-only, 5. not expired yet.
> +bool
> +nsCookieService::IsCookieExposedToURI(nsIURI                           *aHostURI,

This method belongs on CookieServiceParent, since it does not use any non-public part of nsCookieService.

@@ +4270,5 @@
> +// final channel URI is https, 4. not http-only, 5. not expired yet.
> +bool
> +nsCookieService::IsCookieExposedToURI(nsIURI                           *aHostURI,
> +                                      mozilla::net::CookieStruct       &aCookieStruct,
> +                                      nsIChannel                       *aChannel)

We should not need this if we accept nsCookie* instead of CookieStruct&.

@@ +4300,5 @@
> +  aHostURI->GetAsciiHost(sourceHost);
> +  aHostURI->SchemeIs("https", &isSecureURI);
> +  return DomainMatches(cookie, sourceHost)   &&
> +         PathMatches(cookie, sourcePath)     &&
> +         (isSecureURI == cookie->IsSecure()) &&

I don't think this is correct - we _do_ want non-secure cookies to be exposed to secure URIs, but we _don't_ want secure cookies to be exposed to insecure URIs.

::: netwerk/cookie/nsCookieService.h
@@ +31,5 @@
>  #include "nsIFile.h"
>  #include "mozilla/BasePrincipal.h"
>  #include "mozilla/MemoryReporting.h"
>  #include "mozilla/Maybe.h"
> +#include "mozilla/net/NeckoChannelParams.h"

I think we can forward-declare CookieStruct instead of including this header.

::: netwerk/ipc/NeckoChannelParams.ipdlh
@@ +188,5 @@
>    HttpChannelDiverterArgs;
>    PFTPChannel;
>  };
>  
> +// For OnStartRequest Parent -> Child

This comment is not necessary.
Attachment #8872165 - Flags: feedback?(josh) → feedback-
Comment on attachment 8872177 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Review of attachment 8872177 [details] [diff] [review]:
-----------------------------------------------------------------

I am pausing my review here because I would like to understand why some of the changes were made, since they affect almost every part of the patch.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +302,5 @@
>  
>    return NS_OK;
>  }
>  
>  nsresult

We always return NS_OK, so we can return the number of cookies instead.

@@ +310,5 @@
> +{
> +  DocumentTracker::CookiesList *cookiesList = nullptr;
> +  nsCookie *cookie = nullptr;
> +
> +  // Generate nsIEffectiveTLDService for getting base domain on aHostURI.

This comment is unnecessary.

@@ +327,5 @@
> +
> +  if (!cookiesList) {
> +    return NS_OK;
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());

We don't need to sort cookies just to count them.

@@ +330,5 @@
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    cookie = cookiesList->ElementAt(i);
> +    if (cookie->Host().Equals(hostFromURI)) {

nsCookieService::CountCookiesFromHost just returns the number of cookies that exist for a base domain. We should do the same here.

@@ +380,5 @@
> +  CookieStruct cookieStruct;
> +
> +  bool requireHostMatch;
> +  nsCString baseDomain;
> +  // Generate nsIEffectiveTLDService for getting base domain on aHostURI.

This comment is unnecessary.

@@ +382,5 @@
> +  bool requireHostMatch;
> +  nsCString baseDomain;
> +  // Generate nsIEffectiveTLDService for getting base domain on aHostURI.
> +  nsCOMPtr<nsIEffectiveTLDService> TLDService =
> +    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);

Why not store this as a member instead of getting it in so many places?

::: netwerk/cookie/CookieServiceParent.cpp
@@ +219,5 @@
>    CreateDummyChannel(hostURI, const_cast<OriginAttributes&>(aAttrs),
>                       getter_AddRefs(dummyChannel));
>  
>    // NB: dummyChannel could be null if something failed in CreateDummyChannel.
> +  mCookieService->SetCookieStringInternal(hostURI, aIsForeign, &aCookie, nullptr,

This change concerns me. I don't understand why we have stopped sending the cookie string from the child.

::: netwerk/cookie/nsCookieService.cpp
@@ -138,5 @@
>                       const nsCookieKey &aKey,
>                       const nsCookie *aCookie);
>  
> -// struct for temporarily storing cookie attributes during header parsing
> -struct nsCookieAttributes

I am having trouble understanding why we are removing this structure and rewriting all of the nsCookieService code to operate on CookieStruct values instead. Before I spend time looking through all of the changes, I would like to understand why this is necessary. It seems like it would be easier to create an instance of nsCookieAttributes from a CookieStruct value.

@@ +2095,5 @@
>    bool requireHostMatch;
>    nsAutoCString baseDomain;
>    nsresult rv = GetBaseDomain(mTLDService, aHostURI, baseDomain, requireHostMatch);
>    if (NS_FAILED(rv)) {
> +    COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, nullptr,

Why this change?

@@ +4342,5 @@
>  // 1. DomainMatch, 2. PathMatch, 3. cookie's secure flag matches whether the
>  // final channel URI is https, 4. not http-only, 5. not expired yet.
>  bool
> +nsCookieService::IsCookieExposedToURI(nsIURI       *aHostURI,
> +                                      CookieStruct &aCookie,

This is already a large patch. Extra changes such as renaming arguments make it larger and harder to review. Please include them in a separate patch if necessary.

::: netwerk/cookie/nsCookieService.h
@@ +49,5 @@
>  class mozIStorageService;
>  class mozIThirdPartyUtil;
>  class ReadCookieDBListener;
>  
> +struct CookieStrcut;

This does nothing - it's misspelled, and it's not in the mozilla::net namespace. We should remove the header to make these problems appear.
Attachment #8872177 - Flags: feedback?(josh) → feedback-
Comment on attachment 8872179 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

Review of attachment 8872179 [details] [diff] [review]:
-----------------------------------------------------------------

I am halting my review of this patch right now because it looks like the most important part is not working correctly.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +103,5 @@
> +    if (!aData || !aSubject) {
> +      return NS_ERROR_UNEXPECTED;
> +    }
> +
> +    if (!nsCRT::strncmp(aData, u"batch-deleted", 1)) {

This seems like something we need to handle. All of the notifications in https://dxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsCookieService.cpp#2238-2244 are important, because they will cause the content process' local cookie cache to be out of sync with the parent process.

@@ +133,5 @@
> +    mCookieService->GetBaseDomain(TLDService, uri, baseDomain, requireHostMatch);
> +    bool isUpdate = true;
> +    mozilla::OriginAttributes attrs = cookie->OriginAttributesRef();
> +
> +    if (!nsCRT::strncmp(aData, u"deleted", 1)) {

I don't understand what is going on in this method.
1) Why do we ignore updates besides deleting? What about cookies that are set/updated in the parent process that the child does not know about (such as in an HTTP response)?
2) How does calling SendUntrackDocAndCookie help? We do not send the name of the cookie that was deleted; how is the content process supposed to delete the right cookie?
Attachment #8872179 - Flags: feedback?(josh) → feedback-
Comment on attachment 8867902 [details] [diff] [review]
test cases part6

Review of attachment 8867902 [details] [diff] [review]:
-----------------------------------------------------------------

These tests are a good start. We need to be more careful about the ordering of the tests, and we will need tests that verify that the content process rejects cookies that the parent process will reject.

::: netwerk/test/mochitests/file_1331680.js
@@ +26,5 @@
> +  var foundCookies = [];
> +  var cookieString = "";
> +  var index = 0;
> +  while(enumerator.hasMoreElements()) {
> +    foundCookies[index] = enumerator.getNext().QueryInterface(Ci.nsICookie2);

foundCookies.push(...) is more idiomatic.

@@ +37,5 @@
> +      cookieString += "; ";
> +  }
> +  sendAsyncMessage("getCookies:return", cookieString);
> +});
> +/*

I am not sure what to make of these big sections of commented out code.

::: netwerk/test/mochitests/file_iframe_allow_same_origin.html
@@ +2,5 @@
> +<html>
> +<script>
> +document.cookie = "if2_1=123";
> +document.cookie = "if2_2=123";
> +window.parent.postMessage("finish", 'http://mochi.test:8888');

This should be able to use '*' instead (possibly it does not even need a second argument). We should also postMessage the value of document.cookies.

::: netwerk/test/mochitests/file_iframe_allow_scripts.html
@@ +1,4 @@
> +<!DOCTYPE html>
> +<html>
> +<script>
> +document.cookie = "if1=123";

We should postMessage the value of document.cookies back to the parent.

::: netwerk/test/mochitests/test_1331680.html
@@ +26,5 @@
> +  gScript.addMessageListener("getCookies:return", function(e) {
> +    var cookieString = document.cookie;
> +    is(e, cookieString, "Confirm the cookies which be got from parent process and child process are same.");
> +    switch (gTestItems) {
> +      case TestItemsEnum.XHR:

We should assert that the test is running that we expect.

@@ +64,5 @@
> +
> +  /* Test document.cookie
> +   * 1. Set a cookie and confirm observer can get the nofity which is "cookie-changed".
> +   * 2. Set a cookie which set httponly and confirm observer can't get the notify which is "cookie-changed".
> +   * 3. Set a cookie and get cookie.

This comment is no longer true, since we're not checking the observer.

@@ +88,5 @@
> +    gTestItems = TestItemsEnum.XHR;
> +    var xhr = [];
> +    for (var i = 0; i < 2; i++) {
> +      xhr[i] = new XMLHttpRequest();
> +      if (i == 0)

Rather than a loop with hardcoded indexes like this, we should just write the code for two XHRs separately.

@@ +94,5 @@
> +      else
> +        xhr[i].open("GET", 'user_agent_1331680_xhr2.sjs', true); // async request
> +      xhr[i].send();
> +    }
> +    xhr[1].onload = function onload() {

The XHRs can race each other and may finish out of order. It is safer to start the second XHR after the first one is complete to guarantee the right order.

@@ +106,5 @@
> +  const ID = ["if_1", "if_2"];
> +  function create_iframe(id, src, sandbox_flags) {
> +     var iframeEl = document.createElement("iframe");
> +     iframeEl.id   = id;
> +     iframeEl.name = id;

Why do we set the name and id?

@@ +112,5 @@
> +     iframeEl.sandbox = sandbox_flags;
> +     document.body.appendChild(iframeEl);
> +  };
> +
> +  function delete_iframes(id) {

I don't think it makes sense to have a function for this, since most of the code is focused on the second iframe.

@@ +124,5 @@
> +        for (var i = 1; i < IFRAME_COOKIE_NAMES.length; i++) {
> +          document.cookie = COOKIE_NAMES[i] + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +        }
> +      } else {
> +        delete window.frames.if_1;

What is the purpose of these delete statements?

@@ +138,5 @@
> +    gTestsNum = 0;
> +    create_iframe(ID[0], "file_iframe_allow_scripts.html", "allow-scripts");
> +    create_iframe(ID[1], "file_iframe_allow_same_origin.html", "allow-scripts allow-same-origin");
> +    delete_iframes(ID[0]);
> +    delete_iframes(ID[1]);

I think we need to be more careful here, since both iframes can race to load first. We should load one iframe, then load the other once the first is done.

@@ +142,5 @@
> +    delete_iframes(ID[1]);
> +  }
> +  testSetCookie();
> +  testXHR();
> +  testIframe();

Each of these tests should wait to start until the previous one is complete, or they will interfere with each other.

::: netwerk/test/mochitests/user_agent_1331680_xhr1.sjs
@@ +2,5 @@
> +function handleRequest(request, response)
> +{
> +    // avoid confusing cache behaviors
> +    response.setHeader("Cache-Control", "no-cache", false);
> +    response.setHeader("Content-Type", "text/html", false);

These headers and the comment are not part of this test. The filename is also unrelated, since this test not about user agents. A better name would be "set_cookie_xhr1.sjs".

::: netwerk/test/mochitests/user_agent_1331680_xhr2.sjs
@@ +2,5 @@
> +function handleRequest(request, response)
> +{
> +    // avoid confusing cache behaviors
> +    response.setHeader("Cache-Control", "no-cache", false);
> +    response.setHeader("Content-Type", "text/html", false);

Same comments as the other xhr sjs file.
Attachment #8867902 - Flags: feedback?(josh) → feedback-
Blocks: 1368835
No longer blocks: 1368835
I have a profile from facebook.com in which PCookieService::Msg_GetCookieString takes 17% of the total time.

FB is sometimes utterly unusable for me on a quad-core 2017 Mac, in part because each network request — and Facebook makes a lot! — essentially hangs the browser. (It's also terrible in all kinds of other ways, but this one stands out!)

I assume that you don't need an additional profile, but do let me know if I can help in any way.

Updated

25 days ago
Blocks: 1366960
(Assignee)

Comment 156

24 days ago
Created attachment 8873645 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I have modified this patch as below:
[Not Modification]
* DocumentTracker
    * RemoveTrackedDocument(), set a assertion on DocumentCookieListInfo can't find from document hash table.
    *    This assertion will occur on a document be destroyed but a website doesn't set a LOAD_DOCUMENT_NEEDS_COOKIE and GetResponseSynthesized().
    *    In my view, we can only confirm the DocumentCookieListInfo is null or not.
[Modification]
* nsDocument.cpp
    * On Destroy()
        * If (mOringinURI) is true
            * Tried to destroy document from the document hash table.
* ContentChild
    * Modified ProcessHashTableOnCookieServiceChild to UpdateDocumentStatus
    * Modified aIsUpdate to aNewDocument.
    * Modified RefPtr<DocumentTracker> mDocCookieOperation to DocumentTracker *mDocCookieOperation.
    * Modified mDocCookieOperation to mDocumentTracker.
* ContentParent
    * Modified ProcessHashTableOnCookieServiceParent to UpdateDocumentStatus
    * Removed the argument of nsURI and boolean on ProcessHashTableOnCookieServiceParent
    * Removed the for loop for iterating over all processes.
    * Used SingleManagedOrNull(neckoParent->ManagedPCookieServiceParent()) to get PCookieServiceParent.
* CookieServiceChild
    * Removed the superfluous arguments on UntrackDocumentOnChild and TrackDocumentLoadOnChild.
    * Modified TrackDocumentLoadOnChild and UntrackDocumentOnChild to void.
    * Modified RefPtr<DocumentTracker> mDocCookieOperation to DocumentTracker *mDocCookieOperation.
    * Modified mDocCookieOperation to mDocumentTracker.
* DocumentTracker
    * Modified TrackNewDocument to avoid repeated code.
        * Modified TrackNewDocument to void.
    * Modified RemoveTrackedDocument to void.
    * Modified the constructor on struct DoucmentCookieInfo.
    * Removed NS_INLINE_DECL_REFCOUNTING(DocumentTracker).
    * Movies mDocMap to private member.
* PCookieService.ipdl
    * Modified async UntrackDocument to parent only.
* nsCookieService
    * Reverted nsCookieService.cpp and nsCookieService.h.

Would you give me your suggestions?
Thanks!
Attachment #8871330 - Attachment is obsolete: true
Attachment #8873645 - Flags: feedback?(josh)
(Assignee)

Comment 157

24 days ago
Created attachment 8873646 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Hi,
I have modified this patch as below:
[Not Modification]
* moz.build
    * I didn’t removed nsCookieKey.h on EXPORTS.mozilla.net field.
        * The reason is the nsCookieKey.h include by CookieServiceChild, and CookieServiceChild.h set to EXPORTS.mozilla.net field.
[Modification]
* CookieServiceChild
    * Modified GetCookieStringFromCookieHashTable to void.
        * Removed superfluous assertion.
        * Removed superfluous argument.
    * GetCookieStringIPCSync
        * Modified const bool &aIsForegin to bool aIsForegin.
        * Gave attrs to ConfirmDocumentExistsCookies.
* DocumentTracker
    * Modified the return value of GetMatchingDocument.
        * Have to return DocumentCookieInfo*

Would you give me your suggestions?
Thanks!
Attachment #8871331 - Attachment is obsolete: true
(Assignee)

Updated

24 days ago
Attachment #8873646 - Flags: feedback?(josh)
(Assignee)

Updated

24 days ago
Attachment #8873645 - Attachment description: bug-1331680-part1.patch → implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.
(Assignee)

Comment 158

23 days ago
Hi Richard,
I appreciated your enthusiasm, and I also hope that you can provide profile.
once again thanks for your help!
Flags: needinfo?(rnewman)
Created attachment 8873946 [details]
Profile recorded on Facebook.com

(In reply to Amy Chung [:Amy] from comment #158)
> I appreciated your enthusiasm, and I also hope that you can provide profile.

See attached, Amy. It was 16MB, so I bzipped.
Flags: needinfo?(rnewman)
(In reply to Richard Newman [:rnewman] from comment #159)
> See attached, Amy. It was 16MB, so I bzipped.

Thanks Richard. This is a profile from the devtools performance tool. Could you get one from the Gecko Profiler add-on instead? See https://perf-html.io/ . This will give us more useful data.
(Assignee)

Comment 161

23 days ago
Created attachment 8874010 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

Hi,
I have modified the patch as below:
[Additional Modification]
* CookieServiceChild
    * Still included "mozilla/net/NeckoChannelParams.h”
        * Because I moved RecordDocumentCookie and have to define CookiesMap on CookieServiceChild.h
        * If only forward incomplete type in nested name specifier in list, namespace
* DocumentTracker
    * Doesn’t add nsIURI to DocumentCookieInfo
        * Because the member mHostName, mPathName already can use the same condition as IsCookieExposedToURI().

[Modification]
* CookieServiceChild
    * Added using mozilla::net::CookieStruct;
* DocumentTracker
    * RecordDocumentCookie
        * Moved this function to CookieServiceChild.
        * Modified the memory leak
            * Used CookiesList = aCookiesMap->PutEntry(key);
                * But PutEntry is only used by nsTHashTable
                * Modified CookiesMap to nsTHashTable.
                * Created class CookiesListEntry.
        * Moved the if condition to the function top.
    * RemovedTrackedCookies
        * Modified the condition about deleting cookies.
        * Modified the deleting rules on CookiesListEntry.
* nsCookieService
    * Modified GetCookieListInternal to GetCookiesForURI
        * Used GetCookiesForURI instead to GetCookieList.
    * Moved IsCookieExposedToURI to CookieServiceParent.
        * Removed condition "isSecureURI == cookie->IsSecure()” and just confirmed the https.
* CookieServiceParent
    * When parent get the cookies list
        * Converted the nsCookie which in the cookies list to CookieStruct.

Would you give me your suggestions?
Thanks!
Attachment #8872165 - Attachment is obsolete: true
Attachment #8874010 - Flags: feedback?(josh)
(Assignee)

Comment 162

23 days ago
Created attachment 8874023 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Hi,
Sorry for forgetting to remove the GetCookieStructList().
I re-uploaded part 3 patch again.
Attachment #8874010 - Attachment is obsolete: true
Attachment #8874010 - Flags: feedback?(josh)
Attachment #8874023 - Flags: feedback?(josh)
Comment on attachment 8873645 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Review of attachment 8873645 [details] [diff] [review]:
-----------------------------------------------------------------

The remaining comments should be straightforward to address (as long as my belief about why the assertion was being triggered is correct). This code is much easier to read now; good work!

::: dom/base/nsDocument.cpp
@@ +8,5 @@
>   * Base class for all our document implementations.
>   */
>  
>  #include "AudioChannelService.h"
> +#include "ContentParent.h"

This isn't necessary.

@@ +2986,5 @@
>  
>  void
>  nsDocument::SetPrincipal(nsIPrincipal *aNewPrincipal)
>  {
> +  if (aNewPrincipal && XRE_IsContentProcess()) {

We should not run these steps if mChannel does not have LOAD_DOCUMENT_COOKIES present.

@@ +8751,5 @@
>  void
>  nsDocument::Destroy()
>  {
> +  if (XRE_IsContentProcess()) {
> +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();

We should only run these steps if mChannel has LOAD_DOCUMENT_COOKIES set.

@@ +8757,5 @@
> +    if (mDocumentURI) {
> +      ContentChild::UpdateDocumentStatus(mDocumentURI, principal, newDocument);
> +    }
> +
> +    if (mOriginalURI) {

We still need to be sure that we do not send two notifications for the same URI. This is why I mentioned pointer equality.

::: dom/ipc/ContentChild.cpp
@@ +1787,5 @@
>  
> +void
> +ContentChild::UpdateDocumentStatus(nsIURI       *aHostURI,
> +                                   nsIPrincipal *aPrincipal,
> +                                   const bool    aNewDocumen)

aNewDocument. No need for const.

::: dom/ipc/ContentChild.h
@@ +142,5 @@
>    bool IsShuttingDown() const;
>  
>    static void AppendProcessId(nsACString& aName);
>  
> +  static void UpdateDocumentStatus(nsIURI *aHostURI, nsIPrincipal *aPrincipal, const bool aNewDocumen);

aNewDocument. No need for const.

::: dom/ipc/ContentParent.cpp
@@ +5000,5 @@
> +void
> +ContentParent::UpdateDocumentStatus(nsIChannel *aChannel,
> +                                    nsIPrincipal *aPrincipal)
> +{
> +  if (!aChannel) {

I think we should always have a channel here.

@@ +5004,5 @@
> +  if (!aChannel) {
> +    return;
> +  }
> +  PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> +  // Accumulate kids into a stable structure to iterate over

This comment is about code that no longer exists.

@@ +5005,5 @@
> +    return;
> +  }
> +  PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> +  // Accumulate kids into a stable structure to iterate over
> +  if (neckoParent) {

We should always have a neckoParent here.

@@ +5006,5 @@
> +  }
> +  PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> +  // Accumulate kids into a stable structure to iterate over
> +  if (neckoParent) {
> +    PCookieServiceParent *csParent = SingleManagedOrNull(neckoParent->ManagedPCookieServiceParent());

This can use LoneManagedOrNullAsserts as well.

@@ +5010,5 @@
> +    PCookieServiceParent *csParent = SingleManagedOrNull(neckoParent->ManagedPCookieServiceParent());
> +    if (csParent) {
> +      auto *cs = static_cast<CookieServiceParent*>(csParent);
> +      nsCOMPtr<nsIURI> uri;
> +      aChannel->GetURI(getter_AddRefs(uri));

Since the principal has a URI (nsIPrincipal::GetURI), we should not need to get a separate one.

@@ +5047,5 @@
> +  aChannel->GetIsDocument(&isDocument);
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {
> +    UpdateDocumentStatus(aChannel, principal);
> +  }
> +

nit: remove the extra newline here.

::: dom/ipc/ContentParent.h
@@ +197,5 @@
>    static void GetAll(nsTArray<ContentParent*>& aArray);
>  
>    static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
>  
> +

nit: remove this.

@@ +317,5 @@
>    void SendStopProfiler() override;
>    void SendPauseProfiler(const bool& aPause) override;
>    void SendGatherProfile() override;
>  
> +

nit: remove this.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +55,5 @@
>    // Create a child PCookieService actor.
>    NeckoChild::InitNeckoChild();
>    gNeckoChild->SendPCookieServiceConstructor(this);
>  
> +  // Create DocumentTracker to process hash table.

This comment is not necessary.

@@ +111,5 @@
> +                                          const nsCString        &aBaseDomain,
> +                                          const OriginAttributes &aAttrs)
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child.

This comment is not true (the IPC succeeds if no URI is present), and refers to the child when the message comes from the parent.

::: netwerk/cookie/CookieServiceChild.h
@@ +32,5 @@
>  
>    static CookieServiceChild* GetSingleton();
>  
> +  void
> +  TrackDocumentLoadOnChild(nsIPrincipal *aPincipal,

Let's remove OnChild from these method names.

@@ +37,5 @@
> +                           nsIURI *aHostURI);
> +
> +  void
> +  UntrackDocumentOnChild(nsIPrincipal *aPrincipal,
> +                         nsIURI *aHostURI);

These methods do not need a separate nsIURI argument. That can be obtained from the principal.

@@ +69,5 @@
>    void PrefChanged(nsIPrefBranch *aPrefBranch);
>  
>    bool RequireThirdPartyCheck();
>  
> +  DocumentTracker *mDocumentTracker;

We don't need to store a pointer.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +93,5 @@
> +  nsAutoCString baseDomain;
> +  aPrincipal->GetBaseDomain(baseDomain);
> +  mDocumentTracker->RemoveTrackedDocument(aHostURI, baseDomain, attrs);
> +  URIParams uriParams;
> +  SerializeURI(aHostURI, uriParams);

This is unused now.

@@ +117,5 @@
> +                                           const nsCString        &aBaseDomain,
> +                                           const OriginAttributes &aAttrs)
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the parent.

These comments aren't true; the IPC doesn't fail if there is no URI provided. They also refer to "provided by the parent" when this message is received from the child.

::: netwerk/cookie/CookieServiceParent.h
@@ +21,5 @@
>  public:
>    CookieServiceParent();
>    virtual ~CookieServiceParent();
>  
> +  void TrackDocumentLoadOnParent(nsIPrincipal *aPrincipal,

Let's remove OnParent from these method names.

@@ +25,5 @@
> +  void TrackDocumentLoadOnParent(nsIPrincipal *aPrincipal,
> +                                     nsIURI *aHostURI);
> +
> +  void UntrackDocumentOnParent(nsIPrincipal *aPrincipal,
> +                                   nsIURI *aHostURI);

These methods do not need the nsIURI arguments; we can get those from the principals.

@@ +54,3 @@
>  
>    RefPtr<nsCookieService> mCookieService;
> +  DocumentTracker *mDocumentTracker;

We don't need to store a pointer.

::: netwerk/cookie/DocumentTracker.cpp
@@ +1,1 @@
> +#include "DocumentTracker.h"

nit: add the license header to this file.

@@ +12,5 @@
> +DocumentTracker::~DocumentTracker()
> +{
> +}
> +
> +void

Let's return nsCString instead of writing to an outparam.

@@ +28,5 @@
> +void
> +DocumentTracker::TrackNewDocument(nsIURI                 *aHostURI,
> +                                  const nsCString        &aBaseDomain,
> +                                  const OriginAttributes &aAttrs,
> +                                  const bool             &aIsInit)

`bool aIsInit` instead of `const bool &aIsInit`.

@@ +52,5 @@
> +        docCookieInfo->mActiveDocuments++;
> +        return;
> +      }
> +    }
> +  } else {

Let's reverse this code flow:
if (!docCookieInfoList) {
  docCookieInfoList = mDocMap.PutEntry(key);
}

for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
  ...
}

@@ +53,5 @@
> +        return;
> +      }
> +    }
> +  } else {
> +    docCookieInfoList = new DocumentCookieInfoList;

This is a memory leak.

@@ +64,5 @@
> +  docCookieInfo->mSecure = isHTTPS;
> +  if (aIsInit) {
> +    docCookieInfo->mCookiesPresent = true;
> +  } else {
> +    docCookieInfo->mCookiesPresent = false;

docCookieInfo->mCookiesPresent = aIsInit;

@@ +67,5 @@
> +  } else {
> +    docCookieInfo->mCookiesPresent = false;
> +  }
> +  docCookieInfo->mActiveDocuments = 1;
> +  return;

This return is unnecessary.

@@ +79,5 @@
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;
> +  nsDependentCString key;
> +  GenerateStringKey(aBaseDomain, aAttrs, key);
> +  mDocMap.Get(key, &docCookieInfoList);
> +  if (!docCookieInfoList) {

With the changes in nsDocument, I think we should be able to assert this now.

@@ +82,5 @@
> +  mDocMap.Get(key, &docCookieInfoList);
> +  if (!docCookieInfoList) {
> +    return;
> +  }
> +  // Get the information from aHostURI

I don't think this comment adds anything to the code.

@@ +99,5 @@
> +      if (docCookieInfo->mActiveDocuments > 1) {
> +        docCookieInfo->mActiveDocuments--;
> +      } else {
> +        docCookieInfoList->RemoveElementAt(i);
> +        break;

This break should be outside of the else.

@@ +107,5 @@
> +
> +  if (docCookieInfoList->IsEmpty()) {
> +    mDocMap.Remove(key);
> +  }
> +  return;

This return is unnecessary.

::: netwerk/cookie/DocumentTracker.h
@@ +1,1 @@
> +#ifndef mozilla_net_DocumentTracker_h

nit: add the license header to this file.

@@ +6,5 @@
> +#include "nsHashKeys.h"
> +#include "nsTHashtable.h"
> +#include "nsAutoPtr.h"
> +#include "nsIPrincipal.h"
> +#include "nsIURI.h"

We can forward-declare nsIURI and nsIPrincipal instead of including those two headers.

@@ +21,5 @@
> +   , mCookiesPresent(true)
> +   , mActiveDocuments(1)
> +  {}
> +
> +  DocumentCookieInfo(const nsCString &aHostName,

Is this constructor used anywhere now?

@@ +64,5 @@
> +
> +
> +private:
> +  DocumentCookieMap mDocMap;
> +  nsCOMPtr<nsIEffectiveTLDService> mTLDService;

This should be moved to a later patch that uses it.

::: netwerk/cookie/PCookieService.ipdl
@@ +102,5 @@
>                                              bool aFromHttp);
>  
> +  nested(inside_cpow) async UntrackDocument(URIParams host,
> +                                            nsCString baseDomain,
> +                                             OriginAttributes attrs);

nit: indentation.

@@ +109,5 @@
> +both:
> +  async TrackDocumentLoad(URIParams host,
> +                          nsCString baseDomain,
> +                          OriginAttributes attrs);
> +

nit: remove the extra newline.

::: netwerk/cookie/nsCookieService.cpp
@@ +4055,5 @@
>    }
>    return rv;
>  }
>  
> +

nit: remove this.
Attachment #8873645 - Flags: feedback?(josh) → feedback-
Comment on attachment 8873646 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Review of attachment 8873646 [details] [diff] [review]:
-----------------------------------------------------------------

This is close, but we definitely regressed some behaviour since the last version of the patch.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +162,5 @@
> +CookieServiceChild::GetCookieStringFromCookieHashTable(nsIURI                 *aHostURI,
> +                                                       const OriginAttributes &aOriginAttrs,
> +                                                       nsAutoCString          &aCookieString)
> +{
> +  CookiesList *cookiesList = nullptr;

Let's declare this right before it gets used.

@@ +164,5 @@
> +                                                       nsAutoCString          &aCookieString)
> +{
> +  CookiesList *cookiesList = nullptr;
> +
> +  // Generate nsIEffectiveTLDService for getting base domain on aHostURI.

I don't think this comment adds anything to the code.

@@ +181,5 @@
> +  }
> +  cookiesList->Sort(CompareCookiesForSending());
> +  for (uint32_t i = 0; i < cookiesList->Length(); i++) {
> +    nsCookie *cookie = cookiesList->ElementAt(i);
> +    NS_ASSERTION(cookie, "Get DocumentCookieInfo fail");

We don't need this assertion. We can't have null cookies here.

@@ +197,5 @@
> +    }
> +  }
> +}
> +
> +nsresult

No code checks the return value, so we can return void.

@@ +243,5 @@
>        attrs = loadInfo->GetOriginAttributes();
>      }
> +    nsCOMPtr<nsIPrincipal> principal;
> +    nsContentUtils::GetSecurityManager()->
> +              GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));

nit: only indent two spaces.

::: netwerk/cookie/CookieServiceChild.h
@@ +7,5 @@
>  #define mozilla_net_CookieServiceChild_h__
>  
>  #include "mozilla/net/PCookieServiceChild.h"
> +#include "nsClassHashtable.h"
> +#include "nsCookieKey.h"

I think we can forward declare nsCookieKey instead of including the header.

@@ +46,5 @@
>  protected:
> +  typedef nsTArray<RefPtr<nsCookie>> CookiesList;
> +  typedef nsClassHashtable<nsCookieKey, CookiesList> CookiesMap;
> +
> +  CookiesMap mCookiesMap;

Let's move this down with the other members.

::: netwerk/cookie/DocumentTracker.cpp
@@ +28,5 @@
> +DocumentCookieInfo*
> +DocumentTracker::GetMatchingDocument(DocumentCookieInfoList *aDocCookieInfoList,
> +                                     nsIURI                 *aHostURI)
> +{
> +  // Get the information from aHostURI

This comment is not useful.

@@ +44,5 @@
> +    docCookieInfo = &aDocCookieInfoList->ElementAt(i);
> +    if (docCookieInfo->mHostName.Equals(hostFromURI) &&
> +        docCookieInfo->mPathName.Equals(pathFromURI) &&
> +        docCookieInfo->mSecure == isHTTPS) {
> +      break;

We can return here instead.

@@ +61,5 @@
> +
> +  nsDependentCString key;
> +  GenerateStringKey(aBaseDomain, aOriginAttrs, key);
> +  mDocMap.Get(key, &docCookieInfoList);
> +  if (docCookieInfoList) {

if (!docCookieInfoList) {
  return false;
}
DocumentCookieInfo* docCookieInfo =
  GetMatchingDocument(docCookieInfoList, aHostURI);
return docCookieInfo ? docCookieInfo->mCookiesPresent : false;

@@ -60,5 @@
>  
> -  docCookieInfo = docCookieInfoList->AppendElement();
> -  docCookieInfo->mHostName = hostFromURI;
> -  docCookieInfo->mPathName = pathFromURI;
> -  docCookieInfo->mSecure = isHTTPS;

These fields are never set now.

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'nsCookieKey.h',

I don't believe this needs to be exported if we forward-declare nsCookieKey in the header instead.

::: netwerk/cookie/nsCookieService.cpp
@@ +4002,5 @@
>    return NS_OK;
>  }
>  
>  // Get the base domain for aHost; e.g. for "www.bbc.co.uk", this would be
> +// "bbc.co.uk". This is done differently than GetBaseDomain(aTLDService, ): it is assumed

Looks like find/replace was over-eager ;)
Attachment #8873646 - Flags: feedback?(josh) → feedback-
Comment on attachment 8874023 [details] [diff] [review]
implementation part3 --- send and purge matching cookies

Review of attachment 8874023 [details] [diff] [review]:
-----------------------------------------------------------------

There are enough logic errors in this patch that I am worried that I am reviewing code that has not been tested at all.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +196,5 @@
> +      break;
> +    }
> +  }
> +
> +  nsCookie *updateCookie = nsCookie::Create(aCookie.name(),

This is a memory leak. We need to use a RefPtr here.

@@ +206,5 @@
> +                                            aCookie.creationTime(),
> +                                                   aCookie.isSession(),
> +                                                   aCookie.isSecure(),
> +                                                   aCookie.isHttpOnly(),
> +                                                   aAttrs);

nit: indentation.

@@ +214,5 @@
> +    DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(j);
> +    if (docCookieInfo->mHostName.Equals(updateCookie->Host()) &&
> +        docCookieInfo->mPathName.Equals(updateCookie->Path()) &&
> +        docCookieInfo->mSecure == updateCookie->IsSecure()) {
> +      docCookieInfo->mCookiesPresent = true;

This logic belongs in RecvTrackDocLoadAndCookies, where we should use the host URI that is provided. This means we can remove all of the DocumentCookieMap logic from this method.

::: netwerk/cookie/CookieServiceChild.h
@@ +21,5 @@
>  namespace mozilla {
>  namespace net {
> +
> +class CookieStruct;
> +using mozilla::net::CookieStruct;

Is this even necessary since we're already in the same namespace?

::: netwerk/cookie/CookieServiceParent.cpp
@@ +104,5 @@
> +bool
> +CookieServiceParent::IsCookieExposedToURI(nsIURI         *aHostURI,
> +                                          nsCookie       *aCookie)
> +{
> +  if (!aHostURI && !aCookie) {

This is an odd check (did you mean ||?). Can we just assert that both arguments are non-null instead?

@@ +117,5 @@
> +  aHostURI->GetAsciiHost(sourceHost);
> +  aHostURI->SchemeIs("https", &isSecureURI);
> +  return mCookieService->DomainMatches(aCookie, sourceHost) &&
> +         mCookieService->PathMatches(aCookie, sourcePath)   &&
> +         isSecureURI                                        &&

Why are we only exposing cookies to https pages?

@@ +140,5 @@
> +      cookieStruct->lastAccessed() = cookie->LastAccessed();
> +      cookieStruct->creationTime() = cookie->CreationTime();
> +      cookieStruct->isSession() = cookie->IsSession();
> +      cookieStruct->isSecure() = cookie->IsSecure();
> +      cookieStruct->isHttpOnly() = cookie->IsHttpOnly();

Let's assert that cookie->IsHttpOnly() is false here.

::: netwerk/cookie/CookieServiceParent.h
@@ +6,5 @@
>  #ifndef mozilla_net_CookieServiceParent_h
>  #define mozilla_net_CookieServiceParent_h
>  
>  #include "mozilla/net/PCookieServiceParent.h"
> +#include "mozilla/net/NeckoChannelParams.h"

Why is this necessary?

@@ +20,2 @@
>  
> +using mozilla::net::CookieStruct;

Is this statement even necessary here?

@@ +61,5 @@
> +  IsCookieExposedToURI(nsIURI *aHostURI,
> +                       nsCookie *aCookie);
> +
> +  void
> +  GetMatchingCookiesList(nsTArray<nsCookie*> &aFoundCookieList,

const

::: netwerk/cookie/DocumentTracker.cpp
@@ +122,5 @@
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;
> +  nsDependentCString docKey;
> +  GenerateStringKey(aBaseDomain, aAttrs, docKey);
> +  mDocMap.Get(docKey, &docCookieInfoList);
> +  bool cookieExist = false;

This should be declared inside the loop, otherwise its value is never reset after we set it to true.

@@ +126,5 @@
> +  bool cookieExist = false;
> +  uint32_t originCookieListLen = cookiesListEntry->GetCookiesList().Length();
> +  for (uint32_t i = 0; i < originCookieListLen; i++) {
> +    nsCookie *cookie = cookiesListEntry->GetCookiesList().ElementAt(i);
> +    if (docCookieInfoList) {

Let's check this earlier and remove the entry from the cookie map if there are no known documents. This will let us return immediately.

@@ +131,5 @@
> +      for (uint32_t j = 0; j < docCookieInfoList->Length(); j++) {
> +        DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(j);
> +        if (nsCookieService::DomainMatches(cookie, docCookieInfo->mHostName) &&
> +            nsCookieService::PathMatches(cookie, docCookieInfo->mPathName)   &&
> +            docCookieInfo->mSecure == cookie->IsSecure() &&

This secure check is incorrect - if the cookie is secure, then the document must be secure as well. However, if the cookie is insecure, the document's secure status does not matter.

@@ +132,5 @@
> +        DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(j);
> +        if (nsCookieService::DomainMatches(cookie, docCookieInfo->mHostName) &&
> +            nsCookieService::PathMatches(cookie, docCookieInfo->mPathName)   &&
> +            docCookieInfo->mSecure == cookie->IsSecure() &&
> +            docCookieInfo->mCookiesPresent) {

This condition is not relevant.

@@ +140,5 @@
> +      }
> +    }
> +    if (!cookieExist) {
> +      uint32_t removeIndex = i - (originCookieListLen - cookiesListEntry->GetCookiesList().Length());
> +      cookiesListEntry->GetCookiesList().RemoveElementAt(removeIndex);

This loop is still incorrect because the index will skip the next element after removing one. We need to iterate backwards over the list to avoid this problem.

@@ +145,5 @@
> +    }
> +  }
> +
> +  if (cookiesListEntry->GetCookiesList().IsEmpty()) {
> +    aCookiesMap.RawRemoveEntry(cookiesListEntry);

Let's use RemoveEntry instead.

@@ +171,3 @@
>    aHostURI->SchemeIs("https", &isHTTPS);
>  
> +  bool DocumentDeleted = false;

documentDeleted.

::: netwerk/cookie/DocumentTracker.h
@@ +2,5 @@
>  #define mozilla_net_DocumentTracker_h
>  
>  #include "mozilla/net/NeckoChannelParams.h"
>  #include "nsClassHashtable.h"
> +#include "nsCookieKey.h"

We should be able to forward-declare this instead.

@@ +26,5 @@
>  
>    DocumentCookieInfo(const nsCString &aHostName,
>                       const nsCString &aPathName,
>                       const bool      &aSecure,
>                       const bool      &aCookiesPresent)

Remove this argument?

@@ +42,5 @@
>    bool mCookiesPresent;
>    nsrefcnt mActiveDocuments;
>  };
>  
> +class CookiesListEntry : public nsCookieKey

Why do we need this?

@@ +71,5 @@
>  public:
>    DocumentTracker();
>    virtual ~DocumentTracker();
>  
> +  typedef nsTHashtable<CookiesListEntry> CookiesMap;

Why the change from nsClassHashtable in CookieServiceChild.h?

@@ +102,5 @@
>    DocumentCookieInfo*
>    GetMatchingDocument(DocumentCookieInfoList *aDocCookieInfoLIst,
>                        nsIURI *aHostURI);
>  
> +  inline DocumentCookieMap& GetDocMap() { return mDocMap; };

There's no need to for the inline annotation.

@@ +107,5 @@
> +
> +  void
> +  GenerateStringKey(const nsCString &aBaseDomain,
> +                    const OriginAttributes &aAttrs,
> +                    nsDependentCString &aDocKey);

Instead of exposing this API (and GetDocMap), we should add a `MarkCookiesPresent(nsIURI*, const OriginAttributes& aAttrs)` method which CookieServiceChild can call.

::: netwerk/cookie/PCookieService.ipdl
@@ +106,5 @@
> +                        OriginAttributes attrs);
> +
> +  async TrackDocumentLoad(URIParams host,
> +                                              nsCString baseDomain,
> +                                              OriginAttributes attrs);

nit: indentation.

::: netwerk/cookie/moz.build
@@ +22,5 @@
>  if CONFIG['NECKO_COOKIES']:
>      EXPORTS.mozilla.net = [
>          'CookieServiceChild.h',
>          'CookieServiceParent.h',
> +        'DocumentTracker.h',

Why is this necessary?

::: netwerk/cookie/nsCookieService.h
@@ +47,5 @@
>  class mozIStorageService;
>  class mozIThirdPartyUtil;
>  class ReadCookieDBListener;
>  
> +

nit: remove this.

@@ +182,5 @@
>  /******************************************************************************
>   * nsCookieService:
>   * class declaration
>   ******************************************************************************/
> +using mozilla::net::CookieStruct;

It's better to avoid `using` statements in headers, so let's use mozilla::net::CookieStruct inline instead.

::: netwerk/ipc/NeckoChannelParams.ipdlh
@@ +200,5 @@
> +  int64_t   lastAccessed;
> +  int64_t   creationTime;
> +  bool      isSession;
> +  bool      isSecure;
> +  bool      isHttpOnly;

We should not need this, since we only send non-httponly cookies to the child.
Attachment #8874023 - Flags: feedback?(josh) → feedback-
(Assignee)

Comment 166

20 days ago
Created attachment 8874572 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I modified my patch as below:
[Not Modification]
* CookieServiceParent & CookieServiceChild
    * Can’t modified the pointer of DocumentTracker, because the compiled error of "forward declaration”.

[Modification]
* nsDocument
    * Added the condition about confirming the load flags which includes LOAD_DOCUMENT_COOKIES.
* CookieServiceChild
    * Removed “OnChild” from the method names.
    * Removed argument nsIURI on TrackDocumentLoad & UntrackDocumentLoad.
* CookieServiceParent
    * Removed “OnParent” from the method names.
    * Removed argument nsIURI on TrackDocumentLoad & UntrackDocumentLoad.
* DocumentTracker
    * Modified the define of returning value on GenerateStringKey.
    * Fro avoiding memory leak, modified the DocumentMap to nsTHashtable and created a DocCookieInfoListEntry which includes DocumentCookieInfoList.
        * if (!docCookieInfoList) {
        * docCookieInfoList = mDocMap.PutEntry(key);
        * }
        * for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
        * ...
        * }

Would you give me your suggestions?
Thanks!
Attachment #8873645 - Attachment is obsolete: true
Attachment #8874572 - Flags: feedback?(josh)
(Assignee)

Comment 167

19 days ago
Created attachment 8874898 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Hi,
I have modified my patch as below:
* CookieServiceChild
    * Removed #include “nsCookieKey.h”
        * Modified the mCookiesMap to pointer.
* DocumentTracker
    * Modified the arguments of GetMatchingDocument.
    * Modified ConfirmDocumentExistsCookies
        * Moved the condition of confirming the docCookieInfoLIst is null to front of calling GetMatchingDocument.
    * Modified TrackNewDocument
        * Reverted the modification of docCookieInfo fields which have to assign.
* moz.build
    * Removed nsCookieKey.

Would you give me your suggestions?
Thanks!
Attachment #8873646 - Attachment is obsolete: true
Attachment #8874898 - Flags: feedback?(josh)
(Assignee)

Comment 168

18 days ago
Created attachment 8875397 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I modified the 
* CookieServiceChild & CookieServiceParent
  * Added #include "DocumentTracker.h"
  * Created a DocumentCookieInfoList names mDocMap.
    * Because mDocMap is not pointer, I didn't use forward declaration.

* DocumentTracker
  * In RemoveTrackedDocument, I removed the assertion.
    * I tried to load "cnn.com", and I found CookieServiceParent which doesn't create yet when AboutToLoadHttpFtpWyciwygDocumentForChild() be called.
      For the foregoing reasons, the csParents.Lenght() always equals 0 when the first time to call AboutToLoadHttpFtpWyciwygDocumentForChild(), and we can't update this document to hash table.
      * Solution:
        1. Create a nsTArray names UntreatedDoc to store the principal info when NeckoParent doesn't create yet on ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild().
        2. ContentParent Sends the ipc msg to ContentChild for informing ContentChild that there is a new document which doesn't set to Document Hash table.
        3. ContentChild sends the ipc msg to ContentParent for informing ContentParent can delete the principal info from UntreatedDoc.

Would you give me your suggestions?
Thanks!
Attachment #8874572 - Attachment is obsolete: true
Attachment #8874572 - Flags: feedback?(josh)
Attachment #8875397 - Flags: feedback?(josh)
(Assignee)

Comment 169

18 days ago
Created attachment 8875398 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Have to rebase by the modification of part 1 patch.
Attachment #8874898 - Attachment is obsolete: true
Attachment #8874898 - Flags: feedback?(josh)
Attachment #8875398 - Flags: feedback?(josh)
Comment on attachment 8875398 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string

Review of attachment 8875398 [details] [diff] [review]:
-----------------------------------------------------------------

Only minor changes left for this patch.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +240,5 @@
> +       mDocumentTracker.
> +         ConfirmDocumentExistsCookies(aHostURI, baseDomain, attrs)) {
> +    GetCookieStringFromCookieHashTable(aHostURI, attrs, result);
> +  } else {
> +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);

We lost the separate method here.

::: netwerk/cookie/CookieServiceChild.h
@@ +78,5 @@
>  
>    bool RequireThirdPartyCheck();
>  
>    DocumentTracker mDocumentTracker;
> +  CookiesMap *mCookiesMap;

Sorry, I didn't think about the implications of forward-declaring nsCookieKey. It is better to not use a pointer here like the previous patches.

::: netwerk/cookie/DocumentTracker.cpp
@@ +77,5 @@
> +  aHostURI->SchemeIs("https", &isHTTPS);
> +  DocumentCookieInfo *docCookieInfo =
> +    GetMatchingDocument(docCookieInfoList, hostFromURI, pathFromURI, isHTTPS);
> +
> +  return docCookieInfo ? docCookieInfo->mCookiesPresent : false;

Oh, I guess we can simplify this to `return docCookieInfo && docCookieInfo->mCookiesPresent;`
Attachment #8875398 - Flags: feedback?(josh) → feedback+
Comment on attachment 8875397 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Review of attachment 8875397 [details] [diff] [review]:
-----------------------------------------------------------------

Mostly small changes, but a couple logic errors have appeared in this latest version.

::: dom/base/nsDocument.cpp
@@ +2999,5 @@
>  
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> +  if (!chan) {
> +    return;
> +  }

We should move the existing code (including the #ifdef DEBUG block) so it executes before we return here.

@@ +3008,5 @@
> +  if (loadFlags &
> +        nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +      isDocument                               &&
> +      aNewPrincipal                            &&
> +      XRE_IsContentProcess()) {

I find this formatting difficult to read. Let's do this instead:
if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
    isDocument &&
    aNewPrincipal &&
    XRE_IsContentProcess()) {

@@ +8797,5 @@
> +  chan->GetIsDocument(&isDocument);
> +  if (loadFlags &
> +        nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +      isDocument                               &&
> +      XRE_IsContentProcess()) {

Same comment about the formatting as before.

::: dom/ipc/ContentChild.cpp
@@ +1786,5 @@
>  }
>  
> +void
> +ContentChild::UpdateDocumentStatus(nsIPrincipal *aPrincipal,
> +                                   bool          aNewDocumen)

aNewDocument

::: dom/ipc/ContentChild.h
@@ +142,5 @@
>    bool IsShuttingDown() const;
>  
>    static void AppendProcessId(nsACString& aName);
>  
> +  static void UpdateDocumentStatus(nsIPrincipal *aPrincipal, bool aNewDocumen);

aNewDocument

@@ +144,5 @@
>    static void AppendProcessId(nsACString& aName);
>  
> +  static void UpdateDocumentStatus(nsIPrincipal *aPrincipal, bool aNewDocumen);
> +
> +

nit: remove this extra newline.

::: dom/ipc/ContentParent.cpp
@@ +4997,5 @@
>    ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
>  }
>  
> +void
> +ContentParent::UpdateDocumentStatus(nsIChannel *aChannel,

aChannel is not used.

::: netwerk/cookie/CookieServiceChild.h
@@ +16,5 @@
>  
>  namespace mozilla {
>  namespace net {
>  
> +class DocumentTracker;

This should not be necessary since we are including the header now.

::: netwerk/cookie/CookieServiceParent.h
@@ -12,5 @@
>  namespace mozilla { class OriginAttributes; }
>  
>  namespace mozilla {
>  namespace net {
> -

nit: add this newline back in.

::: netwerk/cookie/DocumentTracker.cpp
@@ +18,5 @@
> +DocumentTracker::~DocumentTracker()
> +{
> +}
> +
> +nsDependentCString

We should be using nsAutoCString for this function, not nsDependentCString.

@@ +32,5 @@
> +  return key;
> +}
> +
> +DocumentCookieInfo*
> +DocumentTracker::GetMatchingDocument(DocumentCookieInfoList *aDocumentCookieInfoList,

How about aDocList instead?

@@ +37,5 @@
> +                                     nsAutoCString           aHost,
> +                                     nsAutoCString           aPath,
> +                                     bool                    aIsHTTPS)
> +{
> +  // Get the information from aHostURI

This comment is unrelated to this code.

@@ +43,5 @@
> +    return nullptr;
> +  }
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +  for (uint32_t i = 0;
> +         i < aDocumentCookieInfoList->Length(); i++) {

Let's keep this for loop on the same line.

@@ +44,5 @@
> +  }
> +  DocumentCookieInfo *docCookieInfo = nullptr;
> +  for (uint32_t i = 0;
> +         i < aDocumentCookieInfoList->Length(); i++) {
> +    docCookieInfo =

We can avoid returning incorrect values by doing:
DocumentCookieInfo* docCookieInfo = &aDocumentCookieInfoList->ElementAt(i);

@@ +52,5 @@
> +        docCookieInfo->mSecure == aIsHTTPS) {
> +      return docCookieInfo;
> +    }
> +  }
> +  return docCookieInfo;

This isn't right - we will return the last entry aDocmentCookieInfoList if we don't find a match.

@@ +85,5 @@
> +  }
> +  docCookieInfo->mActiveDocuments++;
> +
> +  DocumentCookieInfoList *docCookieInfoList2 ;
> +  mDocMap.Get(key, &docCookieInfoList2);

What does this do?

@@ +94,5 @@
> +                                       const nsCString        &aBaseDomain,
> +                                       const OriginAttributes &aAttrs)
> +{
> +  nsDependentCString key;
> +  key = GenerateStringKey(aBaseDomain, aAttrs);

nit: combine these two lines into one.

::: netwerk/cookie/DocumentTracker.h
@@ +22,5 @@
> +{
> +  DocumentCookieInfo()
> +   : mSecure(true)
> +   , mCookiesPresent(true)
> +   , mActiveDocuments(1)

This is an incorrect default given the implementation of DocumentTracker::TrackNewDocument (which increases the count by one after creating a new entry).

@@ +36,5 @@
> +class DocumentTracker
> +{
> +public:
> +  DocumentTracker();
> +  virtual ~DocumentTracker();

I suspect this is not necessary any more.

::: netwerk/cookie/nsCookieService.cpp
@@ +4055,5 @@
>    }
>    return rv;
>  }
>  
> +

nit: remove this extra newline.
Attachment #8875397 - Flags: feedback?(josh) → feedback-
(Assignee)

Comment 172

16 days ago
Created attachment 8876177 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Hi,
I have modified as below:
1. Added  nsCookieService::GetXPCOMSingleton() on HttpChannelChild::HttpChannelChild().
2. For avoiding the event of tab crash when loaded about:home first then loaded "http://xxx.com".
   I added a condition of confirming protocol (http, https, ftp and wyciwyg) on  nsDocument::Destroy().

Would you give me your suggestions?
Thanks!
Attachment #8875397 - Attachment is obsolete: true
Attachment #8876177 - Flags: feedback?(josh)
(Assignee)

Comment 173

16 days ago
Created attachment 8876179 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Hi,
I have modified as below:
1. Modified nit.
2. Modified the return condition on ConfirmDocumentExistsCookies.
3. Modified the define of mCookiesMap.

Would you help me to review my patch?
Thanks!
Attachment #8875398 - Attachment is obsolete: true
Attachment #8876179 - Flags: review?(josh)
Comment on attachment 8876179 [details] [diff] [review]
implementation part2 --- Created pref id, implemented the function about get cookies string

Review of attachment 8876179 [details] [diff] [review]:
-----------------------------------------------------------------

::: netwerk/cookie/CookieServiceChild.cpp
@@ +242,5 @@
> +       mDocumentTracker.
> +         ConfirmDocumentExistsCookies(aHostURI, baseDomain, attrs)) {
> +    GetCookieStringFromCookieHashTable(aHostURI, attrs, result);
> +  } else {
> +    SendGetCookieString(uriParams, !!isForeign, attrs, &result);

We still need the separate GetCookieStringSyncIPC method here.
Attachment #8876179 - Flags: review?(josh) → review+
Comment on attachment 8876177 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

Review of attachment 8876177 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/base/nsDocument.cpp
@@ +3029,5 @@
> +  chan->GetLoadFlags(&loadFlags);
> +  bool isDocument = false;
> +  chan->GetIsDocument(&isDocument);
> +  if (loadFlags &
> +        nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&

Please put these on the same line.

@@ +8745,5 @@
>    return true;
>  }
>  
> +bool
> +ConfirmProtocolOnChannel(nsIChannel *aChannel)

This feels like we're working around a problem that I don't understand. If the problem is that we return early from nsDocument::SetPrincipal, then we need to add similar logic to nsDocument::Destroy.

@@ +8789,5 @@
>    // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
>    // tearing down all those frame trees right now is the right thing to do.
>    mExternalResourceMap.Shutdown();
> +
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);

If we also QI to nsIHttpChannelInternal, then we can return if that QI fails. That should avoid the problem with about:home, I believe.

@@ +8798,5 @@
> +  chan->GetLoadFlags(&loadFlags);
> +  bool isDocument = false;
> +  chan->GetIsDocument(&isDocument);
> +  if (loadFlags &
> +        nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&

Please put these on the same line.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +114,5 @@
> +                                          const nsCString        &aBaseDomain,
> +                                          const OriginAttributes &aAttrs)
> +{
> +  // Deserialize URI. Having a host URI is mandatory and should always be
> +  // provided by the child.

This comment is not true.

::: netwerk/cookie/CookieServiceParent.h
@@ -12,5 @@
>  namespace mozilla { class OriginAttributes; }
>  
>  namespace mozilla {
>  namespace net {
> -

nit: add this newline back in.

::: netwerk/cookie/DocumentTracker.cpp
@@ +79,5 @@
> +    docCookieInfo->mSecure = isHTTPS;
> +    docCookieInfo->mCookiesPresent = aIsInit;
> +  }
> +  docCookieInfo->mActiveDocuments++;
> +

nit: remove this extra newline.

@@ +90,5 @@
> +{
> +  nsAutoCString key = GenerateStringKey(aBaseDomain, aAttrs);
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;
> +  mDocMap.Get(key, &docCookieInfoList);
> +  NS_ASSERTION(docCookieInfoList, "Can't find doc list from hash table");

We should assert that the list is non-empty here.

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +180,5 @@
>    mChannelCreationTime = PR_Now();
>    mChannelCreationTimestamp = TimeStamp::Now();
>    mAsyncOpenTime = TimeStamp::Now();
>    mEventQ = new ChannelEventQueue(static_cast<nsIHttpChannel*>(this));
> +  

nit: remove these trailing spaces.

@@ +185,5 @@
> +  // We have to build CookieServiceChild & CookieServiceParent earlier
> +  // for avoiding the situation of adding first document to DocumentCookieMap
> +  // which includes in CookieServiceParent but CookieServiceParent doesn't
> +  // be created yet.
> +  nsCookieService::GetXPCOMSingleton();

Let's:
* move this to NeckoChild::InitNeckoChild
* call CookieServiceChild::GetSingleton() instead
* Use this comment:
// Ensure that the cookie service is initialized before the first IPC HTTP channel is created.
// We require that the parent cookie service actor exists while processing HTTP responses.

::: netwerk/protocol/http/moz.build
@@ +122,5 @@
>  
>  LOCAL_INCLUDES += [
>      '/dom/base',
>      '/netwerk/base',
> +    '/netwerk/cookie',

We won't need this.
Attachment #8876177 - Flags: feedback?(josh) → feedback+
(Assignee)

Comment 176

16 days ago
Created attachment 8876306 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

Hi,
I have modified the patch as below:
* DocumentTracker
    * Moved RemoveTrackedCookies  to CookieServiceChild.
    * Created MarkCookiePresent() 
    * Created ConfirmDocExist
        * If cookie list exists a doc, and the doc matches some condition with nsCookie which is the candidate of removing.
            * nsCookie doesn’t need to remove.
            * Otherwise, remove the nsCookie.
    * Created GetMatchingDocumentList()
        * If  hash table exists a document list, return true.
* CookieServiceChild
    * RecordDocumentCookie
        * Modified the algorithm of removing cookie from cookies list.
    * RemoveTrackedCookie
        * Used iterator for removing cookie from cookies list.

Would you give me your suggestions?
Thanks!
Attachment #8874023 - Attachment is obsolete: true
Attachment #8876306 - Flags: feedback?(josh)
(Assignee)

Comment 177

16 days ago
Created attachment 8876308 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Hi,
I have modified my patch as below:
* CookieServiceChild
    * Created RecordCommonCookie
        * For adding the cookie which from SetCookieString().
    * Created SetCookieInternl
        * For confirming the permission service.
* nsCookieService
    * Reverted the nsCookieAttribute
        * Moved nsCookieAttribute to header.
    * Created SetCookieRules()
        * Set to static function for letting CookieServiceChild can call it.
* CookieServiceParent
    * Reverted the modification of SetCookieStringIntermal.

Would you give me your suggestions?
Thanks!
Attachment #8872177 - Attachment is obsolete: true
Attachment #8876308 - Flags: feedback?(josh)
(Assignee)

Comment 178

13 days ago
Created attachment 8876776 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

Hi,
I have modified the patch as below:
1. Added the method to process "updated", "added", "cleared" and "batch-deleted" on CookieServiceParent.
2. Created "RemoveAll()", "RemoveCookie", "RemoveBatchDeletedCookies", "AddCookie" on PCookieSerivce.
3. CookieServiceChild override the ipc methods.

Would you give me your suggestions?
Thanks!
Attachment #8872179 - Attachment is obsolete: true
Attachment #8876776 - Flags: feedback?(josh)
Comment on attachment 8876306 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

Review of attachment 8876306 [details] [diff] [review]:
-----------------------------------------------------------------

I am still concerned about the logic errors that I found in this patch.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +119,5 @@
> +  }
> +
> +  bool docListExist = mDocumentTracker.GetMatchingDocumentList(aBaseDomain, aAttrs);
> +  if (!docListExist) {
> +    for (auto iter = cookiesList->begin(); iter != cookiesList->end();) {

This loop should not be necessary - we need to do mCookiesMap.Remove(key) instead.

@@ +132,5 @@
> +  for (auto iter = cookiesList->begin(); iter != cookiesList->end(); iter++) {
> +    if (!mDocumentTracker.ConfirmDocExist(aBaseDomain, aAttrs, *iter)) {
> +      cookiesList->RemoveElement(*iter);
> +    }
> +    if (cookiesList->begin() == cookiesList->end()) {

Why do we need this check?

@@ +322,5 @@
>      principal->GetBaseDomain(baseDomain);
>    }
>  
> +  // As:w
> +  // ynchronously call the parent.

Looks like a vim command escaped here.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +126,5 @@
> +bool
> +CookieServiceParent::IsCookieExposedToURI(nsIURI         *aHostURI,
> +                                          nsCookie       *aCookie)
> +{
> +  if (!aHostURI || !aCookie) {

Asserting this are not null makes more sense, since there is no reason they should be. I asked about this in my previous review, too.

@@ +139,5 @@
> +  aHostURI->GetAsciiHost(sourceHost);
> +  aHostURI->SchemeIs("https", &isSecureURI);
> +  return mCookieService->DomainMatches(aCookie, sourceHost) &&
> +         mCookieService->PathMatches(aCookie, sourcePath)   &&
> +         isSecureURI == aCookie->IsSecure()                 &&

This secure URI check is still incorrect, as mentioned in previous patch reviews.

@@ +140,5 @@
> +  aHostURI->SchemeIs("https", &isSecureURI);
> +  return mCookieService->DomainMatches(aCookie, sourceHost) &&
> +         mCookieService->PathMatches(aCookie, sourcePath)   &&
> +         isSecureURI == aCookie->IsSecure()                 &&
> +         aCookie->Expiry() > currentTime;

The check for httponly is missing.

::: netwerk/cookie/DocumentTracker.cpp
@@ +39,5 @@
> +                                         DocumentCookieInfoList *aDocCookieList)
> +{
> +  nsAutoCString key = GenerateStringKey(aBaseDomain, aOriginAttrs);
> +  mDocMap.Get(key, &aDocCookieList);
> +  return aDocCookieList != nullptr;

This does not work, since callers will not see any change to aDocCookieList. Has this code been tested? If so, why do those tests pass? If not, why am I reviewing it?

@@ +52,5 @@
> +  GetMatchingDocumentList(aBaseDomain, aOriginAttrs, docCookieInfoList);
> +  if (!docCookieInfoList) {
> +    return;
> +  }
> + 

nit: remove this trailing whitespace.

@@ +59,5 @@
> +    if (nsCookieService::DomainMatches(aCookie, docCookieInfo->mHostName) &&
> +        nsCookieService::PathMatches(aCookie, docCookieInfo->mPathName)   &&
> +        (!aCookie->IsSecure()                                             ||
> +         docCookieInfo->mSecure == aCookie->IsSecure())) {
> +      docCookieInfo->mCookiesPresent = true;

I think my comment in the last review was unclear. There are two problems with this method right now:
* mCookiesPresent means that all cookies have been retrieved for a particular document. This method should not be called for each cookie that has been received.
* the document that gets marked should be the document associated with the URL that has been loaded in RecvTrackDocAndCookiesLoad

@@ +66,5 @@
> +  }
> +}
> +
> +bool
> +DocumentTracker::ConfirmDocExist(const nsCString        &aBaseDomain,

Let's call this CookieExposedToAnyDoc instead.

@@ +80,5 @@
> +  for (uint32_t i = 0; i < docCookieInfoList->Length(); i++) {
> +    DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(i);
> +    if (nsCookieService::DomainMatches(aCookie, docCookieInfo->mHostName) &&
> +        nsCookieService::PathMatches(aCookie, docCookieInfo->mPathName)   &&
> +        (!aCookie->IsSecure()                                             ||

Please don't align the logical operators in these expressions.

::: netwerk/cookie/DocumentTracker.h
@@ +54,5 @@
>    RemoveTrackedDocument(nsIURI *aHostURI,
>                          const nsCString &aBaseDomain,
> +                        const OriginAttributes &aOriginAttrs);
> +
> +  bool

It would make more sense to return the DocumentCookieInfoList* value instead.

@@ +81,5 @@
> +                    const OriginAttributes &aOriginAttrs,
> +                    nsCookie         *aCookie);
> +
> +  nsAutoCString
> +  GenerateStringKey(const nsCString &aBaseDomain,

This does not need to be a method any more.

::: netwerk/cookie/PCookieService.ipdl
@@ +103,5 @@
>  
>    async UntrackDocument(URIParams host,
>                          nsCString baseDomain,
>                          OriginAttributes attrs);
> +   

nit: remove this trailing whitespace.

::: netwerk/cookie/nsCookieService.cpp
@@ +12,5 @@
>  #include "mozilla/Unused.h"
>  
>  #include "mozilla/net/CookieServiceChild.h"
>  #include "mozilla/net/NeckoCommon.h"
> +#include "mozilla/net/NeckoChannelParams.h"

This should not be necessary now.

@@ +3355,5 @@
> +{
> +  AutoTArray<nsCookie*, 8> foundCookieList;
> +  GetCookiesForURI(aHostURI, aIsForeign, aHttpBound, aOriginAttrs, foundCookieList);
> +
> +  nsCookie* cookie;

Declare this inside the loop as `nsCookie* cookie = ...`

::: netwerk/cookie/nsCookieService.h
@@ +53,5 @@
>  namespace mozilla {
>  namespace net {
>  class nsCookieKey;
>  class CookieServiceParent;
> +class CookieStruct;

I do not believe this is necessary now.

@@ +182,5 @@
>  /******************************************************************************
>   * nsCookieService:
>   * class declaration
>   ******************************************************************************/
> +using mozilla::net::CookieStruct;

I do not believe this is necessary now.
Attachment #8876306 - Flags: feedback?(josh) → feedback-
Comment on attachment 8876308 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

Review of attachment 8876308 [details] [diff] [review]:
-----------------------------------------------------------------

This patch was much more straightforward than the previous version. Thank you!

::: netwerk/cookie/CookieServiceChild.cpp
@@ +240,2 @@
>  
> +    if (cookieBaseDomain.Equals(aBaseDomain)) {

Sorry, my comment in the previous review was unclear. We can return the length of the list of cookies, since that is the same behaviour of nsCookieService::CountCookiesFromHost.

@@ +244,5 @@
> +  }
> +}
> +
> +void
> +CookieServiceChild::RecordCommonCookie(nsCookie              *aCookie,

It is not clear to me why we need to separate RecordDocumentCookie and RecordCommonCookie. Why can't we keep them as a single method?

@@ +276,5 @@
> +                                         const OriginAttributes &aAttrs)
> +{
> +  bool docListExist = mDocumentTracker.
> +                        GetMatchingDocumentList(aBaseDomain, aAttrs);
> +  if (!docListExist) {

There should be no way to set a cookie for which there is no known document. Let's assert that it exists, instead.

@@ +410,5 @@
> +
> +  // check permissions from site permission list, or ask the user,
> +  // to determine if we can set the aCookie
> +  if (aPermissionService) {
> +    bool permission;

Initialize this to false.

@@ +411,5 @@
> +  // check permissions from site permission list, or ask the user,
> +  // to determine if we can set the aCookie
> +  if (aPermissionService) {
> +    bool permission;
> +    aPermissionService->CanSetCookie(aHostURI,

It's not clear to me if nsICookiePermission has ever been used in the content process before. In particular, https://dxr.mozilla.org/mozilla-central/rev/981da978f1f686ad024fa958c9d27d2f8acc5ad0/extensions/cookie/nsCookiePermission.cpp#210-226 is going to be a problem for us.

@@ +427,5 @@
> +    cookie->SetExpiry(aCookieAttributes.expiryTime);
> +  }
> +
> +  if (!aFromHttp) {
> +    CookieStruct cookieStruct;

I don't understand why we convert from nsCookie to CookieStruct here, then immediately convert back to nsCookie in RecordDocument cookie.

@@ +442,5 @@
> +  } else {
> +    RecordCommonCookie(cookie, aBaseDomain, aAttrs);
> +  }
> +}
> +

nit: remove this newline.

@@ +467,5 @@
>    if (RequireThirdPartyCheck())
>      mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
>  
>    nsDependentCString cookieString(aCookieString);
> +  nsDependentCString saveCookieString(aCookieString);

No need for this with the other changes I have recommended.

@@ +479,5 @@
>      if (loadInfo) {
>        attrs = loadInfo->GetOriginAttributes();
>      }
>    }
> +  bool requireHostMatch;

Let's do this:

SendSetCookieString(...);
if (mIPCSync) {
  return NS_OK;
}

@@ +494,5 @@
> +  CookieStatus cookieStatus =
> +    nsCookieService::CheckPrefs(permissionService, mCookieBehavior, mThirdPartySession,
> +                                aHostURI, !!isForeign, aCookieString, numOfCookies);
> +
> +  if (cookieStatus == STATUS_ACCEPTED ||

if (cookieStatus != STATUS_ACCEPTED && cookieStatus != STATUS_ACCEPT_SESSION) {
  return NS_OK;
}

@@ +510,5 @@
> +      serverTime = PR_Now() / PR_USEC_PER_SEC;
> +    }
> +    bool setCookie = false;
> +    nsCookieAttributes cookieAttributes;
> +    while (nsCookieService::SetCookieRules(aHostURI, key, cookieAttributes,

Let's do this intead::
bool moreCookies;
do {
  bool canSetCookie = false;
  moreCookies = nsCookieService::SetCookieRules(...);
  if (canSetCookie) {
    SetCookieInternal(...);
  }
} while(moreCookies);

@@ +518,5 @@
> +      if (setCookie && !mIPCSync) {
> +        SetCookieInternal(cookieAttributes, attrs, baseDomain, aChannel, aHostURI,
> +                          aFromHttp, permissionService);
> +      }
> +      if (!aFromHttp) {

Please add "// document.cookie can only set one cookie at a time."

::: netwerk/cookie/CookieServiceChild.h
@@ +108,5 @@
>  
> +  void
> +  CountCookiesFromHashTable(const nsCString &aBaseDomain,
> +                            const OriginAttributes &aOriginAttrs,
> +                            int &aNumOfCookies);

We should return the count instead.

::: netwerk/cookie/nsCookieService.cpp
@@ +3379,5 @@
>  
>  // processes a single cookie, and returns true if there are more cookies
>  // to be processed
>  bool
> +nsCookieService::SetCookieRules(nsIURI                     *aHostURI,

Let's call this CanSetCookie.

@@ +3394,2 @@
>  {
>    NS_ASSERTION(aHostURI, "null host!");

Let's set aSetCookie to false here to be safe.

@@ +3524,5 @@
> +  nsDependentCString savedCookieHeader(aCookieHeader);
> +  nsCookieAttributes cookieAttributes;
> +  bool newCookie = SetCookieRules(aHostURI, aKey, cookieAttributes, aRequireHostMatch,
> +                                  aStatus, aCookieHeader, aServerTime, aFromHttp,
> +                                  aChannel, mLeaveSecureAlone, setCookie);

We need to check setCookie here.

@@ +3534,5 @@
>                       cookieAttributes.host,
>                       cookieAttributes.path,
>                       cookieAttributes.expiryTime,
> +                     PR_Now(),
> +                     nsCookie::GenerateUniqueCreationTime(PR_Now()),

Let's take an aCurrentTimeInUsec argument instead of calling PR_Now() here.

@@ +3543,5 @@
>    if (!cookie)
>      return newCookie;
>  
>    // check permissions from site permission list, or ask the user,
> +  // to determine if we can set the aCookie

Revert this change.

@@ +3553,5 @@
>                                       &cookieAttributes.isSession,
>                                       &cookieAttributes.expiryTime,
>                                       &permission);
>      if (!permission) {
> +      COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, savedCookieHeader, "aCookie rejected by permission manager");

Revert this change.
Attachment #8876308 - Flags: feedback?(josh) → feedback-

Updated

13 days ago
Attachment #8867893 - Attachment is obsolete: true

Updated

13 days ago
Attachment #8867889 - Attachment is obsolete: true

Updated

13 days ago
Attachment #8867888 - Attachment is obsolete: true
Comment on attachment 8876776 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

Review of attachment 8876776 [details] [diff] [review]:
-----------------------------------------------------------------

This looks more like what I expected. The main issue remaining is that CookieServiceParent sends every cookie to the child, when we should be filtering them based on what cookies the child needs.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +213,5 @@
> +                                             aCookie.lastAccessed(),
> +                                             aCookie.creationTime(),
> +                                             aCookie.isSession(),
> +                                             aCookie.isSecure(),
> +                                             aFromHttp,

nsCookie::Create's argument is aIsHttpOnly. We should be passing false here.

@@ +223,5 @@
> +mozilla::ipc::IPCResult
> +CookieServiceChild::RecvRemoveBatchDeletedCookies(nsTArray<CookieStruct>&& aCookiesList,
> +                                                  nsTArray<OriginAttributes>&& aAttrsList)
> +{
> +  NS_ASSERTION(aCookiesList.Length() == aAttrsList.Length(), "");

You can remove the `, ""`.

@@ +226,5 @@
> +{
> +  NS_ASSERTION(aCookiesList.Length() == aAttrsList.Length(), "");
> +  for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
> +    CookieStruct cookieStruct = aCookiesList.ElementAt(i);
> +    nsCString baseDomain;

Let's just call RecvRemoveCookie from here instead of duplicating all the code.

::: netwerk/cookie/CookieServiceChild.h
@@ +63,5 @@
> +
> +  virtual mozilla::ipc::IPCResult
> +  RecvRemoveBatchDeletedCookies(nsTArray<CookieStruct>&& aCookiesList,
> +                                nsTArray<OriginAttributes>&& aAttrsList) override;
> +                                                        

nit: remove this trailing whitespace.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +67,5 @@
>  namespace net {
>  
> +NS_IMPL_ISUPPORTS(CookieServiceParent,
> +                  nsIObserver)
> +

nit: remove a blank line here.

@@ +95,5 @@
>  void
> +GetInfoFromCookie(nsCookie         *aCookie,
> +                  CookieStruct     &aCookieStruct,
> +                  OriginAttributes &aAttrs)
> +{                              

nit: remove this trailing whitespace.

@@ +115,5 @@
> +                             const char      *aTopic,
> +                             const char16_t  *aData)
> +{
> +  if (!strcmp(aTopic, "cookie-changed") ||
> +      !strcmp(aTopic, "private-cookie-changed")) {

if (strcmp(aTopic, "cookie-changed") != 0 &&
    strcmp(aTopic, "private-cookie-changed") != 0) {
  return NS_OK;
}

@@ +119,5 @@
> +      !strcmp(aTopic, "private-cookie-changed")) {
> +    if (!aData || !aSubject) {
> +      return NS_ERROR_UNEXPECTED;
> +    }
> +    CookieStruct cookieStruct;

We should only send cookies to the child that are exposed to any document that this CookieServiceParent's DocumentTracker knows about.

@@ +136,5 @@
> +        cookieStructList.AppendElement(cookieStruct);
> +        attrsList.AppendElement(attrs);
> +      }
> +      SendRemoveBatchDeletedCookies(cookieStructList, attrsList); 
> +      return NS_ERROR_UNEXPECTED;

We should be returning NS_OK in this method.

@@ +139,5 @@
> +      SendRemoveBatchDeletedCookies(cookieStructList, attrsList); 
> +      return NS_ERROR_UNEXPECTED;
> +    }
> +
> +    if (!nsCRT::strncmp(aData, u"cleared", 1)) {

When we only compare the first letter, we can't differentiate "cleared" and "changed". Let's compare against the whole string.

@@ +152,5 @@
> +    if (!nsCRT::strncmp(aData, u"deleted", 1)) {
> +      SendRemoveCookie(cookieStruct, attrs);
> +    } else if ((!nsCRT::strncmp(aData, u"added", 1)) ||
> +               (!nsCRT::strncmp(aData, u"changed", 1))) {
> +      SendAddCookie(cookieStruct, attrs, cookie->IsHttpOnly());

We should be able to assert that any cookie we send at this point has a false IsHttpOnly flag.

::: netwerk/cookie/CookieServiceParent.h
@@ +10,5 @@
>  #include "mozilla/net/PCookieServiceParent.h"
>  
>  class nsCookieService;
>  class nsCookie;
> +class nsIArray;

This does not look necessary.

::: netwerk/cookie/PCookieService.ipdl
@@ +126,5 @@
> +  async RemoveAll();
> +
> +  async AddCookie(CookieStruct cookie,
> +                  OriginAttributes attrs,
> +                  bool fromHttp);

We should not need this argument.

@@ +129,5 @@
> +                  OriginAttributes attrs,
> +                  bool fromHttp);
> +
> +
> +                    

nit: remove these empty lines.