Closed Bug 1331680 Opened 7 years ago Closed 7 years ago

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

Categories

(Core :: Networking: Cookies, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla57
Performance Impact high
Tracking Status
platform-rel --- +
firefox57 --- fixed

People

(Reporter: ehsan.akhgari, Assigned: amchung)

References

(Blocks 2 open bugs)

Details

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

Attachments

(7 files, 113 obsolete files)

(deleted), application/x-bzip2
Details
(deleted), patch
amchung
: review+
Details | Diff | Splinter Review
(deleted), patch
amchung
: review+
Details | Diff | Splinter Review
(deleted), patch
amchung
: review+
Details | Diff | Splinter Review
(deleted), patch
amchung
: review+
Details | Diff | Splinter Review
(deleted), patch
amchung
: review+
Details | Diff | Splinter Review
(deleted), patch
amchung
: 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)
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: sandbox-sa
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: nobody → amchung
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]
(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)
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)
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]
(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.
Attached patch implementation (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation (obsolete) (deleted) — Splinter Review
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)
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)
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.
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 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-
Attached patch implementation (obsolete) (deleted) — Splinter Review
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)
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)
Attached patch implementation (obsolete) (deleted) — Splinter Review
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)
Attached patch bug-1331680-tmp.patch (obsolete) (deleted) — Splinter Review
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 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 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-
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.
(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.
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)
(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)
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)
* 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.
(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)
Attached patch implementation part1--create & set load flag. (obsolete) (deleted) — Splinter Review
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)
Attachment #8862104 - Attachment description: implementation--create & set load flag. → implementation part1--create & set load flag.
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)
Attached patch implementation part5 --- Destroy cookie (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation -- full version (obsolete) (deleted) — Splinter Review
Attached patch implementation part2 -- data struct (obsolete) (deleted) — Splinter Review
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)
Attachment #8862119 - Attachment description: implementation part3 --- Destroy cookie → implementation part5 --- Destroy cookie
Attached patch implementation part3 -- http channel (obsolete) (deleted) — Splinter Review
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)
Attachment #8862380 - Attachment description: implementation part2 -- http channel → implementation part3 -- http channel
Attached patch implementation part4 -- cookie service (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation -- full version (obsolete) (deleted) — Splinter Review
Attachment #8862121 - Attachment is obsolete: true
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-
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.
One other question - does this patch pass tests on the tryserver right now?
Flags: needinfo?(amchung)
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)
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)
Attached patch implementation part1 -- created loadflags. (obsolete) (deleted) — Splinter Review
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)
Attachment #8865288 - Flags: feedback?(juhsu)
Attached patch implementation part2 -- data struct (obsolete) (deleted) — Splinter Review
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)
Attachment #8865290 - Flags: feedback?(juhsu)
Attached patch implementation part4 -- http channel (obsolete) (deleted) — Splinter Review
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)
Attachment #8865291 - Flags: feedback?(juhsu)
Attached patch implementation part5 -- Destroy cookie (obsolete) (deleted) — Splinter Review
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)
Attachment #8865293 - Attachment description: implementation part5 --- Destroy cookie → implementation part5 -- Destroy cookie
Attachment #8865293 - Flags: feedback?(juhsu)
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 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 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 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 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 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 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.
Attached patch test case -- part6 (obsolete) (deleted) — Splinter Review
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)
Attachment #8865993 - Flags: feedback?(juhsu)
Attachment #8862380 - Flags: feedback?(josh)
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 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)
Attachment #8862380 - Attachment is obsolete: true
Attached patch implementation part1 -- created loadflags. (obsolete) (deleted) — Splinter Review
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)
Attachment #8866439 - Flags: review?(juhsu)
Attached patch implementation part2 -- data struct (obsolete) (deleted) — Splinter Review
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)
Attachment #8866442 - Flags: feedback?(juhsu)
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)
Attachment #8852155 - Attachment is obsolete: true
Attachment #8852874 - Attachment is obsolete: true
Attachment #8859465 - Attachment is obsolete: true
Attachment #8862384 - Attachment is obsolete: true
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 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 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 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+
Attached patch implementation part4 -- http channel (obsolete) (deleted) — Splinter Review
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)
Attachment #8866619 - Flags: feedback?(josh)
Attachment #8866619 - Flags: feedback?(juhsu)
(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 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.
(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!
Attached patch implementation part2 -- data struct (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation part1 -- created loadflags. (obsolete) (deleted) — Splinter Review
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)
Attached patch bug-1331680-cookie-wip-part3.patch (obsolete) (deleted) — Splinter Review
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.
Attached patch bug-1331680-channel-part4.patch (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation part5 --- Destroy cookie (obsolete) (deleted) — Splinter Review
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)
Attached patch implementation part1 -- created loadflags. (obsolete) (deleted) — Splinter Review
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)
Attached patch test cases part6 (obsolete) (deleted) — Splinter Review
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)
Attachment #8867893 - Flags: feedback?(josh)
Attachment #8867889 - Flags: feedback?(josh)
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)
(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)
Attached patch implementation part1 (obsolete) (deleted) — Splinter Review
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)
Attachment #8869169 - Flags: feedback?(juhsu)
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)
Hi,
I have separated the movement of nsCookieKey to independent patch.
Would you give me your suggestions?
Attachment #8869563 - Flags: feedback?(josh)
Attachment #8869563 - Flags: feedback?(juhsu)
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)
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)
Attachment #8869571 - Flags: feedback?(juhsu)
(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)
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)
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)
Attachment #8869845 - Flags: feedback?(juhsu)
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+
> 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 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 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)
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)
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.
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.
(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!
(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.
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)
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 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+
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!
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)
Attachment #8870756 - Attachment description: bug-1331680-part1.patch → implementation part1 -- Data Struct, implementation part1 --- 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)
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)
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)
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)
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)
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-
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)
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)
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)
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)
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)
Attachment #8872169 - Flags: feedback?(juhsu)
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)
Attachment #8872177 - Flags: feedback?(juhsu)
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)
Attachment #8872179 - Flags: feedback?(juhsu)
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 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-
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.
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)
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
Attachment #8873646 - Flags: feedback?(josh)
Attachment #8873645 - Attachment description: bug-1331680-part1.patch → implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.
Hi Richard,
I appreciated your enthusiasm, and I also hope that you can provide profile.
once again thanks for your help!
Flags: needinfo?(rnewman)
Attached file Profile recorded on Facebook.com (deleted) —
(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.
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)
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-
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)
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)
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)
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-
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)
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+
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)
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)
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-
Attachment #8867893 - Attachment is obsolete: true
Attachment #8867889 - Attachment is obsolete: true
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.
Attachment #8876776 - Flags: feedback?(josh) → feedback-
Hi,
I have modified the patch as below:
* CookieServiceChild
  * RemoveTrackedCookies
    * if can't get any documentCookieInfoList from doc hash table
      * mCookiesMap.Remove(key).
   * RecordDocumentCookie
     * Added a bool argument to confirm to call MarkCookiePresent or not.

* DocumentTracker
  * GetMatchingDocumentList
    * Modified the return value to DocumentCookieInfoList.
  * MarkCookiePresent
    * Used the nsIURI which from RecvTrackDocAndCookiesLoad to get the matching doc.

Would you give me your suggestions?
Thanks!
Attachment #8876306 - Attachment is obsolete: true
Attachment #8877285 - Flags: feedback?(josh)
Hi,
Sorry for re-uploading this patch, and this patch removed "class CookieStruct" and "using mozilla::net::CookieStruct" from nsCookieService.
Attachment #8877285 - Attachment is obsolete: true
Attachment #8877285 - Flags: feedback?(josh)
Attachment #8877288 - Flags: feedback?(josh)
Hi,
I have modified the patch as below:
[Remain Unchanged]
* HttpChannelChild
  * The browser crashed when I moved "CookieServiceChild::GetSingleton()" to NeckoChild.
* nsDocument
  * ConfirmProtocolOnChannel()
    * I have confirmed the channel can't be queried on SetPrincipal() and can be queried on SetDestroy().

[Modification]
* DocumentTracker
  * Added a assertion that the doc list is non-empty.

Would you give me your suggestions?
Thanks!
Attachment #8876177 - Attachment is obsolete: true
Attachment #8877289 - Flags: review?(josh)
Hi,
I have modified the patch as below:
[Remain Unchanged]
* CookieServiceChild
  * aPermissionService->CanSetCookie
    * I have confirmed the CookieServiceChild can get the permission list.

[Modification]
* CookieServiceChild
  * CountCookiesFromHashTable()
    * Modified the return value to cookies list len.
  * Merged RecordCommonCookie and RecordDocumentCookie
  * SetCookieStringInternal
    * Modified the while loop to confirm the cookie string includes one cookie or more.
* nsCookieService
  * Modified SetCookieRules to CanSetCookie.
 
Would you give me your suggestions?
Thanks!
Attachment #8876308 - Attachment is obsolete: true
Attachment #8877299 - Flags: feedback?(josh)
Sorry re-uploaded this patch.
I have modified the GenerateStringKey().

Would you give me your suggestions?
Thanks!
Attachment #8877288 - Attachment is obsolete: true
Attachment #8877288 - Flags: feedback?(josh)
Attachment #8877548 - Flags: feedback?(josh)
Hi,
I have added a function RemoveExpiryCookie() for confirming the cookie expiry time.
Would you give me your suggestions?
Thanks!
Attachment #8877299 - Attachment is obsolete: true
Attachment #8877299 - Flags: feedback?(josh)
Attachment #8877551 - Flags: feedback?(josh)
Hi,
I have modified the patch as below:
[Remain Unchanged]
* I didn't add a assertion when CookieServiceParent got a http-only cookie.
  Because nsCookieService has the ability to send http-only cookie on "added" or "changed" notify.

[Modification]
* CookieServiceParent
  * if (strncmp(aData, "...", 1) => if (strcmp(aData, "...")
  * Confirmed any document that this CookieServiceParent's DocumentTracker knows about.

Would you give me your suggestions?
Thanks!
Attachment #8876776 - Attachment is obsolete: true
Attachment #8877560 - Flags: feedback?(josh)
(In reply to Amy Chung [:Amy] from comment #184)
> Created attachment 8877289 [details] [diff] [review]
> implementation part1 -- Data Struct, Update entry to Document HashTable,
> Remove entry from Document Hashtable.
> 
> Hi,
> I have modified the patch as below:
> [Remain Unchanged]
> * HttpChannelChild
>   * The browser crashed when I moved "CookieServiceChild::GetSingleton()" to
> NeckoChild.

Please investigate this crash? I still think NeckoChild is a more appropriate place for that code to live.

> * nsDocument
>   * ConfirmProtocolOnChannel()
>     * I have confirmed the channel can't be queried on SetPrincipal() and
> can be queried on SetDestroy().

I don't understand what this means. In SetPrincipal the QI for nsIHttpChannelInternal returns null, but in Destroy it returns non-null? We need to figure out why this is the case, since this model relies on consistent results in those two methods.
Flags: needinfo?(amchung)
Comment on attachment 8877548 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +124,5 @@
> +    mCookiesMap.Remove(key);
> +    return;
> +  }
> +
> +  for (auto iter = cookiesList->begin(); iter != cookiesList->end(); iter++) {

I've asked around and nobody is sure if this skips elements or not. Let's use something that I know is safe:
for (uint32_t idx = cookiesList->Length(); idx > 0; idx--) {
  if (...) {
    cookiesList->RemoveElementAt(idx - 1);
  }
}

@@ +204,5 @@
> +CookieServiceChild::RecordDocumentCookie(nsIURI                 *aHostURI,
> +                                         const CookieStruct     &aCookie,
> +                                         const nsCString        &aBaseDomain,
> +                                         const OriginAttributes &aAttrs,
> +                                         bool                    aMarkCookiePresent)

This boolean does not make sense for two reasons:
* there is a single caller that always passes false
* marking cookies present is not related to any particular cookie, as described in another comment

@@ +328,5 @@
>        GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
>      principal->GetBaseDomain(baseDomain);
>    }
>  
> +  // Asynchronously call the parent.

This belongs in a previous patch.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +124,5 @@
> +// This matches the usual checks performed on cookies, but also restricts
> +// httponly cookies from being exposed.
> +bool
> +CookieServiceParent::IsCookieExposedToURI(nsIURI         *aHostURI,
> +                                          nsCookie       *aCookie)

nit: super weird spacing here. Let's do:
(nsIURI* aHostURI,
 nsCookie* aCookie)

@@ +138,5 @@
> +  aHostURI->SchemeIs("https", &isSecureURI);
> +  return mCookieService->DomainMatches(aCookie, sourceHost) &&
> +         mCookieService->PathMatches(aCookie, sourcePath) &&
> +         (!aCookie->IsSecure() ||
> +         isSecureURI == aCookie->IsSecure()) &&

nit: indent this line by one space, since it is a sub-expression of the surrounding ()s.

::: netwerk/cookie/DocumentTracker.cpp
@@ +61,5 @@
> +  }
> +  if (nsCookieService::DomainMatches(aCookie, docCookieInfo->mHostName) &&
> +      nsCookieService::PathMatches(aCookie, docCookieInfo->mPathName) &&
> +      (!aCookie->IsSecure() ||
> +       docCookieInfo->mSecure == aCookie->IsSecure())) {

We should not be using the cookie path/domain matching here. This method is not related to any particular cookie - it is an operation that happens once when receiving all of the cookies for a particular document.

@@ +67,5 @@
> +  }
> +}
> +
> +bool
> +DocumentTracker::ConfirmDocExist(DocumentCookieInfoList *aDocCookieInfoList,

My previous review asked for this to be renamed to CookieExposedToAnyDoc.

@@ +76,5 @@
> +  for (uint32_t i = 0; i < aDocCookieInfoList->Length(); i++) {
> +    DocumentCookieInfo *docCookieInfo = &aDocCookieInfoList->ElementAt(i);
> +    if (nsCookieService::DomainMatches(aCookie, docCookieInfo->mHostName) &&
> +        nsCookieService::PathMatches(aCookie, docCookieInfo->mPathName)   &&
> +        (!aCookie->IsSecure()                                             ||

nit: remove any extra spacing before the logical operators, like in previous review comments.

::: 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"

I don't believe this is necessary.
Attachment #8877548 - Flags: feedback?(josh) → feedback-
(In reply to Amy Chung [:Amy] from comment #185)
> Created attachment 8877299 [details] [diff] [review]
> implementation part4 -- confirmed the cookie which can save in
> CookieServiceChild.
> 
> Hi,
> I have modified the patch as below:
> [Remain Unchanged]
> * CookieServiceChild
>   * aPermissionService->CanSetCookie
>     * I have confirmed the CookieServiceChild can get the permission list.

My point was that https://dxr.mozilla.org/mozilla-central/rev/981da978f1f686ad024fa958c9d27d2f8acc5ad0/extensions/cookie/nsCookiePermission.cpp#210-226 calls code that does not exist in the content process.
Comment on attachment 8877551 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +227,5 @@
> +  nsCString baseDomain;
> +  nsCookieKey key(aBaseDomain, aOriginAttrs);
> +  mCookiesMap.Get(key, &cookiesList);
> +
> +  if (!cookiesList) {

return cookiesList ? cookiesList->Length() : 0;

@@ +269,5 @@
> +                                      nsIURI                          *aHostURI,
> +                                      bool                             aFromHttp,
> +                                      nsICookiePermission             *aPermissionService)
> +{
> +  // create a new nsCookie and copy attributes

I don't think this comment adds anything.

@@ +277,5 @@
> +                     aCookieAttributes.host,
> +                     aCookieAttributes.path,
> +                     aCookieAttributes.expiryTime,
> +                     PR_Now(),
> +                     nsCookie::GenerateUniqueCreationTime(PR_Now()),

Let's not call PR_Now() twice.

@@ +282,5 @@
> +                     aCookieAttributes.isSession,
> +                     aCookieAttributes.isSecure,
> +                     aCookieAttributes.isHttpOnly,
> +                     aAttrs);
> +  if (!cookie)

nsCookie::Create cannot return null, since new is infallible.

@@ +304,5 @@
> +    cookie->SetIsSession(aCookieAttributes.isSession);
> +    cookie->SetExpiry(aCookieAttributes.expiryTime);
> +  }
> +
> +  if (aFromHttp) {

This doesn't make sense. Why do we ignore cookies if they come from HTTP (like in bug 1363311)?

@@ +478,5 @@
>    }
>  
> +  nsDependentCString stringServerTime;
> +  if (aServerTime)
> +    stringServerTime.Rebind(aServerTime);

Let's move this back where it used to live before these changes.

@@ +491,5 @@
> +  nsCString baseDomain;
> +  nsCookieService::
> +    GetBaseDomain(mTLDService, aHostURI, baseDomain, requireHostMatch);
> +
> +  nsCookieKey key(baseDomain, attrs);

Let's create this key when we actually need it later.

@@ +516,5 @@
> +  if (result == PR_SUCCESS) {
> +    serverTime = tempServerTime / int64_t(PR_USEC_PER_SEC);
> +  } else {
> +    serverTime = PR_Now() / PR_USEC_PER_SEC;
> +  }

Instead of duplicating this code, let's extract it from nsCookieService::SetCookieStringInternal into a static ParseServerTime method that we can call.

@@ +523,5 @@
> +    nsCookieAttributes cookieAttributes;
> +    bool canSetCookie = false;
> +    moreCookies = nsCookieService::CanSetCookie(aHostURI, key, cookieAttributes,
> +                                                requireHostMatch, cookieStatus,
> +                                                cookieString, serverTime, false,

Why are we passing false here?

@@ +529,5 @@
> +
> +    if (canSetCookie) {
> +      int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
> +      if (!RemoveExpiryCookie(baseDomain, attrs, cookieAttributes, currentTime)) {
> +        SetCookieInternal(cookieAttributes, attrs, baseDomain, aChannel, aHostURI,

I don't understand this change; could you explain it to me? SetCookieInternal calls RecordDocumentCookie, which removes any existing matching cookie. Why do we ignore setting any cookie for which there is a matching cookie that has already expired?

::: netwerk/cookie/nsCookieService.cpp
@@ +3389,5 @@
> +                              int64_t                     aServerTime,
> +                              bool                        aFromHttp,
> +                              nsIChannel                 *aChannel,
> +                              bool                        aLeaveSecureAlone,
> +                              bool                       &aSetCookie)

The spacing here is very odd. Please do:
nsIURI* aHostURI,
const nsCookieKey& aKey,
nsCookieAttributes& aCookieAttributes,
bool aRequestHostMatch,
etc.

@@ +3394,3 @@
>  {
>    NS_ASSERTION(aHostURI, "null host!");
> +  NS_ASSERTION(!aSetCookie, "aSetCookie init value is not false.");

This assertion is not necessary.
Attachment #8877551 - Flags: feedback?(josh) → feedback-
(In reply to Amy Chung [:Amy] from comment #188)
> Created attachment 8877560 [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:
> [Remain Unchanged]
> * I didn't add a assertion when CookieServiceParent got a http-only cookie.
>   Because nsCookieService has the ability to send http-only cookie on
> "added" or "changed" notify.

My point is that CookieServiceParent should not send such a cookie to the child. We should be able to reuse IsCookieExposedToURI to ensure this.
Comment on attachment 8877560 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +175,5 @@
> +  CookiesList *cookiesList = nullptr;
> +  mCookiesMap.Get(key, &cookiesList);
> +
> +  if (!cookiesList) {
> +    return IPC_FAIL(this, "Can't get cookies list");

We should not kill the child process for this. We could be racing with the parent process and have already removed all cookies for this domain, so we should just return ok here.

@@ +220,5 @@
> +  MOZ_ASSERT(aCookiesList.Length() == aAttrsList.Length());
> +  for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
> +    CookieStruct cookieStruct = aCookiesList.ElementAt(i);
> +    if (!RecvRemoveCookie(cookieStruct, aAttrsList.ElementAt(i))) {
> +      return IPC_FAIL(this, "Can't get cookies list");

We don't need to check the return value of RecvRemoveCookie.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +120,5 @@
> +  nsCookieService::
> +    GetBaseDomainFromHost(TLDService, aCookie.host(), baseDomain);
> +  DocumentTracker::DocumentCookieInfoList *docCookieInfoList =
> +    mDocumentTracker.GetMatchingDocumentList(baseDomain, aAttrs);
> +  return docCookieInfoList != nullptr;

This only tells us that there are documents that exist in the same base domain. We should be using IsCookieExposedToURI for this check.

::: netwerk/cookie/CookieServiceParent.h
@@ +10,5 @@
>  #include "mozilla/net/PCookieServiceParent.h"
>  
>  class nsCookieService;
>  class nsCookie;
> +class nsIArray;

This looks unnecessary.
Attachment #8877560 - Flags: feedback?(josh) → feedback-
Hi Josh,
I found if NeckoChild call CookieServiceChild::GetSingleton(), the following error message may occur:
Hit MOZ_CRASH(accessing non-init pref network.cookie.thirdparty.sessionOnly before the rest of the prefs are sent) at /Users/amchung/src/mozilla-central/modules/libpref/prefapi.cpp:779
In my view, maybe CookieServiceChild::GetSingletion() still be called on HttpChannelChild constructor be better?
Flags: needinfo?(amchung) → needinfo?(josh)
Our choices are to add the preference to the list at https://dxr.mozilla.org/mozilla-central/source/dom/ipc/ContentPrefs.cpp#21 or keep the call in HttpChannelChild. Let's keep it in HttpChannelChild, given that big warning on top of the list.
Flags: needinfo?(josh)
Hi,
I have modified the patch as below:
* CookieServiceChild
    * RemoveTrackedCookies
        * Modified the for loop when confirm the cookie which have to be deleted.
    * RecordDocumentCookie
        * Modified the type of aCookie  to nsCookie pointer.
        * Removed the argument aMarkCookiePresent.
* DocumentTracker
    * MarkCookiePresent
        * Set mCookiePresent to true without all conditions.
    * ConfirmDocExist => CookieExposedToAnyDoc.

Would you give me your suggestions?
Thanks!
Attachment #8877548 - Attachment is obsolete: true
Attachment #8878619 - Flags: feedback?(josh)
Hi,
* CookieServiceChild
    * SetCookieStringInternal
        * Moved the code about translating server time to string to origin location.
    * RecordDocumentCookie
        * Added a condition about confirming cookie expired or not.
    * Removed RemoveExpiryCookie().
    * Removed aPermissionService->CanSetCookie, because I found nsCookieService::CheckPref does the same checking and CookieServiceChild already called nsCookieService::CheckPref() on SetCookieStringInternal().
* nsCookieService
    * Create ParseServerTime and set to static.

Would you give me your suggestions?
Attachment #8877551 - Attachment is obsolete: true
Attachment #8878627 - Flags: feedback?(josh)
Hi,
I have modified the patch as below:
* CookieServiceChild
    * RecvRemoveCookie()
        * Modified IPC_FAIL() to IPC_OK().
    * RecvRemoveBatchDeletedCookies
        * Modified IPC_FAIL() to IPC_OK().
* CookieServiceParent
    * CheckCookieCanSend
        * Added a condition of calling IsCookieExposedToURI.
Would you give me your suggestions?
Thanks!
Attachment #8877560 - Attachment is obsolete: true
Attachment #8878632 - Flags: feedback?(josh)
Hi,
Sorry re-uploaded this patch again, I have removed superfluous comment.
Attachment #8878619 - Attachment is obsolete: true
Attachment #8878619 - Flags: feedback?(josh)
Attachment #8878633 - Flags: feedback?(josh)
Comment on attachment 8877289 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

I'm clearing review on this patch until my second question from comment 189 is addressed.
Attachment #8877289 - Flags: review?(josh)
Comment on attachment 8878633 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

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

Only minor changes left!

::: netwerk/cookie/CookieServiceChild.cpp
@@ +107,5 @@
>    SendTrackDocumentLoad(uriParams, baseDomain, attrs);
>  }
>  
>  void
> +CookieServiceChild::RemoveTrackedCookies(const nsCString        &aBaseDomain,

Let's call this PurgeInvisibleCookies.

@@ +244,5 @@
> +    return;
> +  }
> +
> +  cookiesList->AppendElement(aCookie);
> +  mDocumentTracker.MarkCookiePresent(aHostURI, docCookieInfoList);

This belongs in RecvTrackDocAndCookiesLoad, otherwise we invoke it for each cookie that is added.

::: netwerk/cookie/DocumentTracker.cpp
@@ +54,5 @@
> +  aHostURI->GetPath(pathFromURI);
> +  aHostURI->SchemeIs("https", &isHTTPS);
> +  DocumentCookieInfo *docCookieInfo =
> +    GetMatchingDocument(aDocCookieInfoList, hostFromURI, pathFromURI, isHTTPS);
> +  if (!docCookieInfo) {

We should be able to assert that we have a real entry here instead.

@@ +177,5 @@
>      DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(i);
>      if (docCookieInfo->mHostName.Equals(hostFromURI) &&
>          docCookieInfo->mPathName.Equals(pathFromURI) &&
>          docCookieInfo->mSecure == isHTTPS) {
> +      documentDeleted = true;

We actually only need to set this we remove an element. This will let us avoid unnecessary cookie processing.
Attachment #8878633 - Flags: feedback?(josh) → feedback+
Comment on attachment 8878627 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +251,5 @@
> +                     currentTimeInUsec,
> +                     nsCookie::GenerateUniqueCreationTime(currentTimeInUsec),
> +                     aCookieAttributes.isSession,
> +                     aCookieAttributes.isSecure,
> +                     aCookieAttributes.isHttpOnly,

We should return early instead of storing cookies that are http-only.

@@ +285,5 @@
>      }
>    }
>  
>    int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;
> +  if (aCookie->Expiry() <= currentTime) {

It looks like this change belongs in a previous patch.

@@ +465,5 @@
> +                                                cookieString, serverTime, aFromHttp,
> +                                                aChannel, mLeaveSecureAlone, canSetCookie);
> +
> +    if (canSetCookie) {
> +      int64_t currentTime = PR_Now() / PR_USEC_PER_SEC;

This is not used any more.
Attachment #8878627 - Flags: feedback?(josh) → feedback+
Comment on attachment 8878632 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +222,5 @@
> +  for (uint32_t i = 0; i < aCookiesList.Length(); i++) {
> +    CookieStruct cookieStruct = aCookiesList.ElementAt(i);
> +    if (!RecvRemoveCookie(cookieStruct, aAttrsList.ElementAt(i))) {
> +      return IPC_OK();
> +    }

We do not need to check the return value here at all.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +135,5 @@
> +    host.Insert(NS_LITERAL_CSTRING("http://"), 0);
> +  }
> +  nsCOMPtr<nsIURI> uri;
> +  NS_NewURI(getter_AddRefs(uri), host);
> +  return IsCookieExposedToURI(uri, aCookie);

We cannot meaningfully construct a URI to compare against. We need to iterate over all of the known documents for this domain and see if the cookie is exposed to each document's URI.

@@ +181,5 @@
> +
> +  nsCOMPtr<nsICookie> xpcCookie = do_QueryInterface(aSubject);
> +  auto cookie = static_cast<nsCookie*>(xpcCookie.get());
> +  NS_ASSERTION(cookie, "couldn't get cookie");
> +  GetInfoFromCookie(cookie, cookieStruct, attrs);

Let's wait to get the information until we know that we care about the cookie.
Attachment #8878632 - Flags: feedback?(josh) → feedback-
Attached patch test case -- part 6 (obsolete) (deleted) — Splinter Review
Hi,
I have modified the patch as below:
1. separated the test cases to:
    i.   Basic test -- Set cookies
    ii.  Test iframe
    iii. Test XHR
    v.   Test service worker.
    vi.  Test destroy document.
3. In iframe test case, I used Promise to ensure the second iframe is created after first iframe loaded.
4. In XHR test case, I modified the sjs file names.
5. In destroying document test case, I used iframe to implement this test case.
   Because I had tried to use load new URI, I can't control the new document after the URI changed.


Would you give me your suggestions?
Thanks!
Attachment #8867902 - Attachment is obsolete: true
Attachment #8879285 - Flags: feedback?(josh)
Hi,
* I tried to test the test case “browser_force_refresh.js”, the experiments as below:
    * I found the number of calling ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild() will only be once.
    * The number of calling nsDocument::Destroy is Not necessarily just one time.
    * Same as the number of calling nsDocument::SetPricipal.
    * Base on the testing result and the experiments, I removed the assertion which confirms docCookieInfoList on RemoveTrackerdDocument()
* I found the code flow when start to load a new document as below:
    * SetPrincipal ( mChannel is null)
    * Reset ( Set a channel to mChannel)
    * StartDocumentLoad ( Set a channel to mChannel)
    * Base a above reason, the mChannel always is null on SetPrincipal.
    * Do I have to move the code from SetPrincipal() to StartDocumentLoad()?

Would you help me to review my patch?
Thanks!
Attachment #8877289 - Attachment is obsolete: true
Attachment #8879522 - Flags: review?(josh)
Hi,
I have modified the patch as below:
1. RemoveTrackedCookies=>PurgeInvisibleCookies
2. Moved mDocumentTracker.MarkCookiePresent(aHostURI, docCookieInfoList) to RecvTrackDocAndCookiesLoad.
3. Moved documentDeleted = true when docCookieInfoList removed a existing docCookieInfo.
4. Added a assertion on MarkCookiePresent().

Would you help me to review my patch?
Thanks!
Attachment #8878633 - Attachment is obsolete: true
Attachment #8879527 - Flags: review?(josh)
Hi,
I have modified the patch as below:
1. Confirmed the isHttpOnly in advance on CookieServiceChild::SetCookieInternal.
2. Removed int64_t currentTime = PR_Now() / PR_USEC_PER_SEC; from SetCookieStringInternal().

Would you help me to review this patch?
Thanks!
Attachment #8878627 - Attachment is obsolete: true
Attachment #8879529 - Flags: review?(josh)
Attachment #8879529 - Attachment description: bug1331680-part4.patch → implementation part4 -- confirmed the cookie which can save in CookieServiceChild.
Hi,
I have modified the patch as below:
1. Removed to confirm the return value of RecvRemoveCookie.
2. Modified the arguments of IsCookieExposedToURI().
3. Modified CheckCookieCanSend(). 
4. Moved GetInfoFromCookie after calling CheckCookieCanSend().

Would you help me to review this patch?
Thanks!
Attachment #8878632 - Attachment is obsolete: true
Attachment #8879530 - Flags: review?(josh)
Hi,
I have modified the patch as below:
1. Removed the superfluous comment.

Would you help me to review this patch?
Thanks!
Attachment #8870126 - Attachment is obsolete: true
Attachment #8879532 - Flags: review?(josh)
Attached patch test case -- part6 (obsolete) (deleted) — Splinter Review
Hi,
Sorry for reloading this patch, because I missed to add "file_service_worker.js".
Attachment #8879285 - Attachment is obsolete: true
Attachment #8879285 - Flags: feedback?(josh)
Attachment #8879911 - Flags: feedback?(josh)
Attached patch test cases -- part 6 (obsolete) (deleted) — Splinter Review
Hi,
I have fixed the try server error on this patch.

Would you give me your suggestions?
Thanks!
Attachment #8879911 - Attachment is obsolete: true
Attachment #8879911 - Flags: feedback?(josh)
Attachment #8880506 - Flags: feedback?(josh)
Hi,
Sorry for reloading this patch, I have removed nsIURI->GetPath() to nsCookieService::GetPathFromURI() on DocumentTracker.cpp.

And I have a questions as below:
* I found the code flow when start to load a new document as below:
    * SetPrincipal ( mChannel is null)
    * Reset ( Set a channel to mChannel)
    * StartDocumentLoad ( Set a channel to mChannel)
    * Base a above reason, the mChannel always is null on SetPrincipal.
    * Do I have to move the code from SetPrincipal() to StartDocumentLoad()?

Would you give me your suggestions?
Thanks!
Attachment #8879522 - Attachment is obsolete: true
Attachment #8879522 - Flags: review?(josh)
Attachment #8880510 - Flags: review?(josh)
Hi,
I have fixed the try server error on this patch.
Would you give me your suggestions?
Thanks!
Attachment #8879530 - Attachment is obsolete: true
Attachment #8879530 - Flags: review?(josh)
Attachment #8880511 - Flags: review?(josh)
Comment on attachment 8880510 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

Sorry, I looked closely at some code that I reviewed many times before and realized that I forgot the original design that I recommended.

::: dom/base/nsDocument.cpp
@@ +3015,5 @@
>      GetDocGroup();
>    }
>  #endif
> +
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);

Thanks for the investigation. Let's move this code to nsDocument::StartDocumentLoad, but not before the code that invokes Reset.

@@ +3035,5 @@
> +      MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
> +    }
> +    bool newDocument = true;
> +    if (synthesized) {
> +      ContentChild::UpdateDocumentStatus(aNewPrincipal, newDocument);

Whoops, I forgot about an important point in previous reviews. We should always call ContentChild::UpdateDocumentStatus if this is a document that needs cookies; however, if this is a synthesized channel, that is when we should send a message to the parent process about this document. We can add another argument to UpdateDocumentStatus to control this.

@@ +8699,5 @@
>    return true;
>  }
>  
> +bool
> +ConfirmProtocolOnChannel(nsIChannel *aChannel)

Please remove this check.

@@ +8752,5 @@
> +  chan->GetLoadFlags(&loadFlags);
> +  bool isDocument = false;
> +  chan->GetIsDocument(&isDocument);
> +  if (loadFlags &
> +        nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&

nit: Move this on to the previous line.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +90,5 @@
> +  bool isInit = false;
> +  mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);
> +  SendTrackDocumentLoad(uriParams, baseDomain, attrs);

We should not send anything to the parent process at this point. The parent will learn about it from AboutToLoadHttpFtpWyciwygChannel.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +103,5 @@
> +  bool isInit = true;
> +  mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);
> +  Unused << SendTrackDocumentLoad(uriParams, baseDomain, attrs);

We should not need to send the child a notification here. This code is called from AboutToLoadHttpFtpWyciwygChannel, which means the child already knows about this document.

::: netwerk/cookie/DocumentTracker.cpp
@@ +89,5 @@
> +{
> +  nsAutoCString key = GenerateStringKey(aBaseDomain, aAttrs);
> +  DocumentCookieInfoList *docCookieInfoList = nullptr;
> +  mDocMap.Get(key, &docCookieInfoList);
> +  if (!docCookieInfoList) {

I would like to continue asserting here. We can use the assertion to help us verify our assumptions about the design; if we remove it, we have no idea if it's working or not.

::: netwerk/cookie/PCookieService.ipdl
@@ +107,4 @@
>    async __delete__();
> +
> +both:
> +  async TrackDocumentLoad(URIParams host,

This message should only be necessary from child->parent. There are no documents loaded in the parent that must be reported to the child.
Attachment #8880510 - Flags: review?(josh) → review-
Comment on attachment 8879527 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

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

The changes I discussed in part1 also mean we need to make some changes in this one, unfortunately.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +105,5 @@
>    mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
>    URIParams uriParams;
>    SerializeURI(uri, uriParams);
> +
> +  // Send matching cookies to Child.

We will need to send these cookies in response to RecvTrackDocumentLoad, as well.
Attachment #8879527 - Flags: review?(josh) → review-
Comment on attachment 8879529 [details] [diff] [review]
implementation part4 -- confirmed the cookie which can save in CookieServiceChild.

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

::: netwerk/cookie/CookieServiceChild.h
@@ +83,5 @@
>                                     nsIChannel *aChannel,
>                                     const char *aCookieString,
>                                     const char *aServerTime,
>                                     bool aFromHttp);
> +   

nit: remove this trailing whitespace.
Attachment #8879529 - Flags: review?(josh) → review+
Comment on attachment 8880511 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

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

::: netwerk/cookie/CookieServiceParent.cpp
@@ +126,5 @@
> +     DocumentCookieInfo *docCookieInfo = &docCookieInfoList->ElementAt(i);
> +     if (!IsCookieExposedToURI(docCookieInfo->mHostName,
> +                               docCookieInfo->mPathName,
> +                               docCookieInfo->mSecure, aCookie)) {
> +       return false;

This logic is inverted; it means we will only send a cookie to the child if it is visible to every single document for that domain.

::: netwerk/cookie/CookieServiceParent.h
@@ +58,5 @@
>  
>    bool
> +  IsCookieExposedToURI(const nsCString& aHostName,
> +                       const nsCString& aPathName,
> +                       const bool& aSecure,

bool aSecure

::: netwerk/cookie/DocumentTracker.cpp
@@ +46,5 @@
>  void
>  DocumentTracker::MarkCookiePresent(nsIURI                 *aHostURI,
>                                     DocumentCookieInfoList *aDocCookieInfoList)
>  {
> +  if (!aHostURI) {

This change does not look like it belongs in this patch. Why are we calling this with a null URI?
Attachment #8880511 - Flags: review?(josh) → review-
Attachment #8879532 - Flags: review?(josh) → review+
Comment on attachment 8880506 [details] [diff] [review]
test cases -- part 6

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

There are a bunch of uses of postMessage that we should be able to use '*' instead of an explicit origin.

::: netwerk/test/mochitests/file_1331680.js
@@ +6,5 @@
> +var observer = {
> +  observe: function(subject, topic, data) {
> +    if (subject) {
> +      let cookie = subject.QueryInterface(Ci.nsICookie2);
> +      sendAsyncMessage("cookieName", cookie.name);

Can we send the value along with the name?

@@ +12,5 @@
> +  }
> +};
> +
> +addMessageListener("removeCookies" , function(e) {
> +});

What is the purpose of this?

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

Let's use different values.

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

Can we postMessage document.cookie this and verify that the original test file does not see this?

::: netwerk/test/mochitests/file_iframe_create_cookie.html
@@ +1,5 @@
> +<!DOCTYPE html>
> +<html>
> +<script>
> +document.cookie = "cookie_destroy0=123";
> +document.cookie = "cookie_destroy1=123";

Let's use different values.

::: netwerk/test/mochitests/file_service_worker.js
@@ +1,2 @@
> +self.addEventListener('activate', function(event) {
> +  self.clients.claim();

I don't believe we need this for this test.

@@ +2,5 @@
> +  self.clients.claim();
> +});
> +
> +self.addEventListener('fetch', function(event) {
> +  event.respondWith(new Response('Test'));

This service worker should respond with a page that uses postMessage to send its document.cookie value to the original test file. We can use fetch(..) to retrieve file_iframe_intercepted.html and respond with that.

::: netwerk/test/mochitests/set_cookie_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);

Neither of these headers or the comment should be necessary.

@@ +3,5 @@
> +{
> +    // avoid confusing cache behaviors
> +    response.setHeader("Cache-Control", "no-cache", false);
> +    response.setHeader("Content-Type", "text/html", false);
> +    response.setHeader("Set-Cookie", "xhr1=123; path=/", false);

We can use a single SJS file that takes a name URL parameter instead of have two SJS files.

::: netwerk/test/mochitests/test_1331680.html
@@ +20,5 @@
> +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('file_1331680.js'));
> +
> +gScript.addMessageListener("cookieName", confirmCookieName);
> +
> +gScript.sendAsyncMessage('createObserver');

Let's move this right before we start setting cookies.

@@ +24,5 @@
> +gScript.sendAsyncMessage('createObserver');
> +var testsNum = 0;
> +// Confirm the notify which represents the cookie is updating.
> +function confirmCookieName(name) {
> +  for (var i = 0; i < COOKIE_NAMES.length; i++) {

Rather than iterating over all of the possible cookie names, could we have an explicit order of cookie notifications that we expect?

@@ +26,5 @@
> +// Confirm the notify which represents the cookie is updating.
> +function confirmCookieName(name) {
> +  for (var i = 0; i < COOKIE_NAMES.length; i++) {
> +    if (name == COOKIE_NAMES[i]) {
> +      is(name, COOKIE_NAMES[i], "The cookie which names " + name + " is update to db");

"An update for the cookie named " + name + " was observed"

@@ +44,5 @@
> +const COOKIE_NAMES = ["cookie0", "cookie1", "cookie2"];
> +function testSetCookie() {
> +  document.cookie = COOKIE_NAMES[0] + "=123";
> +  document.cookie = COOKIE_NAMES[1] + "=123; HttpOnly";
> +  document.cookie = COOKIE_NAMES[2] + "=123";

Let's use distinct values for each cookie.

@@ +50,5 @@
> +  var confirmCookieString = COOKIE_NAMES[0] + "=123; " + COOKIE_NAMES[2] + "=123";
> +  is(getCookieString, confirmCookieString, "Confirm the cookies string which be get is current.");
> +  for (var i = 0; i < COOKIE_NAMES.length; i++) {
> +    document.cookie = COOKIE_NAMES[i] + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +  }

Can we verify that document.cookie is empty now?

::: netwerk/test/mochitests/test_1331680_destroy.html
@@ +20,5 @@
> + * 3. Create second iframe and set the src to "http://test2.example.org/" (Same base domain to first iframe).
> + * 4. Confirm second iframe can get the cookies which set from iframe 1.
> + * 5. Delete first iframe and second iframe.
> + * 6. Create third iframe and set src to "http://sub1.test2.example.org" (Same base domain to first iframe and second iframe).
> + * 7. Confirm third iframe can't get any cookies.

I don't understand why the third iframe is not supposed to be able to see the cookies.

@@ +56,5 @@
> +  .then(_ => createIframe("if2", "http://test2.example.org:80/tests/netwerk/test/mochitests/file_iframe_get_cookie.html", "allow-scripts allow-same-origin"))
> +  .then(_ => deleteIframes("if1"))
> +  .then(_ => deleteIframes("if2"))
> +  .then(_ => createIframe("if3", "http://sub1.test2.example.org:80/tests/netwerk/test/mochitests/file_iframe_get_cookie.html", "allow-scripts allow-same-origin"))
> +  .then(_ => SimpleTest.finish());

We should have tests that cookies are accessible for iframes:
* with no sandboxing at all
* with allow-scripts and allow-same-origin

We should also have tests that cookies are not accessible for iframes with only allow-scripts or allow-same-origin, but not both.

::: netwerk/test/mochitests/test_1331680_iframe.html
@@ +26,5 @@
> +gScript.sendAsyncMessage('createObserver');
> +// Confirm the notify which represents the cookie is updating.
> +var testsNum = 0;
> +function confirmCookieName(name) {
> +  for (var i = 0; i < IFRAME_COOKIE_NAMES.length; i++) {

Let's be more strict about the order of notifications that we expect.

@@ +56,5 @@
> +    document.body.appendChild(ifr);
> +  });
> +};
> +
> +function confrimCookies(id) {

confirm

@@ +59,5 @@
> +
> +function confrimCookies(id) {
> +  is(document.cookie, "if2_1=123; if2_2=123", "Confirm the cookies can get after iframe was deleted");
> +  var new_ifr = document.getElementById(id);
> +  var innerDoc = (new_ifr.contentDocument) ? new_ifr.contentDocument : new_ifr.contentWindow.document;

We should always be able to use contentDocument.

::: netwerk/test/mochitests/test_1331680_service_worker.html
@@ +14,5 @@
> +<p id="display"></p>
> +<div id="content" style="display: none">
> +<script type="application/javascript">
> +
> +SimpleTest.waitForExplicitFinish();

Most of the implementation of this test needs to run in a different domain. This page will need to
* load an iframe from that domain which registers a service worker, then sets some cookies
* remove the iframe
* load another iframe from that domain that is intercepted by the service worker; the loaded page should postMessage its document.cookie value then unregister its service worker

@@ +56,5 @@
> +}
> +
> +function createCookie() {
> +  document.cookie = "SW_test1=123";
> +  document.cookie = "SW_test2=123";

Let's use distinct values.

::: netwerk/test/mochitests/test_1331680_xhr.html
@@ +1,4 @@
> +<!DOCTYPE HTML>
> +<html>
> +<!--
> +XHR uppercases certain method names, but not others

This is not a description of this test.

@@ +21,5 @@
> +gScript.sendAsyncMessage('createObserver');
> +// Confirm the notify which represents the cookie is updating.
> +var cookiesNum = 0;
> +function confirmCookieName(name) {
> +  for (var i = 0; i < XHR_COOKIE_NAMES.length; i++) {

Let's be more strict about the order we expect cookie notifications to arrive.

@@ +29,5 @@
> +      if (cookiesNum == 2) {
> +        is(document.cookie, "xhr1=123", "Confirm the cookie string"); 
> +        for (var i = 0; i < XHR_COOKIE_NAMES.length; i++) {
> +          document.cookie = XHR_COOKIE_NAMES[i] + "=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +        }

Let's confirm that document.cookie is empty.

@@ +52,5 @@
> + * 4. Child process only can get the xhr1 cookie from cookies hash table.
> + */
> +Promise.resolve()
> +  .then(_ => createXHR('set_cookie_xhr1.sjs'))
> +  .then(_ => createXHR('set_cookie_xhr2.sjs'));

This promise is not doing anything useful. We should make createXHR return a promise that resolves when the xhr's onload is called; then we can use
createXHR(...).then(() => createXHR(...));
Attachment #8880506 - Flags: feedback?(josh) → feedback-
Hi Michael,
I have a question on try server error ( https://treeherder.mozilla.org/#/jobs?repo=try&revision=46e39aa6c582a9a095f6e7a5b3d5194d3217da40&selectedJob=109203179).
I found some unit tests (ex: netwerk/test/unit_ipc/test_bug528292_wrap.js) triggered the assertion on nsPermissionManager::PermissionAvaliable.
And I confirmed the call stack, the error occurred when setting cookie on CookieServiceChild.
The call stack as below:
Assertion failure: PermissionAvaliable(prin, aType), at /home/worker/workspace/build/src/extensions/cookie/nsPermissionManager.cpp:2273
#01: nsPermissionManager::TestPermission [extensions/cookie/nsPermissionManager.cpp:2126]
#02: nsCookieService::CheckPrefs [netwerk/cookie/nsCookieService.cpp:4189]
#03: mozilla::net::CookieServiceChild::SetCookieStringInternal [netwerk/cookie/CookieServiceChild.cpp:522]
How do I confirm whether the permission is copied from parent to child when running unit test?
Flags: needinfo?(michael)
(In reply to Amy Chung [:Amy] from comment #220)
> Hi Michael,
> I have a question on try server error (
> https://treeherder.mozilla.org/#/
> jobs?repo=try&revision=46e39aa6c582a9a095f6e7a5b3d5194d3217da40&selectedJob=1
> 09203179).
> I found some unit tests (ex: netwerk/test/unit_ipc/test_bug528292_wrap.js)
> triggered the assertion on nsPermissionManager::PermissionAvaliable.
> And I confirmed the call stack, the error occurred when setting cookie on
> CookieServiceChild.
> The call stack as below:
> Assertion failure: PermissionAvaliable(prin, aType), at
> /home/worker/workspace/build/src/extensions/cookie/nsPermissionManager.cpp:
> 2273
> #01: nsPermissionManager::TestPermission
> [extensions/cookie/nsPermissionManager.cpp:2126]
> #02: nsCookieService::CheckPrefs [netwerk/cookie/nsCookieService.cpp:4189]
> #03: mozilla::net::CookieServiceChild::SetCookieStringInternal
> [netwerk/cookie/CookieServiceChild.cpp:522]
> How do I confirm whether the permission is copied from parent to child when
> running unit test?

We send permissions from the parent process to the content process when the load for a given page is initiated. You can see us send down the permissions here:
http://searchfox.org/mozilla-central/rev/3291398f10dcbe192fb52e74974b172616c018aa/dom/ipc/ContentParent.cpp#5107-5108

From a quick look at the linked test, it looks like the test is trying to set cookies in the content process before any permissions have been transmitted for the host. This then checks the cookie permissions for the given host, and since we haven't loaded any resources from that host yet in the current content process, the content process isn't supposed to read permissions yet. 

You could force permissions to be loaded for that process by performing a dummy HTTP load to that domain before loading it. We don't have a method for requesting permissions from the parent process without performing a load.
Flags: needinfo?(michael)
Hi,
I have added the conditions same as https://dxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsCookieService.cpp#3320-3342.
Would you help me to review my patch?
Thanks!
Attachment #8876179 - Attachment is obsolete: true
Attachment #8881430 - Flags: review?(josh)
Attachment #8881430 - Flags: review?(josh) → review+
Spoke to Josh a bit about the status of this on IRC just now.  Since I have some experience with service workers, and was looking for a way to help, I offered with fixing up the service worker test (part 6 patch) here.  I should be able to put up a patch for it that addresses comment 219 fairly soon!
(In reply to :Ehsan Akhgari (needinfo please, extremely long backlog) from comment #223)
> Spoke to Josh a bit about the status of this on IRC just now.  Since I have
> some experience with service workers, and was looking for a way to help, I
> offered with fixing up the service worker test (part 6 patch) here.  I
> should be able to put up a patch for it that addresses comment 219 fairly
> soon!
I truly appreciate your help in fixing up this test case.
So this test case follows a pattern very common in many other service worker tests so I copy pasted an existing one basically in the SW test suite (which basically amounts to rewriting the test from scratch!).  Since this bug is already way too long I filed it separately in bug 1377019.  I'll land it and then you can rebase on top of it or use the patch, whichever is easier.  :-)
Depends on: 1377019
Hi,
I have modified my patch as below:
[IPC]
1. Removed the ipc of sending the notify about updating hash table from parent to child.

[nsDocument]
1. Moved the code from SetPrincipal to StartDocumentLoad().
2. Added a argument names aSendToParent on UpdateDocumentStatus.
3. Create a member names "mAddToTracker" on nsDocument, and set the default value to false.
4. Create a function names RemoveDocFromTracker().
5. Added a condition which confirms mAddToTracker is true on ~nsDocument.
   - if true, call RemoveDocFromTracker().
6. Removed ConfirmProtocolOnChannel().

Would you help me to review my patch?
Thanks!
Attachment #8880510 - Attachment is obsolete: true
Attachment #8882589 - Flags: review?(josh)
Hi,
I have modified my patch as below:
1. Added code about sending document and cookies on 
CookieServiceParent::RecvTrackDocumentLoad().
2. Removed the assertion on CookieServiceChild::RecvTrackDocAndCookiesLoad().

Would you help me to review my patch?
Thanks!
Attachment #8879527 - Attachment is obsolete: true
Attachment #8882597 - Flags: review?(josh)
Hi,
I have modified my patch as below:
1. Inverted the logic on CheckCookieCanSend.
2. IsCookieExposedToURI(...const bool& aSecure)->IsCookieExposedToURI(...bool aSecure).

Would you help me to review my patch?
Thanks!
Attachment #8880511 - Attachment is obsolete: true
Attachment #8882611 - Flags: review?(josh)
Comment on attachment 8882589 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

::: dom/base/nsDocument.cpp
@@ +8723,5 @@
> +      isDocument &&
> +      XRE_IsContentProcess()) {
> +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
> +    bool sendToParent = true;
> +    mAddToTracker = false;

Let's assert that mAddToTracker is true before setting this to false.

@@ +8758,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();
> +
> +  RemoveDocFromTracker();

After talking with smaug, we should remove this and always rely on ~nsDocument instead.

::: dom/ipc/ContentChild.cpp
@@ +1844,5 @@
>  
> +void
> +ContentChild::UpdateDocumentStatus(nsIPrincipal *aPrincipal,
> +                                   bool          aNewDocument,
> +                                   const bool   &aSendToParent)

Just bool, not const bool&

::: netwerk/cookie/CookieServiceParent.cpp
@@ +102,5 @@
> +  aPrincipal->GetBaseDomain(baseDomain);
> +  bool isInit = true;
> +  mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);

This serialized URI is currently not used. However, we still need to send the request URL and the response URL like we discussed, otherwise the child will become confused by redirects.

::: netwerk/test/mochitests/mochitest.ini
@@ +23,5 @@
>  [test_user_agent_updates.html]
>  [test_user_agent_updates_reset.html]
>  [test_viewsource_unlinkable.html]
>  [test_xhr_method_case.html]
> +[test_1331680_cnn.html]

This file is missing.

::: netwerk/test/unit/test_http_redirect.js
@@ +33,5 @@
> +  },
> +
> +  onStartRequest: function(request, ctx) {
> +    do_check_eq(request.status, Cr.NS_OK);
> +    this.count++;

I'm not sure I understand what this test is verifying. We should never receive more than one OnStartRequest and one OnStopRequest notification for any channel.
Attachment #8882589 - Flags: review?(josh) → review-
Comment on attachment 8882597 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

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

::: netwerk/cookie/CookieServiceParent.cpp
@@ +181,5 @@
> +                     getter_AddRefs(dummyChannel));
> +  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
> +  thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
> +  bool isForeign = true;
> +  thirdPartyUtil->IsThirdPartyChannel(dummyChannel, hostURI, &isForeign);

We should send the value of IsThirdPartyChannel from the child instead of using a dummy channel here.
Attachment #8882597 - Flags: review?(josh) → review-
Attachment #8882611 - Flags: review?(josh) → review+
Hi,
I have modified the patch as below:
1. Added a nsIChannel argument on ContentChild::UpdateDocumentStatus.
2. Created isForeign before calling SendTrackDocumentLoad() on CookieServiceChild.
3. Added a bool argument on CookieServiceParent::RecvTrackDocumentLoad().
4. Deleted dummyChannel on CookieServiceParent::RecvTrackDocumentLoad().

Would you help me to review this patch?
Thanks!
Attachment #8882597 - Attachment is obsolete: true
Attachment #8882637 - Flags: review?(josh)
Comment on attachment 8882637 [details] [diff] [review]
implementation part3 -- send and purge matching cookies

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +105,5 @@
>    bool isInit = false;
>    mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
>    URIParams uriParams;
>    SerializeURI(uri, uriParams);
> +  bool isForeign;

We need to initialize this or it will contain garbage if RequireThirdPartyCheck is false.
Attachment #8882637 - Flags: review?(josh) → review+
Hi,
I have modified the patch as below:
1. Added the function of redirect.
2. Removed "RemoveDocFromTracker" on Destroy().
3. I found a problem as below:
   * When Reset() is called in the second time, the isDocument is false and loadflags doesn't include LOAD_DOCUMENT_NEEDS_COOKIE.
   * I have confirmed the loadflags on nsHTMLDocument::Open().
     * LOAD_DOCUMENT_NEEDS_COOKIE doesn't exist on loadfalg from caller channel.
   * Base on above reason, I removed the document when Reset() is called in the second time.

Would you help me to review my patch?
Thanks!
Attachment #8882589 - Attachment is obsolete: true
Attachment #8883375 - Flags: review?(josh)
Comment on attachment 8883375 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

We will need to add an automated test to patch6 for a page that has a cross-domain redirect and checks that it can see the cookies for the redirected domain.

::: dom/base/nsDocument.cpp
@@ -2086,5 @@
>      }
>    }
>  
>    principal = MaybeDowngradePrincipal(principal);
> -

nit: revert this change.

@@ +2110,5 @@
>      }
>    }
>  
>    mChannel = aChannel;
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);

We can use mChannel directly.

@@ +2118,5 @@
> +  nsLoadFlags loadFlags;
> +  chan->GetLoadFlags(&loadFlags);
> +  bool isDocument = false;
> +  chan->GetIsDocument(&isDocument);
> +  if (!(loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) &&

What if we do this instead:

nsLoadFlags loadFlags;
aChannel->GetLoadFlags(&loadFlags);
if (!(loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) || !aChannel->GetIsDocument()) {
    RemoveDocFromTracker();
}

mChannel = aChannel;


This will avoid further code duplication and better express the condition we care about.

@@ +2558,5 @@
>      }
>    }
>  
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);
> +  if (!chan) {

We should be able to use mChannel directly, and returning early here is incorrect.

@@ +8725,5 @@
>  
>  void
> +nsDocument::RemoveDocFromTracker()
> +{
> +  nsCOMPtr<nsIChannel> chan = do_QueryInterface(mChannel);

We can use mChannel directly.

::: dom/ipc/ContentParent.cpp
@@ +5124,5 @@
> +  aChannel->GetLoadFlags(&newLoadFlags);
> +  bool isDocument = false;
> +  aChannel->GetIsDocument(&isDocument);
> +  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {
> +    UpdateDocumentStatus(principal, nullptr);

We should get the channel's original principal here (https://dxr.mozilla.org/mozilla-central/source/caps/nsIScriptSecurityManager.idl#214-218) and pass that as the old principal. This will correctly deal with any redirects that occurred without us having to keep track of them.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +88,5 @@
> +{
> +  nsCOMPtr<nsIURI> newHostURI = DeserializeURI(aNewHost);
> +  nsCOMPtr<nsIURI> oldHostURI = DeserializeURI(aOldHost);
> +  mDocumentTracker.RemoveTrackedDocument(oldHostURI, aOldBaseDomain, aOldAttrs);
> +  mDocumentTracker.TrackNewDocument(newHostURI, aNewBaseDomain, aNewAttrs, true);

Let's do this:
* compare the URLs - if they are equal, we do not need to do anything.
* track the new document
* remove the old document
(in a later patch we will also need to run the purge cookies operation at this point)

::: netwerk/cookie/PCookieService.ipdl
@@ +111,4 @@
>    async __delete__();
> +
> +child:
> +  async TrackRedirectDocLoad(URIParams newHost,

Let's call this TrackRequestedDocLoad.

@@ +114,5 @@
> +  async TrackRedirectDocLoad(URIParams newHost,
> +                             URIParams oldHost,
> +                             nsCString newBaseDomain,
> +                             nsCString oldBaseDomain,
> +                             OriginAttributes aNewAttrs,

The origin attributes will not not change, so we only need one.

::: netwerk/protocol/http/HttpChannelChild.cpp
@@ +43,5 @@
>  #include "nsInputStreamPump.h"
>  #include "InterceptedChannel.h"
>  #include "mozIThirdPartyUtil.h"
>  #include "nsContentSecurityManager.h"
> +#include "nsCookieService.h"

This is not necessary anymore.

::: netwerk/protocol/http/HttpChannelParent.cpp
@@ +1373,5 @@
> +  }
> +  nsCOMPtr<nsIPrincipal> newPrincipal, oldPrincipal;
> +  ssm->GetChannelResultPrincipal(aNewChannel, getter_AddRefs(newPrincipal));
> +  ssm->GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
> +  cp->UpdateDocumentStatus(newPrincipal, oldPrincipal);

I don't think this code is necessary, and we don't want to send a notification for each redirection that occurs, since we only care about the final one.

@@ -1396,5 @@
>      LOG(("  aRequest is not nsHttpChannel"));
>      NS_ERROR("Expecting only nsHttpChannel as aRequest in HttpChannelParent::OnStartRequest");
>      return NS_ERROR_UNEXPECTED;
>    }
> -

nit: revert this change.

@@ +1420,5 @@
>  
> +  nsCOMPtr<nsIURI> newURI;
> +  mChannel->GetURI(getter_AddRefs(newURI));
> +  nsAutoCString newspec;
> +  newURI->GetSpec(newspec);

The URI and spec are not used.

@@ +1573,5 @@
>  
> +  nsCOMPtr<nsIURI> newURI;
> +  mChannel->GetURI(getter_AddRefs(newURI));
> +  nsAutoCString newspec;
> +  newURI->GetSpec(newspec);

The URI and spec are not used.

::: netwerk/protocol/http/HttpChannelParentListener.cpp
@@ +207,5 @@
> +      MOZ_ASSERT(mNextListener);
> +      RefPtr<HttpChannelParent> channel = do_QueryObject(mNextListener);
> +      channel->NotifyChannelRedirect(newChannel, oldChannel);
> +    }
> +  }

I don't think any of this code is necessary.
Attachment #8883375 - Flags: review?(josh) → review-
Hi,
I have modified the patch as below:
[nsDocument]
1. Used mChannel directly.
2. Moved the code about removing document above " mChannel = aChannel;" on Reset().

[ContentParent]
1. Found the old principal from origin URI and set it to UpdateDocumentStatus.

[CookieServiceChild]
1. Added the condition of comparing old URI and new URI.
2. TrackRedirectDocLoad => TrackRequestedDocLoad.

[HttpChannelParent & HttpChannelParentListener]
1. Removed the modification.

Would you help me to review my patch?
Thanks!
Attachment #8883375 - Attachment is obsolete: true
Attachment #8883653 - Flags: review?(josh)
Hi,
I have modified this patch as below:
1. Separated the ipc function of receiving the notification about updating document and cookies from parent.
2. Purged cookie when occurred the redirection.

Would you help me to review this patch?
Thanks!
Attachment #8882637 - Attachment is obsolete: true
Attachment #8883656 - Flags: review?(josh)
Comment on attachment 8883653 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

::: dom/base/nsDocument.cpp
@@ +2121,5 @@
> +  if ((!(loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) ||
> +       !isDocument) &&
> +      principal &&
> +      XRE_IsContentProcess() &&
> +      mAddToTracker) {

I suggested specific code to use instead of what currently is here (although use `!isDocument` instead of `!aChannel->GetIsDocument()`, which was incorrect).

@@ +2558,5 @@
>        mIsSrcdocDocument = true;
>      }
>    }
>  
> +  if (mChannel) {

My previous review included "returning early here is incorrect".

::: dom/ipc/ContentParent.cpp
@@ +5118,5 @@
>    rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
>    NS_ENSURE_SUCCESS(rv, rv);
>  
> +  nsCOMPtr<nsIURI> originalURI;
> +  aChannel->GetOriginalURI(getter_AddRefs(originalURI));

Let's do this extra work inside the if statement; otherwise it's unused.

@@ +5122,5 @@
> +  aChannel->GetOriginalURI(getter_AddRefs(originalURI));
> +
> +  OriginAttributes attrs = principal->OriginAttributesRef();
> +
> +  nsCOMPtr<nsIPrincipal> oldPrincipal = BasePrincipal::CreateCodebasePrincipal(originalURI, attrs);;

nit: remove the extra ;

::: netwerk/cookie/CookieServiceChild.cpp
@@ +90,5 @@
> +  bool sameURI = false;
> +  newHostURI->Equals(oldHostURI, &sameURI);
> +  if (!sameURI) {
> +    mDocumentTracker.RemoveTrackedDocument(oldHostURI, aOldBaseDomain, aAttrs);
> +    mDocumentTracker.TrackNewDocument(newHostURI, aNewBaseDomain, aAttrs, true);

If we call TrackNewDocument first, we can avoid unnecessarily purging cookies.

@@ +97,5 @@
> +}
> +
> +void
> +CookieServiceChild::TrackDocumentLoad(nsIPrincipal *aPrincipal,
> +                                      const bool   &aSendToParent)

bool aSendToParent

@@ +108,5 @@
> +  aPrincipal->GetURI(getter_AddRefs(uri));
> +  bool isInit = false;
> +  mDocumentTracker.TrackNewDocument(uri, baseDomain, attrs, isInit);
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);

We only need to serialize the URI if aSendToParent is true.
Attachment #8883653 - Flags: review?(josh) → review-
Attachment #8883656 - Flags: review?(josh) → review+
Hi,
I have modified my patch as below:
1. Modified the if conditions on Reset().
2. Removed the if condition of confirming mChannel is null on  nsDocument::StartDocumentLoad.
3. Added if condition of confirming originalURI is not null on ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild().
4. Moved SerializeURI into "if (aSendToParent)" on 	
CookieServiceChild::TrackDocumentLoad().
5. Swapped mDocumentTracker.TrackNewDocument and mDocumentTracker.RemoveTrackedDocument on CookieServiceChild::RecvTrackRequestedDocLoad().

Would you help me to review my patch?
Thanks!
Attachment #8883653 - Attachment is obsolete: true
Attachment #8884002 - Flags: review?(josh)
Comment on attachment 8884002 [details] [diff] [review]
implementation part1 -- Data Struct, Update entry to Document HashTable, Remove entry from Document Hashtable.

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

::: dom/base/nsDocument.cpp
@@ +2110,5 @@
>        mChromeXHRDocBaseURI = nullptr;
>      }
>    }
>  
> +  if (!aChannel) {

We should not be returning early here, since that changes the behaviour of the method.

@@ +2119,5 @@
> +  bool isDocument = false;
> +  aChannel->GetIsDocument(&isDocument);
> +  if (!(loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) || !isDocument) {
> +    if (XRE_IsContentProcess() && mAddToTracker && principal) {
> +      ContentChild::UpdateDocumentStatus(principal, false, false);

My suggestion was to call RemoveDocFromTracker. Why are we not using that?

::: dom/ipc/ContentParent.cpp
@@ +5124,5 @@
> +  nsCOMPtr<nsIURI> originalURI;
> +  aChannel->GetOriginalURI(getter_AddRefs(originalURI));
> +  nsCOMPtr<nsIPrincipal> oldPrincipal;
> +
> +  if (originalURI) {

The documentation says "This is never null.", so we should rely on that.

@@ +5126,5 @@
> +  nsCOMPtr<nsIPrincipal> oldPrincipal;
> +
> +  if (originalURI) {
> +    OriginAttributes attrs = principal->OriginAttributesRef();
> +    oldPrincipal = BasePrincipal::CreateCodebasePrincipal(originalURI, attrs);

When I said "Let's do this extra work inside the if statement", I meant that oldPrincipal is only used when calling UpdateDocumentStatus. Let's move this work inside that if statement.
Attachment #8884002 - Flags: review?(josh) → review+
Attached patch test case -- part 6 (obsolete) (deleted) — Splinter Review
Hi Josh,
I have modified the patch as below:
1. Added a notification names "doc-table-clear" on CookieServiceParent.
2. Modified the steps of the destroy test.
   i.    Create new notification names "doc-table-clear" on CookieServiceParent.
   ii.   Create a iframe (id=if1, sandbox flag = allow-scripts allow-same-origin, src=http://example.org/tests/netwerk/test/mochitests/xxx1.html) and create two cookies names "if_cookie1=if_del1" and "if_cookie2=if_del2".
   iii.  Create a iframe (id=if2, sandbox flag = allow-scripts allow-same-origin src=http://example.org/tests/netwerk/test/mochitests/xxx2.html) and post document.cookie to parent.
   iv.   Confirm the if2.doucment.cookie = "if_cookie1=if_del1; if_cookie2=if_del2".
   v.    Create a iframe (id=if3, src=http://example.org/tests/netwerk/test/mochitests/xxx3.html) and confirm if3 can access cookie string "if_cookie1=if_del1; if_cookie2=if_del2".
   vi.   Delete if1, if2 and if3.
   vii.  Confirm the "doc-table-clear" is triggered.
   viii. Clear all cookies.
3. Removed the service worker test(I have tested Bug 1377019, the testing results are pass).
4. Added a test case of redirection.
5. Modified sync pref id to true on xpcshell test for avoiding try server error.

Would you give me your suggestions?
Thanks!
Attachment #8880506 - Attachment is obsolete: true
Attachment #8885882 - Flags: feedback?(josh)
Ok, so we face a problem. The problem is that my mental model for when StartDocumentLoad is called is completely incorrect, which means that the model for how we track documents in the parent and child processes is also incorrect. As an example, consider this:
* foo.com/a.html creates an iframe for bar.com/b.html
* the iframe loads about:blank immediately, and creates a network request for bar.com/b.html
* the parent process requests bar.com/b.html
* the parent receives a network response for bar.com/b.html and sends the cookies for bar.com/b.html to the child
* the parent sends the network response for bar.com/b.html to the child
* nsDocument::StartDocumentLoad runs as a result of the child receiving the network response

The model I proposed in comment 100 is based on the idea that the child process keeps track of documents that have not yet received a network response. The code we have right now does not do that, and that means that we will incorrectly deal with cases where we remove an iframe (or close a tab) before we receive a network response for its target. Even if we figure out a way to implement the original model correctly, I am not confident that we can plug all of the holes in time for this to ship and not regress the behaviour of cookies in the wild.

I am worried that we keep finding significant holes in our model and implementation. I am especially worried by how little time is left before FF 57 becomes the nightly release. I think we should revise our plan and implement a less complicated solution that directly addresses the original sync IPC performance problem.
A long time ago I proposed an idea where the parent process would have a "cookie thread" that would be responsible for storing cookies. The child process would have a separate IPC channel to this thread. It would still use sync messages to look up cookie values. However, the response should be much faster than to the main thread since the cookie thread wouldn't be bogged down with things like layout, painting, chrome JS, etc.

I think this would be a lot simpler, although it wouldn't be as fast as the approach here, and it wouldn't have the privacy benefits.
I wrote up an implementation plan that involved replicating the cookies in all processes before seeing bill's suggestion. I'm going to try creating the IPC thread that comment 242 suggests; if I can get it to work (since it's all new to me), I'll attach my patch and Amy can work on making the cookie service threadsafe and get it to talk to the new thread. If it looks like it will be too complicated, I'll post my original plan instead.
Attached patch Background cookie thread (not working) (obsolete) (deleted) — Splinter Review
I got the browser to start up, and then it crashed immediately when loading google.com as expected. The first immediate problem is that we can't deserialize URLs off the main thread; when I dug deeper into the code that the cookie service uses, I become confident that making the cookie service usable off the main thread is a larger task than comment 242 implies. I am going to post my alternative suggestion that allows reusing much of the existing work instead. I am posting my WIP infrastructure for posterity.
I propose the following plan for a less complicated solution to the sync IPC problem:

Part 1:
* CookieServiceChild gets a new member: a hashtable of nsCookieKey keys to nsTArray<nsRefPtr<nsCookie>> values
* every time we create a new content process (ContentParent::Init), we send all of the known cookies (except for httponly cookies) to the new process and they are stored in CookieServiceChild's hashtable
* when CookieServiceChild::GetCookieStringInternal is called,
  * if network.cookie.ipc.sync is true, perform the existing sync IPC request
  * otherwise, retrieve the appropriate cookies from the hashtable and turn them into a string without performing any IPC
* when CookieServiceChild::SetCookieStringInternal is called
  * if network.cookie.ipc.sync is true, perform the existing async IPC request
  * otherwise, use the same code as nsCookieService to decide whether the cookie should be rejected or not. If the cookie is rejected, return without performing any IPC. If the cookie is accepted, it is added to the local hashtable (unless it is httponly) and sent over IPC to the parent.

Part 2:
* ContentParent observes all notifications for cookie changes and forwards them to ContentChild
* ContentChild passes them to the cookie service to ensure that no notifications are missed after the process starts and before the cookie service exists in the child

We should be able to reuse a bunch of the changes from the existing patches:
* lots of part2
* from patch3: the IPDL CookieStruct; CookieServiceChild::RecordDocumentCookie
* lots (maybe all) of part4
* from part5: most of it can be reused, but ContentParent needs to be the observer instead of CookieServiceParent, and all updates (except for httponly cookies) need to be sent to ContentChild

Sorry that I keep changing this project! I think this is the best way to make sure we are finished this work in time and to ensure that we do not regress the behaviour of cookies.
Attachment #8885882 - Flags: feedback?(josh)
(In reply to Josh Matthews [:jdm] from comment #241)
> Ok, so we face a problem. The problem is that my mental model for when
> StartDocumentLoad is called is completely incorrect, which means that the
> model for how we track documents in the parent and child processes is also
> incorrect. As an example, consider this:
> * foo.com/a.html creates an iframe for bar.com/b.html
> * the iframe loads about:blank immediately, and creates a network request
> for bar.com/b.html
> * the parent process requests bar.com/b.html
> * the parent receives a network response for bar.com/b.html and sends the
> cookies for bar.com/b.html to the child
> * the parent sends the network response for bar.com/b.html to the child
> * nsDocument::StartDocumentLoad runs as a result of the child receiving the
> network response
> 
> The model I proposed in comment 100 is based on the idea that the child
> process keeps track of documents that have not yet received a network
> response. The code we have right now does not do that, and that means that
> we will incorrectly deal with cases where we remove an iframe (or close a
> tab) before we receive a network response for its target. Even if we figure
> out a way to implement the original model correctly, I am not confident that
> we can plug all of the holes in time for this to ship and not regress the
> behaviour of cookies in the wild.
> 
> I am worried that we keep finding significant holes in our model and
> implementation. I am especially worried by how little time is left before FF
> 57 becomes the nightly release. I think we should revise our plan and
> implement a less complicated solution that directly addresses the original
> sync IPC performance problem.

Hi Josh,
I have some questions on your suggestions:
1. If the iframe is removed before we receive a network response, in my view, it means this iframe doesn't get/set any cookie.
   Do we have to process the document of this iframe from hash table?
2. If a new content process is created and we send cookies list to  CookieServiceChild's hashtable, in my view,  CookieServiceChild doesn't update any doc to doc hashtable in this timing.
   Does this situation occur the same problem?
3. Would you explain why we have to create CookieServiceParent and CookieServiceChild on ContentParent and ContentChild?
4. In my view, we could fix this problem as below:
   * The first solution:
     i.  If CookieSerivceChild gets the cookies list from CookiesServiceParent but CookieServiceChild doesn't exit any doc in doc hashtable, CookieServiceChild still stores the cookies list on cookie hashtable.
     ii. When CookieServiceChild updates the document to doc hashtable, CookieServiceChild confirm the base domain of the cookie's host = the base domain of the principal.
   * The second solution:
     i.  Parent send ipc msg to child when parent update the doc on ContentParent::About...(), and if child doesn't exit any doc in doc hashtable whose key's base domain same as the doc which is sent from parent.
     ii. If Parent call mDocumentTracker.RemoveTrackedDocument() is true, send ipc msg to notify child about removing doc from doc hashtable.
   Would we use above method to fix this problem?
Flags: needinfo?(josh)
Attachment #8885965 - Attachment is obsolete: true
Flags: needinfo?(josh)
Whoops, didn't mean to clear the needinfo on the previous change.
1. The problem is that when the parent sends the cookies to the child, it is difficult to tell if we actually are supposed to store them or not, because we have no record of the document yet.
2. comment 245 does not include any document hashtable, because it does not try to keep track of which documents and domains are loaded in each content process. All processes receive all cookies.
3. I think this question is based off of the patch that I attached; that patch was a failed attempt to implement comment 242, and is unrelated to the plan in comment 245.
4. The IPC message sent from ContentParent::About...() is received earlier in the content process than the code that causes nsDocument::StartDocumentLoad to run. This ordering is the problem that worries me.

What I like about the plan in comment 245 is that the solution is straightforward - all processes have access to all cookies, and the parent and child processes broadcast updates to the stored cookies. This makes it easier to reason about and test than the previous plan, which reduces the risk involved in fixing the original IPC issue.
(In reply to Josh Matthews [:jdm] from comment #247)
> Whoops, didn't mean to clear the needinfo on the previous change.
> 1. The problem is that when the parent sends the cookies to the child, it is
> difficult to tell if we actually are supposed to store them or not, because
> we have no record of the document yet.
> 2. comment 245 does not include any document hashtable, because it does not
> try to keep track of which documents and domains are loaded in each content
> process. All processes receive all cookies.
> 3. I think this question is based off of the patch that I attached; that
> patch was a failed attempt to implement comment 242, and is unrelated to the
> plan in comment 245.
> 4. The IPC message sent from ContentParent::About...() is received earlier
> in the content process than the code that causes
> nsDocument::StartDocumentLoad to run. This ordering is the problem that
> worries me.
> 
> What I like about the plan in comment 245 is that the solution is
> straightforward - all processes have access to all cookies, and the parent
> and child processes broadcast updates to the stored cookies. This makes it
> easier to reason about and test than the previous plan, which reduces the
> risk involved in fixing the original IPC issue.

Hi Josh,
I still have more questions on your reply:
1. If child have to store all cookies which are sent from parent, why don’t we modify RecvTrackCookieLoad() to remove the confirmation of doc hash table is null or not?
2. Why do we have to move all notifications of cookie-changed to ContentParent?
   Do we need to move cookie hash table to ContentChild?
(In reply to Amy Chung [:Amy] from comment #248)
> (In reply to Josh Matthews [:jdm] from comment #247)
> Hi Josh,
> I still have more questions on your reply:
> 1. If child have to store all cookies which are sent from parent, why don’t
> we modify RecvTrackCookieLoad() to remove the confirmation of doc hash table
> is null or not?

This is an interesting point. In my design, we send a big IPC message once at process creation time, and then small IPC messages later for each cookie update that occurs after that point. If we use the existing patches but remove the document hashtable, we send small-to-medium IPC messages each time a document is loaded, as well as small IPC messages for each cookie update. Let's try your suggestion!

> 2. Why do we have to move all notifications of cookie-changed to
> ContentParent?
>    Do we need to move cookie hash table to ContentChild?

The reason this is necessary in my plan from comment 245 is the following:
1. ContentParent creates a new process and sends the entire cookie database to the new ContentChild
2. a cookie is updated by some network request
3. ContentChild creates the CookieServiceChild actor and populates the child-side cookie hashtable
4. CookieServiceParent actor is created in the parent process and adds itself as an observer for cookie updates

In this situation, the cookie update in step 2 is never sent to the new content process, because the observer is not created until later. On the other hand, if ContentParent is the observer, then it would be sent to the new content process. I think this is a problem in the current patches, too.
Hi Josh,
I would like to confirm the steps of modification by comment 245 as below:
Part3:
1. Remove the confirmation of doc hash table is null or not from RecvTrackCookieLoad().
Part5:
1. Add cookie-changed to Observe of ContentParent.
2. Copy the CookieServiceParent::Observe() to ContentParent::Observe().
3. Remove cookie-changed from CookieServiceParent.
4. Create appropriate functions for getting the notifications which send from ContentParent. Ex: aData is “added”, create AddCookie() function on CookieServiceParent, and CookieServiceParent call SendAddCookie() for sending cookie struct to CookieServiceChild.

Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
(In reply to Amy Chung [:Amy] from comment #250)
> Hi Josh,
> I would like to confirm the steps of modification by comment 245 as below:
> Part3:
> 1. Remove the confirmation of doc hash table is null or not from
> RecvTrackCookieLoad().
> Part5:
> 1. Add cookie-changed to Observe of ContentParent.
> 2. Copy the CookieServiceParent::Observe() to ContentParent::Observe().
> 3. Remove cookie-changed from CookieServiceParent.
> 4. Create appropriate functions for getting the notifications which send
> from ContentParent. Ex: aData is “added”, create AddCookie() function on
> CookieServiceParent, and CookieServiceParent call SendAddCookie() for
> sending cookie struct to CookieServiceChild.
> 
> Would you give me your suggestions?
> Thanks!

The change in part 3 is not enough. We will need to remove all references to the document hashtables in both processes from all patches.
Flags: needinfo?(josh)
Hi Josh,
Does "the reference to the document hashtable" means we will remove GetMatchingDocumentList() from CookieServiceChild and CookieServiceParent?
Base on your reply, we will need to modify the patches as below:
1. Part3- Remove GetMatchingDocumentList() from RecvTrackCookiesLoad().
2. Part3- Remove GetMatchingDocumentList() from PurgeInvisibleCookies().
3. Part5- Remove CheckCookieCanSend().

Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
Yes. If you remove DocumentTracker.h and DocumentTracker.cpp, then any code that no longer compiles will need to be updated. The point of my changes is to remove any attempt to keep track of which documents are loaded in a particular content process.
Flags: needinfo?(josh)
Hi Josh,
I have modified the patch as below:
1. Remove GetMatchingDocumentList() from RecvTrackCookiesLoad().
2. Remove GetMatchingDocumentList() from PurgeInvisibleCookies().

Would you help me to review my patch?
Thanks!
Attachment #8883656 - Attachment is obsolete: true
Attachment #8887155 - Flags: review?(josh)
Hi Josh,
I have moved the cookie-changed to ContentParent.
Would you help me to review my patch?

Thanks!
Attachment #8882611 - Attachment is obsolete: true
Attachment #8887159 - Flags: review?(josh)
Attached patch test case -- part 6 (obsolete) (deleted) — Splinter Review
Hi Josh,
I have modified the test cases as below:
The test case test_browserElement_inproc_CookiesNotThirdParty.html which sets the mozbrowser to true, this setting occurs permission of child which doens't match to the permission of sending from parent.
Base on above reason, I set network.cookie.ipc.sync to true.

Would you help me to review my patch?
Thanks!
Attachment #8885882 - Attachment is obsolete: true
Attachment #8887161 - Flags: review?(josh)
I don't understand why I'm reviewing the changes that you describe. Like I said in comment 253, there is no reason we should track documents at all any more.
Flags: needinfo?(amchung)
Comment on attachment 8887159 [details] [diff] [review]
implementation part5 -- turn CookieServiceParent into an observer  and send updates for cookie changes to child

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

::: dom/ipc/ContentParent.cpp
@@ +2868,5 @@
> +      return NS_ERROR_UNEXPECTED;
> +    }
> +    PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> +    PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
> +    if (!csParent) {

This is the dangerous part. If the cookie service does not exist yet, the content process will miss updates. To avoid this problem, we need to send these updates using PContent instead of PCookieService.

::: netwerk/cookie/CookieServiceParent.cpp
@@ -211,5 @@
>  void
>  CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy)
>  {
> -  // Nothing needed here. Called right before destructor since this is a
> -  // non-refcounted class.

Revert these changes.
Attachment #8887159 - Flags: review?(josh) → review-
(In reply to Josh Matthews [:jdm] from comment #257)
> I don't understand why I'm reviewing the changes that you describe. Like I
> said in comment 253, there is no reason we should track documents at all any
> more.

Hi Josh,
1. If we remove the DocumentTracker,  whether it is expressed child will store all of the cookies from the websites which was visited by user?
2. If we don't process document anymore, Whether the cookies are removed from child receive "cookie-changed" notification every-time?
3. Base on above questions, does this modification use too much memory?
Flags: needinfo?(amchung) → needinfo?(josh)
1. Yes, that is the point of the plan in comment 245.
2. The parent process will need to broadcast all all cookie-changed notifications to each content process.
3. When I look in about:memory, my cookie database reports that it uses 1.74mb of memory. That sounds like an acceptable increase to me.
Flags: needinfo?(josh)
(In reply to Josh Matthews [:jdm] from comment #258)
> Comment on attachment 8887159 [details] [diff] [review]
> implementation part5 -- turn CookieServiceParent into an observer  and send
> updates for cookie changes to child
> 
> Review of attachment 8887159 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/ipc/ContentParent.cpp
> @@ +2868,5 @@
> > +      return NS_ERROR_UNEXPECTED;
> > +    }
> > +    PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> > +    PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
> > +    if (!csParent) {
> 
> This is the dangerous part. If the cookie service does not exist yet, the
> content process will miss updates. To avoid this problem, we need to send
> these updates using PContent instead of PCookieService.
> 
I have a questions of this modification as below:
1. If we have to use PContent, whether it is expressed ContentChild have to exist cookie map?
2. If "cookie-changed" notification is snet from nsCookieService, in my view, "cookie-changed" notifications will occur after network response. And this timing cookie service parent and cookie service child have created from HttpChannelChild::HttpChannelChild().
   Could we still use PCookieServiceParent?
> ::: netwerk/cookie/CookieServiceParent.cpp
> @@ -211,5 @@
> >  void
> >  CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy)
> >  {
> > -  // Nothing needed here. Called right before destructor since this is a
> > -  // non-refcounted class.
> 
> Revert these changes.
Flags: needinfo?(josh)
(In reply to Amy Chung [:Amy] from comment #261)
> (In reply to Josh Matthews [:jdm] from comment #258)
> > Comment on attachment 8887159 [details] [diff] [review]
> > implementation part5 -- turn CookieServiceParent into an observer  and send
> > updates for cookie changes to child
> > 
> > Review of attachment 8887159 [details] [diff] [review]:
> > -----------------------------------------------------------------
> > 
> > ::: dom/ipc/ContentParent.cpp
> > @@ +2868,5 @@
> > > +      return NS_ERROR_UNEXPECTED;
> > > +    }
> > > +    PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
> > > +    PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
> > > +    if (!csParent) {
> > 
> > This is the dangerous part. If the cookie service does not exist yet, the
> > content process will miss updates. To avoid this problem, we need to send
> > these updates using PContent instead of PCookieService.
> > 
> I have a questions of this modification as below:
> 1. If we have to use PContent, whether it is expressed ContentChild have to
> exist cookie map?
> 2. If "cookie-changed" notification is snet from nsCookieService, in my
> view, "cookie-changed" notifications will occur after network response. And
> this timing cookie service parent and cookie service child have created from
> HttpChannelChild::HttpChannelChild().
>    Could we still use PCookieServiceParent?
> > ::: netwerk/cookie/CookieServiceParent.cpp
> > @@ -211,5 @@
> > >  void
> > >  CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy)
> > >  {
> > > -  // Nothing needed here. Called right before destructor since this is a
> > > -  // non-refcounted class.
> > 
> > Revert these changes.

Oh, that's a good point. My plan in comment 245 would miss notifications if the observer was CookieServiceParent; however, if we use your existing patches and send all of the visible cookies as part of each document network response, then it should not be a problem. You can ignore my request to change the observer from CookieServiceParent to ContentParent.
Flags: needinfo?(josh)
Hi Josh,
I have modified the patch as below:
* Merged part 1 and part 3.
Part1:
[nsDocument]
* Remove mAddToTracker from nsDocument
* Remove RemoveDocFromTracker().
* Reserve GetSynthsized(), because when service worker, child have to ask parent to send the cookie list.

[ContentParent]
* Rename UpdateDocumentStatus to UpdateCookieStatus
ContentChild
* Rename UpdateDocumentStatus to UpdateCookieStatus

[PCookieService]
* Remove TrackRequestedDocLod
* Remove TrackDocumentLoad
* Remove UntrackDocument

[CookieServiceChild]
* Modify TrackDocumentLoad to TrackCookieLoad(Implement on part3).
* Remove UntrackDoucment

[CookieServiceParent]
* Modify TrackDocumentLoad to TrackCookieLoad(Implement on part3).
* Remove UntrackDocument

*Remove DocumentTracker

Part3:
[PCookieService.ipdl]
* Create PrepareCookieList on parent.

[CookieServiceChild]
* Remove PurgeInvisibleCookies().
* Implement TrackCookieLoad(ask cookie list from parent).

[CookieServiceParent]
* Implement TrackCookieLoad, and only reserve SendTrackCookiesLoadd().
* Implement RecvPrepareCookieList().

Would you help me to review my patch?
Thanks!
Attachment #8884002 - Attachment is obsolete: true
Attachment #8888232 - Flags: review?(josh)
Hi Josh,
I have modified the patch as below:
* Merged part2 and part4 patch.

Part2:
CookieServiceChild:
* Remove ConfirmDocumentExistsCookies().

Part4:
* didn't modify any code.

Would you help me to review my patch?
Thanks!
Attachment #8881430 - Attachment is obsolete: true
Attachment #8888235 - Flags: review?(josh)
Hi Josh,
I have modified this patch as below:
* Removed CheckCookieCanSend().
* Reverted modification of removing comments on CookieServiceParent.

Would you help me to review my patch?
Thanks!
Attachment #8887159 - Attachment is obsolete: true
Attachment #8888238 - Flags: review?(josh)
Attached patch test case -- part 4 (obsolete) (deleted) — Splinter Review
Hi Josh,
I reserved the test cases as below:
1. Basic test.
2. XHR test.
3. ifrmame test.

And I blocked some tests as below:
[unitest]
1. test_cookie_header.js 
2. test_bug528292.js
3. test_multipart_streamconv.js

[mochitest]
1. test_browserElement_inproc_CookiesNotThirdParty.html
   In this test, iframe set mozbrowser to true, and I found child will create the permission key which is different from the key which is sent from parent.

Would you help me to review my patch?
Thanks!
Attachment #8887161 - Attachment is obsolete: true
Attachment #8887161 - Flags: review?(josh)
Attachment #8888241 - Flags: review?(josh)
Attachment #8879529 - Attachment is obsolete: true
Attachment #8887155 - Attachment is obsolete: true
Attachment #8887155 - Flags: review?(josh)
Attached patch test case -- part 4 (obsolete) (deleted) — Splinter Review
Hi Josh,
I have separated test cases and the modifications of fixing try server error.
Attachment #8888241 - Attachment is obsolete: true
Attachment #8888241 - Flags: review?(josh)
Attachment #8888243 - Flags: review?(josh)
Attached patch fixed try server error - part 5 (obsolete) (deleted) — Splinter Review
Hi Josh,
This patch can fix the try server error:
[unitest]
1. test_cookie_header.js 
2. test_bug528292.js
3. test_multipart_streamconv.js

[mochitest]
1. test_browserElement_inproc_CookiesNotThirdParty.html
   In this test, iframe set mozbrowser to true, and I found child will create the permission key which is different from the key which is sent from parent.

Would you help me to review my patch?
Thanks!
Attachment #8888246 - Flags: review?(josh)
Comment on attachment 8888232 [details] [diff] [review]
implementation part1 -- send and purge matching cookies

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

There are some small changes we can make that will reduce the size of the patch, but otherwise the logic looks fine to me.

::: dom/base/nsDocument.cpp
@@ +2468,5 @@
>    }
>  }
>  
> +bool
> +nsDocument::GetSynthesized() {

Let's call this IsSynthesized.

@@ +2556,5 @@
> +    mChannel->GetIsDocument(&isDocument);
> +    nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
> +    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +        isDocument &&
> +        principal &&

No need to check (or get) the principal anymore.

@@ +2558,5 @@
> +    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
> +        isDocument &&
> +        principal &&
> +        XRE_IsContentProcess()) {
> +      if (GetSynthesized()) {

This can be merged with the outer if expression.

::: dom/base/nsDocument.h
@@ +873,5 @@
>  
>    virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
>    virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
>    virtual void ResolveScheduledSVGPresAttrs() override;
> +  void RemoveDocFromTracker(bool aSendToParent, nsIPrincipal *aPrincipal);

This can be removed.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +87,5 @@
>    gCookieService = nullptr;
>  }
>  
>  void
> +CookieServiceChild::TrackCookieLoad(nsIChannel   *aChannel)

nit: weird spacing.

@@ +89,5 @@
>  
>  void
> +CookieServiceChild::TrackCookieLoad(nsIChannel   *aChannel)
> +{
> +  if (!aChannel) {

Let's remove this; there's no way we should be calling this with a null channel.

::: netwerk/cookie/CookieServiceChild.h
@@ +42,5 @@
> +  void
> +  TrackCookieLoad(nsIChannel *aChannel);
> +
> +  void
> +  UntrackDocument(nsIPrincipal *aPrincipal);

This can be removed.

@@ +72,5 @@
> +                       const OriginAttributes &aAttrs);
> +
> +  void
> +  PurgeInvisibleCookies(const nsCString &aBaseDomain,
> +                        const OriginAttributes &aAttrs);

This can be removed.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +79,5 @@
>  {
>  }
>  
>  void
> +CookieServiceParent::TrackCookieLoad(nsIChannel   *aChannel)

nit: spacing.

@@ +81,5 @@
>  
>  void
> +CookieServiceParent::TrackCookieLoad(nsIChannel   *aChannel)
> +{
> +  if (!aChannel) {

There's no reason this should be called with a null channel, so let's remove this.

@@ +142,5 @@
> +         aCookie->Expiry() > currentTime;
> +}
> +
> +void
> +CookieServiceParent::GetMatchingCookiesList(const nsTArray<nsCookie*> &aFoundCookieList,

Looking closely at nsCookieService::GetCookiesForURI, I don't think we need GetMatchingCookiesList any more. GetCookiesForURI already performs the same checks (including the http-only one). We can move IsCookieExposedToURI to a later patch that actually needs it.

::: netwerk/cookie/PCookieService.ipdl
@@ +102,5 @@
>                                              bool aFromHttp);
>  
> +  async PrepareCookieList(URIParams host,
> +                          bool isForeign,
> +                          nsCString baseDomain,

Since the parent does not use the base domain any more, let's remove it.

@@ +110,5 @@
> +
> +child:
> +  async TrackCookiesLoad(URIParams host,
> +                         CookieStruct[] cookiesList,
> +                         nsCString baseDomain,

Since the base domain can be computed in the child from the URI, let's remove it.
Attachment #8888232 - Flags: review?(josh) → review-
Comment on attachment 8888235 [details] [diff] [review]
implementation part2 -- Created pref id, implemented the function about get cookies string, confirmed the cookie which can save in CookieServiceChild.

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

::: netwerk/cookie/CookieServiceChild.h
@@ +95,2 @@
>                          const OriginAttributes &aAttrs);
> + 

nit: remove this trailing space.

@@ +98,5 @@
> +  CountCookiesFromHashTable(const nsCString &aBaseDomain,
> +                            const OriginAttributes &aOriginAttrs);
> +
> +  bool
> +  RemoveExpiryCookie(const nsCString &aBaseDomain,

This can be removed now.

::: netwerk/cookie/nsCookieService.cpp
@@ +3430,5 @@
> +                              int64_t             aServerTime,
> +                              bool                aFromHttp,
> +                              nsIChannel*         aChannel,
> +                              bool                aLeaveSecureAlone,
> +                              bool&               aSetCookie)

We should initialize aSetCookie to false at the start of this method.
Attachment #8888235 - Flags: review?(josh) → review+
Comment on attachment 8888238 [details] [diff] [review]
implementation part3 -- turn ContentParent into an observer  and send updates for cookie changes to child

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

::: dom/ipc/ContentParent.cpp
@@ +1365,5 @@
>      for (size_t i = 0; i < length; ++i) {
>        obs->AddObserver(this, sObserverTopics[i], false);
>      }
> +    obs->AddObserver(this, "cookie-changed", false);
> +    obs->AddObserver(this, "private-cookie-changed", false);

Let's add these to sObserverTopics instead.

@@ +1770,5 @@
>      }
> +    obs->RemoveObserver(static_cast<nsIObserver*>(this),
> +                        "cookie-changed");
> +    obs->RemoveObserver(static_cast<nsIObserver*>(this),
> +                        "private-cookie-changed");

Let's add these to sObserverTopics instead.

@@ +2873,5 @@
> +    if (!csParent) {
> +      return NS_OK;
> +    }
> +    auto *cs = static_cast<CookieServiceParent*>(csParent);
> +    if (!cs) {

There is no way to get a null pointer here if csParent was non-null, so we can remove this check.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +81,5 @@
>  }
>  
>  void
> +GetInfoFromCookie(nsCookie         *aCookie,
> +                  CookieStruct     &aCookieStruct)

nit: spacing.

@@ +188,5 @@
>  // in a child process.
>  // This matches the usual checks performed on cookies, but also restricts
>  // httponly cookies from being exposed.
>  bool
> +CookieServiceParent::IsCookieExposedToURI(const nsCString& aHostName,

Since this is not called by any other code besides GetMatchingCookiesList, we can remove it entirely.
Attachment #8888238 - Flags: review?(josh) → review+
Comment on attachment 8888243 [details] [diff] [review]
test case -- part 4

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

::: netwerk/test/mochitests/file_1331680.js
@@ +8,5 @@
> +    if (topic == "cookie-changed") {
> +      let cookie = subject.QueryInterface(Ci.nsICookie2);
> +      sendAsyncMessage("cookieName", cookie.name + "=" + cookie.value);
> +    } else {
> +      sendAsyncMessage("docTableClear", true);

This is not used any more.

@@ +15,5 @@
> +};
> +
> +addMessageListener("createObserver" , function(e) {
> +  Services.obs.addObserver(observer, "cookie-changed");
> +  Services.obs.addObserver(observer, "doc-table-clear");

This is not used any more.

::: netwerk/test/mochitests/mochitest.ini
@@ +4,5 @@
>    partial_content.sjs
>    rel_preconnect.sjs
>    user_agent.sjs
>    user_agent_update.sjs
> +  set_cookie_xhr.sjs

This file is missing.

::: netwerk/test/mochitests/set_cookie_xhr2.sjs
@@ +1,2 @@
> +
> +function handleRequest(request, response)

This file isn't used.

::: netwerk/test/mochitests/test_1331680.html
@@ +3,5 @@
> +<!--
> +https://bugzilla.mozilla.org/show_bug.cgi?id=1331680
> +-->
> +<head>
> +  <title>Test for Bug 1331680</title>

Cookies set in content processes update immediately.

@@ +14,5 @@
> +<div id="content" style="display: none">
> +<script type="application/javascript">
> +
> +SimpleTest.waitForExplicitFinish();
> +SimpleTest.requestFlakyTimeout("Testing an event not happening");

We can remove this.

@@ +21,5 @@
> +
> +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('file_1331680.js'));
> +
> +gScript.addMessageListener("cookieName", confirmCookieName);
> +gScript.addMessageListener("createObserver:return", testSetCookie);

Waiting for a retun value should not be necessary here. We should be able to start running the test immediately.

@@ +43,5 @@
> +      is(name, "cookie2=test3", "An update for the cookie named " + name + " was observed.");
> +    break;
> +    case 4:
> +      is(name, "cookie2=test3", "An update for the cookie named " + name + " was observed.");
> +      window.setTimeout(confirmRemoveAllCookies, 100);

We don't need to wait to check document.cookie. We can finish the test immediately. We should also remove the observer before finishing.

@@ +61,5 @@
> +  var confirmCookieString = COOKIE_NAMES[0] + "=test1; " + COOKIE_NAMES[2] + "=test3";
> +  is(document.cookie, confirmCookieString, "Confirm the cookies string which be get is current.");
> +  for (var i = 0; i < COOKIE_NAMES.length; i++) {
> +    document.cookie = COOKIE_NAMES[i] + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT;";
> +  }

Let's check that document.cookie is empty here.

::: netwerk/test/mochitests/test_1331680_iframe.html
@@ +22,5 @@
> +var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('file_1331680.js'));
> +
> +gScript.addMessageListener("cookieName", confirmCookieName);
> +
> +gScript.sendAsyncMessage('createObserver');

I don't think the observer makes sense for this test. We can call SimpleTest.finish() from confirmCookies instead.

@@ +46,5 @@
> +
> +/* Test iframe
> + * 1. Create three iframes, and one of the iframe will create two cookies.
> + * 2. Confirm the cookies can be proessed from observer.
> + * 3. Delete the iframe which creates cookies "if2_1" and  "if2_2".

We don't need this step any more, since we don't track documents.

@@ +54,5 @@
> +function createIframe(id, src, sandbox_flags) {
> +  return new Promise(resolve => {
> +    var ifr = document.createElement("iframe");
> +    ifr.id   = id;
> +    ifr.src  = src;

nit: weird spacing.

@@ +61,5 @@
> +    document.body.appendChild(ifr);
> +  });
> +};
> +
> +function confrimCookies(id) {

confirmCookies

@@ +64,5 @@
> +
> +function confrimCookies(id) {
> +  is(document.cookie, "if2_1=if2_val1; if2_2=if2_val2", "Confirm the cookies can get after iframe was deleted");
> +  var new_ifr = document.getElementById(id);
> +  var innerDoc = (new_ifr.contentDocument) ? new_ifr.contentDocument : new_ifr.contentWindow.document;

We can use contentDocument; there is not need to check for it.

@@ +70,5 @@
> +  document.cookie = IFRAME_COOKIE_NAMES[1] + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +  document.cookie = IFRAME_COOKIE_NAMES[2] + "=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +}
> +
> +function deleteIframes(id) {

We can remove this.

@@ +83,5 @@
> +Promise.resolve()
> +  .then(_ => createIframe(ID[0], "file_iframe_allow_scripts.html", "allow-scripts"))
> +  .then(_ => createIframe(ID[1], "file_iframe_allow_same_origin.html", "allow-scripts allow-same-origin"))
> +  .then(_ => createIframe(ID[2], "about:blank", "allow-scripts allow-same-origin"))
> +  .then(_ => deleteIframes(ID[1]))

We can remove the deletion step.

::: netwerk/test/mochitests/test_1331680_xhr.html
@@ +2,5 @@
> +<html>
> +<!--
> +-->
> +<head>
> +  <title>Test for XHR Method to set cookie</title>

Cookie changes from XHR requests are observed in content processes

@@ +23,5 @@
> +  testsNum++;
> +  switch(testsNum) {
> +    case 1:
> +      is(name, "xhr1=xhr_val1", "The cookie which names " + name + " is update to db");
> +    break;

nit: indent the break statements by two spaces.

@@ +30,5 @@
> +      for (var i = 0; i < XHR_COOKIE_NAMES.length; i++) {
> +        document.cookie = XHR_COOKIE_NAMES[i] + "=; path=/; expires=Thu, 01-Jan-1970 00:00:01 GMT";
> +      }
> +    break;
> +    case 3:

Let's send a message to remove the observer here as well.
Attachment #8888243 - Flags: review?(josh) → review-
Comment on attachment 8888246 [details] [diff] [review]
fixed try server error - part 5

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

::: dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
@@ +47,5 @@
>  
>  // Disable third-party cookies for this test.
>  addEventListener('testready', function() {
> +  SpecialPowers.pushPrefEnv({'set': [['network.cookie.cookieBehavior', 1],
> +                                     ['network.cookie.ipc.sync', true]]}, runTest);

I am worried about why this is necessary. Could you explain in more detail why the test fails without this change?
Attachment #8888246 - Flags: review?(josh) → review-
(In reply to Josh Matthews [:jdm] from comment #273)
> Comment on attachment 8888246 [details] [diff] [review]
> fixed try server error - part 5
> 
> Review of attachment 8888246 [details] [diff] [review]:
> -----------------------------------------------------------------
> 
> ::: dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
> @@ +47,5 @@
> >  
> >  // Disable third-party cookies for this test.
> >  addEventListener('testready', function() {
> > +  SpecialPowers.pushPrefEnv({'set': [['network.cookie.cookieBehavior', 1],
> > +                                     ['network.cookie.ipc.sync', true]]}, runTest);
> 
> I am worried about why this is necessary. Could you explain in more detail
> why the test fails without this change?

In CookieServiceChild::SetCookieStringInternal(), we call nsCookieService::CheckPrefs() for confirming the permission. 
And in nsCookieService::CheckPrefs, PermissionService->CanAccess() will confirm  whether or not the permission key exists in child.
I found if the iframe set mozbrowser to true, in this test, parent sent permission whose value is "http://example.com/~Browser^1" key to child.
When the iframe set some cookies by doucment.cookie, PermissionManager creates a permission key whose value is "http://example.com".
Base on above reason, the tab shows crash information because PermissionManager occurs the assertion.
And if I comment "iframe.setAttribute('mozbrowser', 'true');", the PermissionManager doesn't trigger the assertion.
In my view, maybe if mozbrowser set to true, the permission will be change in child.
I will open a follow-up bug to fix this problem, and I think we could set network.cookie.ipc.sync to true first for avoiding test fail.
Sorry, I forgot to ni you on comment 274.
Flags: needinfo?(josh)
What is the backtrace for the assertion that you see?
(In reply to Josh Matthews [:jdm] from comment #276)
> What is the backtrace for the assertion that you see?

In CommonTestPermissionInternal(), this function set assertion when PermissionAvaliable() return false, and mPermissionKeyPromiseMap can't get any permission key on child in browserElement_CookiesNotThirdParty.js.
Hi Josh,
I have modified the patch as below:
[nsDocument]
1. GetSynthesized=>IsSynthezied
2. Removed RemoveDocFromTracker from nsDocument.h.

[CookieServiceChild]
1. Removed the confirmation of aChannel from TrackCookieLoad().
2. Removed UntrackDocument from CookieServiceChild.h.
3. Removed PurgeInvisibleCookies from CookieServiceChild.h

[CookieServiceParent]
1. Removed the confirmation of aChannel from TrackCookieLoad().
2. Modified GetMatchingCookiesList to TransMatchingCookiesList which transfer nsCookie list to CookieStruct list.
3. Removed IsCookieExposedToURI and I found this function doesn't need to move to later patch. When CookieServiceParent receives the notification of "cookie-changed" and has to send cookies list or cookie to child, CookieServiceParent just need to confirm the cookie's IsHttpOnly() is false.
And in the notification "cookie-changed", it doesn't include nsIURI information.
Base on above reason, I removed the IsCookieExposedToURI().

[PCookiseService]
1. Removed basedomain from PrepareCookieList.
2. Removed basedomain from TrackCookiesLoad.

Would you help me to review my patch?
Attachment #8888232 - Attachment is obsolete: true
Attachment #8888800 - Flags: review?(josh)
Attached patch test case -- part 4 (obsolete) (deleted) — Splinter Review
Hi Josh,
[file_1331680.js]
I have modified this patch as below:
1. Removed the notification of "doc-table-clear".
2. Added removeObserver().

[test_1331680.html]
1. Sent removeObserver() to file_1331680.js.
2. Moved the confirmation of removing all cookies to testSetCookie().
3. Added a listener for getting the msg from file_1331680.js when observer is removed.

[test_1331680_iframe.html]
1. Canceled to create observer.
2. Removed deleteIframes.
3. Removed the confirmation of new_ifr.contentDocument is null or not.

[test_1331680_xhr.html]
1. Sent removeObserver() to file_1331680.js.

Would you help me to review my patch?
Thanks!
Attachment #8888243 - Attachment is obsolete: true
Attachment #8888814 - Flags: review?(josh)
Comment on attachment 8888800 [details] [diff] [review]
implementation part1 -- send and purge matching cookies

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

If we can use the cookie's host as the argument to GetBaseDomainFromHost in RecordDocumentCookie, then we should be able to remove the URIs from the IPDL messages. Otherwise, this looks fine.

::: netwerk/cookie/CookieServiceChild.cpp
@@ +101,5 @@
> +  if (loadInfo) {
> +    attrs = loadInfo->GetOriginAttributes();
> +  }
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);

We don't need this URI if we remove it from the PrepareCookieList message.

@@ +106,5 @@
> +  SendPrepareCookieList(uriParams, isForeign, attrs);
> +}
> +
> +mozilla::ipc::IPCResult
> +CookieServiceChild::RecvTrackCookiesLoad(const URIParams        &aHost,

We don't need the URI argument if we remove it from RecordDocumentCookie.

@@ +167,5 @@
> +  nsAutoCString hostFromURI;
> +  nsAutoCString baseDomain;
> +  aHostURI->GetAsciiHost(hostFromURI);
> +  nsCookieService::
> +    GetBaseDomainFromHost(mTLDService, hostFromURI, baseDomain);

We should be able to use the cookie's host here.

::: netwerk/cookie/CookieServiceParent.cpp
@@ +91,5 @@
> +    attrs = loadInfo->GetOriginAttributes();
> +  }
> +
> +  URIParams uriParams;
> +  SerializeURI(uri, uriParams);

This can be removed if we don't need the URI any more.

@@ +106,5 @@
> +  Unused << SendTrackCookiesLoad(uriParams, matchingCookiesList, attrs);
> +}
> +
> +void
> +CookieServiceParent::TransMatchingCookiesList(const nsTArray<nsCookie*> &aFoundCookieList,

Let's call this SerialializeCookieList.

@@ +108,5 @@
> +
> +void
> +CookieServiceParent::TransMatchingCookiesList(const nsTArray<nsCookie*> &aFoundCookieList,
> +                                              nsTArray<CookieStruct>    &aCookiesList,
> +                                              nsIURI                    *aHostURI)

This URI is not necessary any more.

::: netwerk/cookie/PCookieService.ipdl
@@ +108,4 @@
>    async __delete__();
> +
> +child:
> +  async TrackCookiesLoad(URIParams host,

I believe the URI arguments can be removed from these messages.
Attachment #8888800 - Flags: review?(josh) → review+
Comment on attachment 8888814 [details] [diff] [review]
test case -- part 4

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

::: netwerk/test/mochitests/set_cookie_xhr.sjs
@@ +3,5 @@
> +{
> +  var queryString = request.queryString;
> +  // avoid confusing cache behaviors
> +  response.setHeader("Cache-Control", "no-cache", false);
> +  response.setHeader("Content-Type", "text/html", false);

We don't need lines 5-7.
Attachment #8888814 - Flags: review?(josh) → review+
Comment on attachment 8888246 [details] [diff] [review]
fixed try server error - part 5

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

Ok, so the cookie setting permission check has been broken forever when mozbrowser is involved, but calling PermissionAvaliable from the content process actually exposes that brokenness. I am fine with setting the preference for that test in order to avoid the assertion. We're not changing any of the behaviour involved here.
Attachment #8888246 - Flags: review- → review+
Flags: needinfo?(josh)
Comment on attachment 8888238 [details] [diff] [review]
implementation part3 -- turn ContentParent into an observer  and send updates for cookie changes to child

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

::: dom/ipc/ContentParent.cpp
@@ +2864,5 @@
>      Unused << SendUpdateRequestedLocales(requestedLocales);
>    }
> +  else if (!strcmp(aTopic, "cookie-changed") ||
> +           !strcmp(aTopic, "private-cookie-changed")) {
> +    if (!aData || !aSubject) {

While running browser_bug1108547.js I discovered a problem - nsCookieService::RemoveAll passes null as the aSubject argument, so we never send the RemoveAll message to the child.
The other problem preventing browser_bug1108547.js from passing was that the code in nsCookieService that observes the last-pb-context-exited notification did not work with these patches. It is responsible for clearing the private cookie database, but it currently does that by recreating the database object, which does not generate the observer notifications that ContentParent observes. Replacing `mPrivateDBState = new DBState();` with the following snippet worked for me:
>    mozilla::OriginAttributesPattern pattern;
>    pattern.mPrivateBrowsingId.Construct(1);
>    RemoveCookiesWithOriginAttributes(pattern, EmptyCString());
(In reply to Josh Matthews [:jdm] from comment #284)
> The other problem preventing browser_bug1108547.js from passing was that the
> code in nsCookieService that observes the last-pb-context-exited
> notification did not work with these patches. It is responsible for clearing
> the private cookie database, but it currently does that by recreating the
> database object, which does not generate the observer notifications that
> ContentParent observes. Replacing `mPrivateDBState = new DBState();` with
> the following snippet worked for me:
> >    mozilla::OriginAttributesPattern pattern;
> >    pattern.mPrivateBrowsingId.Construct(1);
> >    RemoveCookiesWithOriginAttributes(pattern, EmptyCString());

Hi Josh,
I have some question on your suggestions as below:
1. Dose this snippet has to implement on CookieSerivceChild?
2. https://dxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsCookieService.cpp#4926
   RemoveCookiesWithOriginAttributes() seems only process on parent.
3. If I could follow the code from https://dxr.mozilla.org/mozilla-central/source/netwerk/cookie/nsCookieService.cpp#4963-4993 to process on mCookiesMap which in the CookieServiceChild.
   The problem is parent doesn't get any information about base domain from "last-pb-context-exited" notification.
   Does any other method implement private browsing on child process?
Flags: needinfo?(josh)
My comment was describing a problem in the code of nsCookieService::Observe.
Flags: needinfo?(josh)
(In reply to Josh Matthews [:jdm] from comment #286)
> My comment was describing a problem in the code of nsCookieService::Observe.

Hi Josh,
I have modified from your suggestions and tested browser_bug1108547.js.
This test can pass on my patches.
Thanks!
I noticed in https://hg.mozilla.org/try/rev/2311e48737515a101157f7b58c67f0d88407db9c that the patch still contains changes that make us use sync IPC when private browsing is in effect. Please revert that.
(In reply to Josh Matthews [:jdm] from comment #288)
> I noticed in
> https://hg.mozilla.org/try/rev/2311e48737515a101157f7b58c67f0d88407db9c that
> the patch still contains changes that make us use sync IPC when private
> browsing is in effect. Please revert that.

Ok, I have removed this modifications.
Thanks!
Hi Josh,
I found the iframe test possibly occurred error on OS X, because the iframe maybe got the cookies string as "if2_2=if2_val2; if2_1=if2_val1".
In my view, we could modify the iframe to set one cookie on file_iframe_allow_same_origin.html.
Would you give me your suggestions?
Thanks!
Flags: needinfo?(josh)
The cookie string getter sorts the cookies using CompareCookiesForSending before creating the string. If they are not being sorted in the expected order, we need to figure out why that is the case.
Flags: needinfo?(josh)
Specifically, in this case I suspect that we will need to investigate the creation time of the cookies, since that is used to determine the sort order of the cookies.
Hi Josh,
I found the flow of test_1331680_iframe possibly occurred error as below:
1. Created if2_1.
2. Created if2_2.
3. iframe got the cookie=> if2_1=if2_val1; if2_2=if2_val2.
4. CookieServiceChild received "cookie-changed" notification, then updated if2_1.
   The creation time of if2_1 was changed.
5. test_1331680_iframe got the cookie.=> if2_2=if2_val2; if2_1=if2_val1.
6. CookieServiceChild received "cookie-changed" notification, then updated if2_2.
   The creation time of if2_2 was changed.

Do we have to reserve the creation time when CookieServiceChild receive "cookie-changed" notification?
Flags: needinfo?(josh)
Yes, I think that solution makes the most sense. In RecordDocumentCookie, if we find an existing cookie we can replace the creation time of the new cookie with the time from the existing one before we remove it.
Flags: needinfo?(josh)
Hi Josh,
I have modified the problem of creation time.
Would you help me to review this patch?
Thanks!
Attachment #8888238 - Attachment is obsolete: true
Attachment #8892268 - Flags: review?(josh)
Attachment #8892268 - Attachment description: bug1331680-part3.patch → implementation part3 -- turn ContentParent into an observer and send updates for cookie changes to child
Comment on attachment 8892268 [details] [diff] [review]
implementation part3 -- turn ContentParent into an observer and send updates for cookie changes to child

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

::: netwerk/cookie/CookieServiceChild.cpp
@@ +388,5 @@
> +          cookie->IsHttpOnly() == aCookie->IsHttpOnly() &&
> +          // We don't want to perform this optimization if the cookie is
> +          // considered stale, since in this case we would need to update the
> +          // database.
> +          !cookie->IsStale()) {

The comment does not make sense here, since there is no database access in the child process. We don't need to check if the cookie is stale or not for this, so let's remove that.
Attachment #8892268 - Flags: review?(josh) → review+
Attachment #8888800 - Attachment is obsolete: true
Attachment #8893283 - Flags: review+
Attachment #8893283 - Attachment is obsolete: true
Attachment #8893287 - Flags: review+
Blocks: 1381382
Attachment #8879532 - Attachment is obsolete: true
Attachment #8893303 - Flags: review+
Attachment #8893287 - Attachment is obsolete: true
Attachment #8893304 - Flags: review+
Attachment #8893305 - Flags: review+
Attachment #8893284 - Attachment is obsolete: true
Attached patch test case -- part 4 (obsolete) (deleted) — Splinter Review
Attachment #8888814 - Attachment is obsolete: true
Attachment #8893309 - Flags: review+
Attached patch fixed try server error - part 5 (obsolete) (deleted) — Splinter Review
Attachment #8888246 - Attachment is obsolete: true
Attachment #8893310 - Flags: review+
Attachment #8893304 - Attachment is obsolete: true
Attachment #8893344 - Flags: review+
Attached patch test case -- part 4 (deleted) — Splinter Review
Attachment #8893309 - Attachment is obsolete: true
Attachment #8893345 - Flags: review+
Attached patch fixed try server error - part 5 (deleted) — Splinter Review
Attachment #8893310 - Attachment is obsolete: true
Attachment #8893346 - Flags: review+
Pushed by josh@joshmatthews.net:
https://hg.mozilla.org/integration/mozilla-inbound/rev/cbda2eff9f93
Part 0: Extract nsCookieKey. r=jdm
https://hg.mozilla.org/integration/mozilla-inbound/rev/5a87c9d62dc4
Part 1: Send required cookies to the content process on demand. r=jdm
https://hg.mozilla.org/integration/mozilla-inbound/rev/84714cef1f52
Part 2: Use local cookie hashtable in content process, and perform cookie permission checks synchronously. r=jdm
https://hg.mozilla.org/integration/mozilla-inbound/rev/384a25a0a2aa
Part 3: Broadcast cookie changes to all content processes. r=jdm
https://hg.mozilla.org/integration/mozilla-inbound/rev/b651936901f8
Part 4: Tests for document.cookie behaving synchronously and reflecting changes from the parent process. r=jdm
Keywords: checkin-needed
I folded part 4 and part 5 together, and added better commit messages for all of the patches. Thanks for all your work here, Amy!
Pushed by josh@joshmatthews.net:
https://hg.mozilla.org/integration/mozilla-inbound/rev/099cfd49075f
Bustage fix for nsIURI changes. a=bustage
(In reply to Josh Matthews [:jdm] from comment #311)
> I folded part 4 and part 5 together, and added better commit messages for
> all of the patches. Thanks for all your work here, Amy!

Thanks for your help and review, I learned a lot on this bug!
Hope this patches could improve the performance of cookie in the feature.
Having watched this bug from the sidelines, and having seen how the sync cookie IPC messages can hose performance, I'm so stoked to have this landed.

Great job, Amy!
(In reply to Mike Conley (:mconley) - Buried in needinfo / review backlog, eating my way out from comment #314)
> I'm so stoked to have this landed.
> 
> Great job, Amy!

Seconded! A thorny and important problem, tackled with tenacity.
Agreed. Great job!

Also, sorry if it's slightly off topic, but someone on IRC mentioned that Chrome may in fact have sync cookies. Can anyone confirm?

Doesn't need to be a big deal, but if that's the case this is a nice data point.
Thanks so much for doing this! Turned out to be one heck of a bug ^.^.
Thanks for all of your hard work on this Amy, and for all of your hard work in helping with the reviews, Josh!

Is there a follow-up bug on file for turning the network.cookie.ipc.sync pref on?
No. Enabling that preference means that we revert to using sync IPC for all cookie access from the content process. It is the kill switch in case we find that we've totally broken cookies right before release.
(In reply to Josh Matthews [:jdm] from comment #319)
> No. Enabling that preference means that we revert to using sync IPC for all
> cookie access from the content process. It is the kill switch in case we
> find that we've totally broken cookies right before release.

Oh, I understand now, I got it backwards originally!  :-)
Hooray! Very good and pressured work here.
Good job, Amy!
Tucked into comment 306 are some performance results from this work:

Time spent in a gecko profile on cookie code on CNN.com went from 314ms to .1168ms.  On Facebook it went from 193ms to .085ms.
In other words, document.cookie access is now *2000 times as fast* as with our old synchronous IPC code.  

My understanding is that Chromium is still using some sort of sync IPC here, so a performance comparison would be very interesting...
This turned out to be a bug of epic proportions, in so many ways! Many congrats and thanks to Amy for pushing this over the finish line and Josh for his tireless reviewing, mentoring and coaching efforts.
* takes hat off and makes a slight bow *
Depends on: 1396676
Depends on: 1425031
Depends on: 1443662
Depends on: 1462402
Performance Impact: --- → P1
Whiteboard: [necko-active][platform-rel-Linkedin][qf:p1][necko-quantum] → [necko-active][platform-rel-Linkedin][necko-quantum]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: