Skip to content

script: initial CookieStore implementation - #37968

Merged
jdm merged 11 commits into
servo:mainfrom
sebsebmc:cookie-store-api
Aug 21, 2025
Merged

jdm merged 11 commits into
servo:mainfrom
sebsebmc:cookie-store-api

Conversation

@sebsebmc

@sebsebmc sebsebmc commented Jul 9, 2025 •

Copy link
Copy Markdown
Contributor

This is a first draft at implementing the required infrastructure for CookieStore, which requires setting up IPC between script and the resource thread to allow for async/"in parallel" handling of cookie changes that have a promise API.

Cookie Store also will need to receive change events when cookies for a url are changed so the architecture needs to support that.

Expect this PR to be reworked once the architecture becomes more settled, cookie change events will be implemented in follow up PRs

Testing: WPT tests exist for this API
Part of #37674

Comment thread components/shared/net/lib.rs Resolved
DeleteCookies(ServoUrl),
DeleteCookie(ServoUrl, String),
DeleteCookieAsync(CookieStoreId, CookieRequestId, ServoUrl, String),
NewCookieListener(CookieStoreId, IpcSender<CookieAsyncResponse>, ServoUrl),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For lifecycle management I believe I will also need a RemoveCookieListener message for when a global navigates to a new page. The spec makes clear that CookieStore's relevant URL is the global's creation_url.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that sounds like a good idea.

@gterzian gterzian Aug 17, 2025 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you currently sending that message? I think it could be done on drop of the global.

EDIT: drop of the cookiestore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I noticed that I forgot to wire this up yesterday. I'll look at adding this to the drop

@gterzian gterzian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great so far, thank you.

How do you want to scope this PR?

Comment thread components/script/dom/cookiestore.rs Resolved
Comment thread components/script/dom/cookiestore.rs Resolved
Comment thread components/script/dom/cookiestore.rs Resolved
Comment thread components/script/dom/cookiestore.rs Outdated
// 1. Perform the steps defined in Cookies § Retrieval Model to compute the "cookie-string from a given cookie
// store" with url as request-uri. The cookie-string itself is ignored, but the intermediate cookie-list is
// used in subsequent steps.
// For the purposes of the steps, the cookie-string is being generated for a "non-HTTP" API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also belongs in resource threads, and instead I would just document the method with a note on sending a message to run the "in-parallel" steps.

Comment thread components/script/dom/cookiestore.rs Resolved
Comment thread components/script/dom/window.rs Resolved
Comment thread components/script/dom/window.rs Outdated
.or_init(|| Storage::new(self, StorageType::Local, CanGc::note()))
}

// https://wicg.github.io/cookie-store/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct link is https://wicg.github.io/cookie-store/#dom-window-cookiestore(and format is wrong also).

Comment thread components/shared/net/lib.rs Resolved
DeleteCookies(ServoUrl),
DeleteCookie(ServoUrl, String),
DeleteCookieAsync(CookieStoreId, CookieRequestId, ServoUrl, String),
NewCookieListener(CookieStoreId, IpcSender<CookieAsyncResponse>, ServoUrl),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that sounds like a good idea.

Comment thread components/shared/net/lib.rs Resolved
@sebsebmc

Copy link
Copy Markdown
Contributor Author

How do you want to scope this PR?

I think the goal for this PR is going to be setting up the infrastructure needed to be able to do the most minimal Set-Get-Delete implementation. The change event listener is definitely going to be in a follow up

@gterzian

Copy link
Copy Markdown
Member

I think the goal for this PR is

Sounds good.

@sebsebmc sebsebmc changed the title wip: cookiestore: setup basics script: initial CookieStore implementation Aug 1, 2025
@sebsebmc

Copy link
Copy Markdown
Contributor Author

@gterzian @jdm I don't want to make this PR too much larger, I think this is ready for the next round of review.

@gterzian gterzian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial commend; I'll take another look later today.

Comment thread components/script/dom/window.rs Outdated
}

// https://cookiestore.spec.whatwg.org/#Window
fn CookieStore(&self) -> DomRoot<CookieStore> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you can pass a can_gc argument using Bindings.conf.

self.store_id,
global.creation_url().clone(),
options.name.to_string(),
));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere you could log an error if the send fails. In theory it never should.

creation_url.clone(),
Serde(cookie.build()),
NonHTTP,
));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it fails you probably want to. remove the inflight promise as well, and reject it with an error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yeah probably just going to make a small helper for this then. I wish there was a nice way to make this a type/lifetime error somehow.

p.reject_error(Error::Security, can_gc);
return p;
}
// 4. Let url be settings’s creation URL.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I always add a line between the last line of code and the next comment.

fn cookie_to_list_item(cookie: Cookie) -> CookieListItem {
CookieListItem {
// Let domain be the result of running UTF-8 decode without BOM on cookie’s domain.
domain: cookie

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add a TODO to actually call into UTF-8 decode without BOM

Comment thread components/net/resource_thread.rs Outdated
);
return;
}
let _ = sender.unwrap().send(CookieAsyncResponse { data });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nicer to do a Let Some() = else

@gterzian gterzian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, thank you. I might spend some time looking at the spec again and into some things mentioned below, in the meantime let's address the current comments.

Comment thread components/script/dom/cookiestore.rs Outdated
.collect_vec(),
CanGc::note());
},
_ => {promise.resolve_native(&(), CanGc::note());}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better yo be explicit here.

}

struct CookieListener {
task_source: SendableTaskSource,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's document which task source this is, and add a link to the spec is such an appropriate link exists

DeleteCookies(ServoUrl),
DeleteCookie(ServoUrl, String),
DeleteCookieAsync(CookieStoreId, CookieRequestId, ServoUrl, String),
NewCookieListener(CookieStoreId, IpcSender<CookieAsyncResponse>, ServoUrl),

@gterzian gterzian Aug 17, 2025 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you currently sending that message? I think it could be done on drop of the global.

EDIT: drop of the cookiestore.

.send(CoreResourceMsg::GetCookieDataForUrlAsync(
self.store_id,
creation_url.clone(),
Some(name.to_string()),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I'm not quite sure how to make this work with the decoding that happens later in parallel. Should we do the decoding before turning it into a string, which means decoding it here, or can we do it later on the string in parallel. But rust strings are already utf8 I believe. Needs some looking into. Any relevant tests failing at this point?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the only encoding/decoding tests that exist are checking if we strip the BOM from the name and we currently pass the test. (We don't strip it)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's some UTF8 encoding checks in the tests for the cookie change events, which we haven't implemented at all.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I guess we can then leave it as it, and revisit when the others parts are implemented.

@gterzian gterzian left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with one nit; thank you.

Comment thread components/script/dom/cookiestore.rs Outdated
Some(name.to_string()),
));
if res.is_err() {
self.in_flight.borrow_mut().pop_back();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would it's simpler to only push it back if the send is ok.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I do like that a bit more, since now the pattern is consistent about pushing back if we send a message. Now anytime we send a message we always have a pattern to check to make sure we also stored a promise.

Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
Signed-off-by: Sebastian C <sebsebmc@gmail.com>
@sebsebmc
sebsebmc marked this pull request as ready for review August 20, 2025 18:38
@gterzian
gterzian added this pull request to the merge queue Aug 20, 2025
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 20, 2025
@sebsebmc

Copy link
Copy Markdown
Contributor Author

Not sure what happened there, is that just a flake on the MacOS runner?

@jdm

jdm commented Aug 21, 2025

Copy link
Copy Markdown
Member
  cargo:warning=fatal error: /Applications/Xcode_16.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: can't write to output file (Input/output error)
  cargo:warning=/Applications/Xcode_16.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ar: internal ranlib command failed

Wild. Never seen that before! Let's try again.

@jdm
jdm added this pull request to the merge queue Aug 21, 2025
Merged via the queue into servo:main with commit b869b7e Aug 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants