Synchronize cookies between QWebEngine and KCookieServer

Authored by stefanocrocco on Jun 27 2018, 3:16 PM.

Description

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

Details

Committed
stefanocroccoAug 7 2018, 7:33 AM
Reviewer
dfaure
Differential Revision
D14379: Synchronize cookies between QWebEngine and KCookieServer
Parents
R226:9878ba8ffef9: Fix issue with info protocol and refresh
Branches
Unknown
Tags
Unknown