Diff
Modified: trunk/Source/WebCore/ChangeLog (134954 => 134955)
--- trunk/Source/WebCore/ChangeLog 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/ChangeLog 2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,3 +1,57 @@
+2012-11-16 Martin Robinson <mrobin...@igalia.com>
+
+ [GTK] Move CredentialBackingStore usage from GtkAuthenticationDialog to ResourceHandleSoup
+ https://bugs.webkit.org/show_bug.cgi?id=101840
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Make ResourceHandleSoup aware of per-session CredentialStorage and persistent CredentialStorage.
+ Persistent credential storage interaction is moved from GtkAuthenticationDialog, so that it can
+ be used whether or not GtkAuthenticationDialog is used or not. We try to properly handle redirects
+ in the manner that the CFNet backend does.
+
+ No new tests. There are tests for this behavior, but they cannot be activated until we finish
+ plumbing this through to the API layer. Once that patch lands, the tests will be turned on.
+
+ * platform/gtk/GtkAuthenticationDialog.cpp: No longer store credentials into the persistent
+ storage manually, instead rely on ResourceHandleSoup. Also, we no longer get proposed credentials
+ from the persistent storage here as well. They are pre-loaded by the ResourceHanndle.
+ * platform/gtk/GtkAuthenticationDialog.h: Remove callbacks and members associated with saving
+ credentials to the persistent credential store.
+ * platform/network/ResourceHandle.h:
+ (ResourceHandle): Add a method which is used to continue asynchronously after looking for
+ proposed credentials in the persistent credential store.
+ * platform/network/ResourceHandleInternal.h: Add a member which tracks persistent credentials to be added once we know
+ an authentication succeeded.
+ * platform/network/gtk/CredentialBackingStore.cpp:
+ (CredentialForChallengeAsyncReadyCallbackData): Added this data structure used for asynchronous access
+ of stored credentials.
+ (WebCore::credentialForChallengeAsyncReadyCallback): Ditto for this callback.
+ (WebCore::CredentialBackingStore::credentialForChallenge): Make this method asynchronous.
+ * platform/network/gtk/CredentialBackingStore.h:
+ (CredentialBackingStore): Update method signatures for for making credentialForChallenge asynchronous.
+ * platform/network/soup/AuthenticationChallenge.h:
+ (WebCore::AuthenticationChallenge::setProposedCredential): Added a setter so that ResourceHandleSoup
+ can set proposed credentials from the persistent credential store.
+ * platform/network/soup/ResourceHandleSoup.cpp:
+ (WebCore::gotHeadersCallback): For GTK+ save any pending credential in the persistent credential storage
+ if the authentication succeeded.
+ (WebCore::applyAuthenticationToRequest): Added this method which generically embeds stored credentials
+ in the request URI. This is the method that Soup uses to override any soup-stored session credential.
+ (WebCore::restartedCallback): Strip credentials for requests that span a security origin. Handle
+ authenticating requests from the session store.
+ (WebCore::createSoupRequestAndMessageForHandle): Make the local request reference mutable.
+ (WebCore::ResourceHandle::start): Remove some code which is now part of applyAuthenticationToRequest.
+ Call applyAuthenticationToRequest and clear the user and password members like the CFNet backend does.
+ (WebCore::getCredentialFromPersistentStoreCallback): Added this callback for getting persistently stored credentials.
+ (WebCore::ResourceHandle::continueDidReceiveAuthenticationChallenge): Split out didReceiveAuthenticationChallenge
+ into this asynchronous bit.
+ (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): For GTK+ continue handling this situation after
+ first looking in the persistent credential store.
+ (WebCore::ResourceHandle::receivedCredential): Store session credentials in the session storage, which is
+ at the moment a redundant version of the Soup session storage and also prepare any persistent credentials
+ for storage later (see gotHeadersCallback).
+
2012-11-16 Erik Arvidsson <a...@chromium.org>
Update DOMException name: TypeMismatchError
Modified: trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp (134954 => 134955)
--- trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.cpp 2012-11-16 16:40:01 UTC (rev 134955)
@@ -66,8 +66,6 @@
, m_loginEntry(0)
, m_passwordEntry(0)
, m_rememberCheckButton(0)
- , m_isSavingPassword(false)
- , m_savePasswordHandler(0)
{
GtkDialog* dialog = GTK_DIALOG(m_dialog);
gtk_dialog_add_buttons(dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
@@ -190,10 +188,10 @@
void GtkAuthenticationDialog::show()
{
- Credential savedCredential = credentialBackingStore().credentialForChallenge(m_challenge);
- if (!savedCredential.isEmpty()) {
- gtk_entry_set_text(GTK_ENTRY(m_loginEntry), savedCredential.user().utf8().data());
- gtk_entry_set_text(GTK_ENTRY(m_passwordEntry), savedCredential.password().utf8().data());
+ const Credential& credentialFromPersistentStorage = m_challenge.proposedCredential();
+ if (!credentialFromPersistentStorage.isEmpty()) {
+ gtk_entry_set_text(GTK_ENTRY(m_loginEntry), credentialFromPersistentStorage.user().utf8().data());
+ gtk_entry_set_text(GTK_ENTRY(m_passwordEntry), credentialFromPersistentStorage.password().utf8().data());
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton), TRUE);
}
@@ -204,39 +202,6 @@
void GtkAuthenticationDialog::destroy()
{
gtk_widget_destroy(m_dialog);
-
- // Do not delete the object if it's still saving the password,
- // the save password callback will delete it.
- if (!m_isSavingPassword)
- delete this;
-}
-
-void GtkAuthenticationDialog::savePasswordCallback(SoupMessage*, GtkAuthenticationDialog* dialog)
-{
- dialog->savePassword();
-}
-
-void GtkAuthenticationDialog::savePassword()
-{
- ASSERT(!m_username.isNull());
- ASSERT(!m_password.isNull());
-
- // Anything but 401 and 5xx means the password was accepted.
- SoupMessage* message = m_challenge.soupMessage();
- if (message->status_code != 401 && message->status_code < 500) {
- Credential credentialToSave = Credential(
- String::fromUTF8(m_username.data()),
- String::fromUTF8(m_password.data()),
- CredentialPersistencePermanent);
- credentialBackingStore().storeCredentialsForChallenge(m_challenge, credentialToSave);
- }
-
- // Disconnect the callback. If the authentication succeeded we are done,
- // and if it failed we'll create a new GtkAuthenticationDialog and we'll
- // connect to 'got-headers' again in GtkAuthenticationDialog::authenticate()
- g_signal_handler_disconnect(message, m_savePasswordHandler);
-
- // Dialog has been already destroyed, after saving the password it should be deleted.
delete this;
}
@@ -244,16 +209,8 @@
{
const char *username = gtk_entry_get_text(GTK_ENTRY(m_loginEntry));
const char *password = gtk_entry_get_text(GTK_ENTRY(m_passwordEntry));
-
- bool rememberCredentials = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton));
- if (m_rememberCheckButton && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton))) {
- m_username = username;
- m_password = password;
- m_isSavingPassword = true;
- m_savePasswordHandler = g_signal_connect(m_challenge.soupMessage(), "got-headers", G_CALLBACK(savePasswordCallback), this);
- }
-
- CredentialPersistence persistence = rememberCredentials ? CredentialPersistencePermanent : CredentialPersistenceForSession;
+ CredentialPersistence persistence = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_rememberCheckButton)) ?
+ CredentialPersistencePermanent : CredentialPersistenceForSession;
m_challenge.authenticationClient()->receivedCredential(m_challenge,
Credential(String::fromUTF8(username), String::fromUTF8(password), persistence));
}
Modified: trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h (134954 => 134955)
--- trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/gtk/GtkAuthenticationDialog.h 2012-11-16 16:40:01 UTC (rev 134955)
@@ -42,8 +42,6 @@
private:
void destroy();
void authenticate();
- void savePassword();
- static void savePasswordCallback(SoupMessage*, GtkAuthenticationDialog*);
static void authenticationDialogResponseCallback(GtkWidget*, gint responseID, GtkAuthenticationDialog*);
AuthenticationChallenge m_challenge;
@@ -51,10 +49,6 @@
GtkWidget* m_loginEntry;
GtkWidget* m_passwordEntry;
GtkWidget* m_rememberCheckButton;
- bool m_isSavingPassword;
- unsigned long m_savePasswordHandler;
- CString m_username;
- CString m_password;
};
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/network/ResourceHandle.h (134954 => 134955)
--- trunk/Source/WebCore/platform/network/ResourceHandle.h 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/ResourceHandle.h 2012-11-16 16:40:01 UTC (rev 134955)
@@ -166,6 +166,7 @@
#endif
#if USE(SOUP)
+ void continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage);
void sendPendingRequest();
static SoupSession* defaultSession();
static uint64_t getSoupRequestInitiaingPageID(SoupRequest*);
Modified: trunk/Source/WebCore/platform/network/ResourceHandleInternal.h (134954 => 134955)
--- trunk/Source/WebCore/platform/network/ResourceHandleInternal.h 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/ResourceHandleInternal.h 2012-11-16 16:40:01 UTC (rev 134955)
@@ -194,6 +194,12 @@
RefPtr<NetworkingContext> m_context;
SoupSession* soupSession();
#endif
+#if PLATFORM(GTK)
+ struct {
+ Credential credential;
+ AuthenticationChallenge challenge;
+ } m_credentialDataToSaveInPersistentStore;
+#endif
#if PLATFORM(QT)
QNetworkReplyHandler* m_job;
RefPtr<NetworkingContext> m_context;
Modified: trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp (134954 => 134955)
--- trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.cpp 2012-11-16 16:40:01 UTC (rev 134955)
@@ -62,32 +62,58 @@
return attributes;
}
-Credential CredentialBackingStore::credentialForChallenge(const AuthenticationChallenge& challenge)
+struct CredentialForChallengeAsyncReadyCallbackData {
+ CredentialBackingStore::CredentialForChallengeCallback callback;
+ void* data;
+};
+
+static void credentialForChallengeAsyncReadyCallback(SecretService* service, GAsyncResult* asyncResult, CredentialForChallengeAsyncReadyCallbackData* callbackData)
{
- GOwnPtr<GList> elements(secret_service_search_sync(
- 0, // The default SecretService.
- SECRET_SCHEMA_COMPAT_NETWORK,
- createAttributeHashTableFromChallenge(challenge).get(),
- static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS), // The default behavior is to only return the most recent item.
- 0, // cancellable
- 0)); // error
- if (!elements || !elements->data)
- return Credential();
+ CredentialBackingStore::CredentialForChallengeCallback callback = callbackData->callback;
+ void* data = ""
+ delete callbackData;
+ GOwnPtr<GError> error;
+ GOwnPtr<GList> elements(secret_service_search_finish(service, asyncResult, &error.outPtr()));
+ if (error || !elements || !elements->data) {
+ callback(Credential(), data);
+ return;
+ }
+
GRefPtr<SecretItem> secretItem = adoptGRef(static_cast<SecretItem*>(elements->data));
GRefPtr<GHashTable> attributes = adoptGRef(secret_item_get_attributes(secretItem.get()));
String user = String::fromUTF8(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "user")));
- if (user.isEmpty())
- return Credential();
+ if (user.isEmpty()) {
+ callback(Credential(), data);
+ return;
+ }
size_t length;
GRefPtr<SecretValue> secretValue = adoptGRef(secret_item_get_secret(secretItem.get()));
const char* passwordData = secret_value_get(secretValue.get(), &length);
String password = String::fromUTF8(passwordData, length);
- return Credential(user, password, CredentialPersistencePermanent);
+ callback(Credential(user, password, CredentialPersistencePermanent), data);
}
+void CredentialBackingStore::credentialForChallenge(const AuthenticationChallenge& challenge, CredentialForChallengeCallback callback, void* data)
+{
+ // The default flag only returns the most recent item, not all of them.
+ SecretSearchFlags searchFlags = static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS);
+ CredentialForChallengeAsyncReadyCallbackData* callbackData = new CredentialForChallengeAsyncReadyCallbackData;
+ callbackData->callback = callback;
+ callbackData->data = ""
+
+ secret_service_search(
+ 0, // The default SecretService.
+ SECRET_SCHEMA_COMPAT_NETWORK,
+ createAttributeHashTableFromChallenge(challenge).get(),
+ searchFlags,
+ 0, // cancellable
+ reinterpret_cast<GAsyncReadyCallback>(credentialForChallengeAsyncReadyCallback),
+ callbackData);
+}
+
void CredentialBackingStore::storeCredentialsForChallenge(const AuthenticationChallenge& challenge, const Credential& credential)
{
CString utf8Password = credential.password().utf8();
Modified: trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h (134954 => 134955)
--- trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/gtk/CredentialBackingStore.h 2012-11-16 16:40:01 UTC (rev 134955)
@@ -38,9 +38,10 @@
friend CredentialBackingStore& credentialBackingStore();
virtual ~CredentialBackingStore() { }
- Credential credentialForChallenge(const AuthenticationChallenge&);
+ typedef void (*CredentialForChallengeCallback)(const Credential&, void* data);
+
+ void credentialForChallenge(const AuthenticationChallenge&, CredentialForChallengeCallback, void* data);
void storeCredentialsForChallenge(const AuthenticationChallenge&, const Credential&);
-
private:
CredentialBackingStore() { }
};
Modified: trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h (134954 => 134955)
--- trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/soup/AuthenticationChallenge.h 2012-11-16 16:40:01 UTC (rev 134955)
@@ -49,6 +49,7 @@
SoupSession* soupSession() const { return m_soupSession.get(); }
SoupMessage* soupMessage() const { return m_soupMessage.get(); }
SoupAuth* soupAuth() const { return m_soupAuth.get(); }
+ void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; }
private:
friend class AuthenticationChallengeBase;
Modified: trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp (134954 => 134955)
--- trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp 2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,11 +1,12 @@
/*
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
* Copyright (C) 2008 Alp Toker <a...@atoker.com>
* Copyright (C) 2008 Xan Lopez <x...@gnome.org>
* Copyright (C) 2008, 2010 Collabora Ltd.
* Copyright (C) 2009 Holger Hans Peter Freyther
* Copyright (C) 2009 Gustavo Noronha Silva <g...@gnome.org>
* Copyright (C) 2009 Christian Dywan <christ...@imendio.com>
- * Copyright (C) 2009, 2010, 2011 Igalia S.L.
+ * Copyright (C) 2009, 2010, 2011, 2012 Igalia S.L.
* Copyright (C) 2009 John Kjellberg <john.kjellb...@power.alstom.com>
* Copyright (C) 2012 Intel Corporation
*
@@ -31,6 +32,7 @@
#include "CachedResourceLoader.h"
#include "ChromeClient.h"
#include "CookieJarSoup.h"
+#include "CredentialStorage.h"
#include "FileSystem.h"
#include "Frame.h"
#include "GOwnPtrSoup.h"
@@ -68,6 +70,10 @@
#include "BlobStorageData.h"
#endif
+#if PLATFORM(GTK)
+#include "CredentialBackingStore.h"
+#endif
+
namespace WebCore {
#define READ_BUFFER_SIZE 8192
@@ -279,7 +285,7 @@
return session;
}
-static void gotHeadersCallback(SoupMessage* msg, gpointer data)
+static void gotHeadersCallback(SoupMessage* message, gpointer data)
{
ResourceHandle* handle = static_cast<ResourceHandle*>(data);
if (!handle)
@@ -293,18 +299,65 @@
d->m_response.resourceLoadTiming()->receiveHeadersEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime);
#endif
+#if PLATFORM(GTK)
+ // We are a bit more conservative with the persistent credential storage than the session store,
+ // since we are waiting until we know that this authentication succeeded before actually storing.
+ // This is because we want to avoid hitting the disk twice (once to add and once to remove) for
+ // incorrect credentials or polluting the keychain with invalid credentials.
+ if (message->status_code != 401 && message->status_code < 500 && !d->m_credentialDataToSaveInPersistentStore.credential.isEmpty()) {
+ credentialBackingStore().storeCredentialsForChallenge(
+ d->m_credentialDataToSaveInPersistentStore.challenge,
+ d->m_credentialDataToSaveInPersistentStore.credential);
+ }
+#endif
+
// The original response will be needed later to feed to willSendRequest in
// restartedCallback() in case we are redirected. For this reason, so we store it
// here.
ResourceResponse response;
- response.updateFromSoupMessage(msg);
-
+ response.updateFromSoupMessage(message);
d->m_response = response;
}
+static void applyAuthenticationToRequest(ResourceHandle* handle, bool redirect)
+{
+ // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open().
+ ResourceHandleInternal* d = handle->getInternal();
+ String user = d->m_user;
+ String password = d->m_pass;
+
+ ResourceRequest& request = d->m_firstRequest;
+ if (!handle->client() || handle->client()->shouldUseCredentialStorage(handle)) {
+ if (d->m_user.isEmpty() && d->m_pass.isEmpty())
+ d->m_initialCredential = CredentialStorage::get(request.url());
+ else if (!redirect) {
+ // If there is already a protection space known for the URL, update stored credentials
+ // before sending a request. This makes it possible to implement logout by sending an
+ // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that
+ // an authentication dialog doesn't pop up).
+ CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url());
+ }
+ }
+
+ if (!d->m_initialCredential.isEmpty()) {
+ user = d->m_initialCredential.user();
+ password = d->m_initialCredential.password();
+ }
+
+ // We always put the credentials into the URL. In the CFNetwork-port HTTP family credentials are applied in
+ // the didReceiveAuthenticationChallenge callback, but libsoup requires us to use this method to override
+ // any previously remembered credentials. It has its own per-session credential storage.
+ if (!user.isEmpty() || !password.isEmpty()) {
+ KURL urlWithCredentials(request.url());
+ urlWithCredentials.setUser(d->m_user);
+ urlWithCredentials.setPass(d->m_pass);
+ request.setURL(urlWithCredentials);
+ }
+}
+
// Called each time the message is going to be sent again except the first time.
// It's used mostly to let webkit know about redirects.
-static void restartedCallback(SoupMessage* msg, gpointer data)
+static void restartedCallback(SoupMessage* message, gpointer data)
{
ResourceHandle* handle = static_cast<ResourceHandle*>(data);
if (!handle)
@@ -313,29 +366,50 @@
if (d->m_cancelled)
return;
- GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(msg), false));
+ GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(message), false));
String location = String::fromUTF8(uri.get());
KURL newURL = KURL(handle->firstRequest().url(), location);
ResourceRequest request = handle->firstRequest();
request.setURL(newURL);
- request.setHTTPMethod(msg->method);
+ request.setHTTPMethod(message->method);
// Should not set Referer after a redirect from a secure resource to non-secure one.
if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) {
request.clearHTTPReferrer();
- soup_message_headers_remove(msg->request_headers, "Referer");
+ soup_message_headers_remove(message->request_headers, "Referer");
}
+ const KURL& url = ""
+ d->m_user = url.user();
+ d->m_pass = url.pass();
+ request.removeCredentials();
+
+ ResourceResponse& redirectResponse = d->m_response;
+ if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
+ // If the network layer carries over authentication headers from the original request
+ // in a cross-origin redirect, we want to clear those headers here.
+ request.clearHTTPAuthorization();
+ soup_message_headers_remove(message->request_headers, "Authorization");
+
+ // TODO: We are losing any username and password specified in the redirect URL, as this is the
+ // same behavior as the CFNet port. We should investigate if this is really what we want.
+ } else
+ applyAuthenticationToRequest(handle, true);
+
+ // Per-request authentication is handled via the URI-embedded username/password.
+ GOwnPtr<SoupURI> newSoupURI(soup_uri_new(request.urlStringForSoup().utf8().data()));
+ soup_message_set_uri(message, newSoupURI.get());
+
if (d->client())
- d->client()->willSendRequest(handle, request, d->m_response);
+ d->client()->willSendRequest(handle, request, redirectResponse);
if (d->m_cancelled)
return;
#if ENABLE(WEB_TIMING)
- d->m_response.setResourceLoadTiming(ResourceLoadTiming::create());
- d->m_response.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime();
+ redirectResponse.setResourceLoadTiming(ResourceLoadTiming::create());
+ redirectResponse.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime();
#endif
// Update the first party in case the base URL changed with the redirect
@@ -749,14 +823,15 @@
SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(d->soupSession(), SOUP_TYPE_REQUESTER));
GOwnPtr<GError> error;
- const ResourceRequest& request = handle->firstRequest();
+ ResourceRequest& request = handle->firstRequest();
+
d->m_soupRequest = adoptGRef(soup_requester_request(requester, request.urlStringForSoup().utf8().data(), &error.outPtr()));
if (error) {
d->m_soupRequest.clear();
return false;
}
- // Non-HTTP family requests do not need a soupMessage, as it's callbacks really only apply to HTTP.
+ // SoupMessages are only applicable to HTTP-family requests.
if (isHTTPFamilyRequest && !createSoupMessageForHandleAndRequest(handle, request)) {
d->m_soupRequest.clear();
return false;
@@ -780,15 +855,6 @@
// Used to set the keep track of custom SoupSessions for ports that support it (EFL).
d->m_context = context;
- if (!(d->m_user.isEmpty() || d->m_pass.isEmpty())) {
- // If credentials were specified for this request, add them to the url,
- // so that they will be passed to NetworkRequest.
- KURL urlWithCredentials(firstRequest().url());
- urlWithCredentials.setUser(d->m_user);
- urlWithCredentials.setPass(d->m_pass);
- d->m_firstRequest.setURL(urlWithCredentials);
- }
-
// Only allow the POST and GET methods for non-HTTP requests.
const ResourceRequest& request = firstRequest();
bool isHTTPFamilyRequest = request.url().protocolIsInHTTPFamily();
@@ -797,6 +863,11 @@
return true;
}
+ applyAuthenticationToRequest(this, false);
+ // The CFNet backend clears these, so we do as well.
+ d->m_user = String();
+ d->m_pass = String();
+
if (!createSoupRequestAndMessageForHandle(this, isHTTPFamilyRequest)) {
this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
return true;
@@ -856,17 +927,87 @@
gIgnoreSSLErrors = ignoreSSLErrors;
}
+#if PLATFORM(GTK)
+void getCredentialFromPersistentStoreCallback(const Credential& credential, void* data)
+{
+ static_cast<ResourceHandle*>(data)->continueDidReceiveAuthenticationChallenge(credential);
+}
+#endif
+
+void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage)
+{
+ ASSERT(d->m_currentWebChallenge);
+ AuthenticationChallenge& challenge = d->m_currentWebChallenge;
+
+ ASSERT(challenge.soupSession());
+ ASSERT(challenge.soupMessage());
+ if (!credentialFromPersistentStorage.isEmpty())
+ challenge.setProposedCredential(credentialFromPersistentStorage);
+
+ if (!client()) {
+ soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage());
+ clearAuthentication();
+ return;
+ }
+
+ ASSERT(challenge.soupSession());
+ ASSERT(challenge.soupMessage());
+ client()->didReceiveAuthenticationChallenge(this, challenge);
+}
+
void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
{
ASSERT(d->m_currentWebChallenge.isNull());
+
+ bool shouldUseCredentialStorage = !client() || client()->shouldUseCredentialStorage(this);
+ if (!d->m_user.isNull() && !d->m_pass.isNull()) {
+ Credential credential = Credential(d->m_user, d->m_pass, CredentialPersistenceForSession);
+ if (shouldUseCredentialStorage)
+ CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+ soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+
+ return;
+ }
+
+ // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly.
+ if (shouldUseCredentialStorage) {
+ if (!d->m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
+ // The stored credential wasn't accepted, stop using it. There is a race condition
+ // here, since a different credential might have already been stored by another
+ // ResourceHandle, but the observable effect should be very minor, if any.
+ CredentialStorage::remove(challenge.protectionSpace());
+ }
+
+ if (!challenge.previousFailureCount()) {
+ Credential credential = CredentialStorage::get(challenge.protectionSpace());
+ if (!credential.isEmpty() && credential != d->m_initialCredential) {
+ ASSERT(credential.persistence() == CredentialPersistenceNone);
+
+ // Store the credential back, possibly adding it as a default for this directory.
+ if (challenge.failureResponse().httpStatusCode() == 401)
+ CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+
+ soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+ return;
+ }
+ }
+ }
+
d->m_currentWebChallenge = challenge;
+ soup_session_pause_message(challenge.soupSession(), challenge.soupMessage());
- if (client()) {
- ASSERT(challenge.soupSession());
- ASSERT(challenge.soupMessage());
- soup_session_pause_message(challenge.soupSession(), challenge.soupMessage());
- client()->didReceiveAuthenticationChallenge(this, challenge);
+#if PLATFORM(GTK)
+ // We could also do this before we even start the request, but that would be at the expense
+ // of all request latency, versus a one-time latency for the small subset of requests that
+ // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials
+ // will become session credentials after the first use.
+ if (shouldUseCredentialStorage) {
+ credentialBackingStore().credentialForChallenge(challenge, getCredentialFromPersistentStoreCallback, this);
+ return;
}
+#endif
+
+ continueDidReceiveAuthenticationChallenge(Credential());
}
void ResourceHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& challenge)
@@ -885,6 +1026,26 @@
if (challenge != d->m_currentWebChallenge)
return;
+ // FIXME: Support empty credentials. Currently, an empty credential cannot be stored in WebCore credential storage, as that's empty value for its map.
+ if (credential.isEmpty()) {
+ receivedRequestToContinueWithoutCredential(challenge);
+ return;
+ }
+
+ // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup,
+ // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now,
+ // we place the credentials in the store even though libsoup will never fire the authenticate signal again for
+ // this protection space.
+ if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent)
+ CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url());
+
+#if PLATFORM(GTK)
+ if (credential.persistence() == CredentialPersistencePermanent) {
+ d->m_credentialDataToSaveInPersistentStore.credential = credential;
+ d->m_credentialDataToSaveInPersistentStore.challenge = challenge;
+ }
+#endif
+
ASSERT(challenge.soupSession());
ASSERT(challenge.soupMessage());
soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
Modified: trunk/Source/WebKit/gtk/ChangeLog (134954 => 134955)
--- trunk/Source/WebKit/gtk/ChangeLog 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebKit/gtk/ChangeLog 2012-11-16 16:40:01 UTC (rev 134955)
@@ -1,3 +1,17 @@
+2012-11-16 Martin Robinson <mrobin...@igalia.com>
+
+ [GTK] Move CredentialBackingStore usage from GtkAuthenticationDialog to ResourceHandleSoup
+ https://bugs.webkit.org/show_bug.cgi?id=101840
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Enable the CredentialStore by default for the WebKit1 GTK+ port. Before this value
+ didn't have an bearing on whether or not the persistent credential storage was used.
+ Now is does.
+
+ * WebCoreSupport/FrameLoaderClientGtk.cpp:
+ (WebKit::FrameLoaderClient::shouldUseCredentialStorage): Enable credential storage by default.
+
2012-11-15 Gustavo Noronha Silva <g...@gnome.org>
[GTK] Split WebCore/platform into a separate library
Modified: trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp (134954 => 134955)
--- trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp 2012-11-16 16:22:08 UTC (rev 134954)
+++ trunk/Source/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp 2012-11-16 16:40:01 UTC (rev 134955)
@@ -192,11 +192,9 @@
}
}
-bool
-FrameLoaderClient::shouldUseCredentialStorage(WebCore::DocumentLoader*, unsigned long identifier)
+bool FrameLoaderClient::shouldUseCredentialStorage(WebCore::DocumentLoader*, unsigned long identifier)
{
- notImplemented();
- return false;
+ return true;
}
void FrameLoaderClient::dispatchDidReceiveAuthenticationChallenge(WebCore::DocumentLoader*, unsigned long identifier, const AuthenticationChallenge& challenge)