Skip to content
Commit b10baef5 authored by Stefano Crocco's avatar Stefano Crocco
Browse files

Synchronize cookies between QWebEngine and KCookieServer

Summary:
KHTML part and KWebKitPart both use KCookieServer to manage cookies,
while WebEnginePart doesn't. This has at least two consequences:
- cookie settings made using the ""Cookie" page in Konqueror settings dialog or
  in SystemSettings aren't honoured
- you can't use KIO to download files from many sites (in particular, sites
  requiring authentication) because the cookies received by the browser and
  those used by KIO are different.

Qt WebEngine provide a class, `QWebEngineCookieStore` to allow synchronizing
cookies between Qt WebEngine and other systems. Unfortunately, the API provided
by this class is quite different from the API used by KCookieServer.

I added a class, `WebEnginePartCookieJar` to manage the synchronization of cookies
between Qt WebEngine and `KCookieServer`, then added a static variable of this
class to `WebEnginePart`. This static variable is filled by `WebEnginePart`'s
constructor the first time it's created.

`WebEnginePartCookieJar` does the following:
- disables persistent cookies in `QWebEngineProfile`. This way, cookies will
  only be stored on disk by `KCookieStore`
- on creation, loads cookies from `KCookieServer` and adds them to
  `QWebEngineCookieStore`
- in response to the `QWebEngineCookieStore::cookiesAdded` signal, it adds the
  cookie to `KCookieServer` according to the Cookies KCM settings
- in response to the `QWebEngineCookieStore::cookiesRemoved` signal, it removes
  the cookie from `KCookieServer`
- in response to the `QApplication::lastWindowClosed` signal, it calls
  `KCookieServer::deleteSessionCookies` for each window (having store each
  window's id earlier).

Some functionality is missing, however, because of `KCookieServer`'s API:
- there's no way to know when a cookie is added to `KCookieServer` or removed
  from it, so (aside from loading the coockies from `KCookieServer` when the
  `WebEnginePartCookieJar` is created) the synchronization is only one-way: from
  the `QWebEngineCookieStore` to `KCookieServer`. This should not be an issue
  most of the times, but it also means that, if the user deletes a cookie from
  the Cookies KCM while Konqueror is running, this change won't be propagated to
  `QWebEngineCookieStore` until Konqueror is restarted
- when the cookie policy is set to "Ask", `KCookieServer` doesn't provide a way
  to find out what the user chose. As a workaround, `WebEnginePartCookieJar`
  checks whether the cookie exists in `KCookieStore` and removes it from the
  `QWebEngineCookieStore` if it doesn't. However, there's no way to distinguish
  between an "Accept" and "AcceptUntilSession" answer.

Some tricks have been needed to make all this work. In particular:
- `QWebEngineCookieStore` only provides the origin URL to the function set as
  cookie filter; however, `QWebEngineCookieStore::setCookieFilter` only exists
  since Qt 5.11, so on earlier Qt versions we can't determine whether a cookie
  is a cross origin cookie or not and honour the corresponding setting in the
  Cookie KCM
- `KCookieServer::addCookies` requires an URL as parameter, but
  `QWebEngineCookieStore::cookiesAdded` doesn't provide one (I thought the URL
  passed to the cookie filter could be used, but there's no warranty that the
  order the requests are passed to the filter is the same in which cookies are
  added). Looking at the source code for `KCookieStore` and `KCookieJar`,
  however, it seems that they use this parameter mainly to determine the domain
  if it isn't specified in the cookie. Since the `QNetworkCookie` given by
  `KCookieServer::addCookies` seems to always have a non-empty domain, the URL
  is created using that domain as host
- since `QWebEngineCookieStore` doesn't provide a way to find out which page
  made the request resulting in a cookie, there's no way to be sure of the
  window ID to pass to `KCookieServer::addCookies`. `WebEnginePartCookieJar`
  assumes that the window is the active one (this, of course, is only a problem
  when Konqueror has more than one window open)
- there's an issue with expiration times. `KCookieJar` reads expiration times
  using `QDateTime::fromString` which, it seems, always interpret that time as
  local time even if expiration times in cookies is in GMT; this can lead to
  cookies to be considered expired when they should not. For example, if my
  local time is GMT +1 and I receive a cookie at 14:00 (local time) which
  expires in half an hour, it's expiration field will be something like
  "13:30:00 GMT"; however `KCookieJar` seems to interpret it as 13:30 local time
  and, since in local time, the time is 14:00, it considers the cookie expired.
  I worked around this issue by manually setting the time zone in the
  `QNetworkCookie` to GMT, but I can't understand why this happens (it also
  happens when calling `KCookieServer::addCookie` from the command line using
  `qdbus`, but it doesn't happen when using KHTMLPart).

Test Plan: Open the "Cookies" page in SystemSettings, remove all cookies, use Konqueror to navigate web pages using cookies and note which cookies have appeared in the list; repeat the operation using KHTML and check that the cookies are the same.

Reviewers: dfaure

Reviewed By: dfaure

Differential Revision: https://phabricator.kde.org/D14379
parent 9878ba8f
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment