[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.15.1-1414-gc69ee75

ap at apple.com ap at apple.com
Thu Oct 29 20:42:39 UTC 2009


The following commit has been merged in the webkit-1.1 branch:
commit 995c263e156e77e60f93e2329263f8031ee771eb
Author: ap at apple.com <ap at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Fri Oct 9 22:29:41 2009 +0000

            Reviewed by Brady Eidson.
    
            https://bugs.webkit.org/show_bug.cgi?id=30260
            <rdar://problem/6447115> REGRESSION: Logging out from SAP doesn't work
    
            Tests: http/tests/xmlhttprequest/logout.html
                   http/tests/xmlhttprequest/re-login-async.html
                   http/tests/xmlhttprequest/re-login.html
    
            Fix several issues with existing credential handling code.
    
            * platform/network/CredentialStorage.cpp:
            (WebCore::pathToDefaultProtectionSpaceMap): Changed the data structure to a simpler one.
            (WebCore::originsWithCredentials): The reason for two-stage lookup above was that we didn't
            want to iterate paths for origins that never had credentials associated with them. Changed
            to use a separate HashSet for this.
            (WebCore::pathToDefaultProtectionSpaceMap): The concept of default per-path credentials didn't
            match the spec very well. UAs are supposed to deduce protection space from an URL, and then
            use whichever credentials are known for this protection space. So, OriginToDefaultBasicCredentialMap
            is now PathToDefaultProtectionSpaceMap.
            (WebCore::protectionSpaceMapKeyFromURL): Factored out a helper that extracts a directory
            URL from a given URL. These directory URLs are what we use as keys in PathToDefaultProtectionSpaceMap.
            (WebCore::CredentialStorage::set): Updated for above changes.
            (WebCore::findDefaultProtectionSpaceForURL): Factored out code iterating path length to find
            a prefix in OriginToDefaultBasicCredentialMap.
            (WebCore::CredentialStorage::set): Another version of set() can update credentials for a
            URL default protection space. It does nothing if the given URL doesn't correspond to a known
            protection space.
            (WebCore::CredentialStorage::get): Renamed from getDefaultAuthenticationCredential.
    
            * platform/network/CredentialStorage.h: Made the distinction between methods that use a known
            protection space and those that deduce one from URL more clear.
    
            * platform/network/mac/ResourceHandleMac.mm:
            (WebCore::ResourceHandle::start): Update credentials before starting the request for real.
            This makes the following pattern work:
              var req = new XMLHttpRequest("GET", "logout.html", "logout", "logout"); // wrong credentials
              req.send("");
              req.abort();
            Abort() is used here to avoid having UA present an auth dialog after getting a 401 response.
            Note that one cannot log in using the same method, because there isn't a known protection
            space for the URL yet in that case, so the added code has no effect.
            (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Use a correct persistence for
            calling receivedCredential(). This fixes logging in using an async XHR (withut abort(), of
            course), and matches sync case.
            (+[WebCoreSynchronousLoader loadRequest:allowStoredCredentials:returningResponse:error:]):
            Renamed getDefaultAuthenticationCredential() to get().
    
            * platform/network/cf/ResourceHandleCFNet.cpp:
            (WebCore::ResourceHandle::start):
            (WebCore::WebCoreSynchronousLoader::load):
            Same changes as in Mac code.
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@49406 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 11e4fd8..14e0788 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,24 @@
+2009-10-09  Alexey Proskuryakov  <ap at apple.com>
+
+        Reviewed by Brady Eidson.
+
+        https://bugs.webkit.org/show_bug.cgi?id=30260
+        <rdar://problem/6447115> REGRESSION: Logging out from SAP doesn't work
+
+        * http/tests/xmlhttprequest/logout-expected.txt: Added.
+        * http/tests/xmlhttprequest/logout.html: Added.
+        * http/tests/xmlhttprequest/resources/logout: Added.
+        * http/tests/xmlhttprequest/resources/logout/resource.php: Added.
+        Test that the logout method in question works (it currently works in Firefox, too).
+
+        * http/tests/xmlhttprequest/re-login-async-expected.txt: Added.
+        * http/tests/xmlhttprequest/re-login-async.html: Added.
+        * http/tests/xmlhttprequest/re-login-expected.txt: Added.
+        * http/tests/xmlhttprequest/re-login.html: Added.
+        * http/tests/xmlhttprequest/resources/re-login: Added.
+        * http/tests/xmlhttprequest/resources/re-login/resource.php: Added.
+        Test that switching to a new credential works again, too.
+
 2009-10-09  Enrica Casucci  <enrica at apple.com>
 
         Reviewed by Adele Peterson.
diff --git a/LayoutTests/http/tests/xmlhttprequest/logout-expected.txt b/LayoutTests/http/tests/xmlhttprequest/logout-expected.txt
new file mode 100644
index 0000000..c04e367
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/logout-expected.txt
@@ -0,0 +1,7 @@
+rdar://problem/6447115 Test that a method for logging out of a site that is used by SAP works.
+
+If an authentication dialog appears, please cancel it.
+
+Login: PASS
+Async request sent before logout: PASS
+Logout: PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/logout.html b/LayoutTests/http/tests/xmlhttprequest/logout.html
new file mode 100644
index 0000000..c4c2248
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/logout.html
@@ -0,0 +1,56 @@
+<body>
+<p><a href="rdar://problem/6447115">rdar://problem/6447115</a> Test that a method for logging out of a site that is used by SAP works.</p>
+<p>If an authentication dialog appears, please cancel it.</p>
+<span>Login: </span><span id="login">FAIL - Test not run</span><br>
+<span>Async request sent before logout: </span><span id="async">FAIL - Test not run</span><br>
+<span>Logout: </span><span id="logout">FAIL - Test not run</span>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+}
+
+function login()
+{
+    var xhr = new XMLHttpRequest;
+    // "?login" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/logout/resource.php?login", false, "user", "pass");
+    xhr.send("");
+}
+
+function logout()
+{
+    var xhr = new XMLHttpRequest;
+    // logout.html doesn't even exist - we don't need to send this request to server.
+    xhr.open("GET", "resources/logout/subdirectory/logout.html", true, "logout", "logout");
+    xhr.send("");
+    xhr.abort();
+}
+
+function isAuthenticated()
+{
+    var xhr = new XMLHttpRequest;
+    // "?isAuthenticated" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/logout/resource.php?isAuthenticated", false);
+    xhr.send("");
+    return xhr.status == 200;
+}
+
+login();
+document.getElementById("login").innerHTML = isAuthenticated() ? "PASS" : "FAIL";
+
+// Test that a request sent before logout actually has credentials.
+var r = new XMLHttpRequest;
+r.open("GET", "resources/logout/resource.php?isAuthenticated", true);
+r.onload = function() {
+    document.getElementById("async").innerHTML = r.status == 200 ? "PASS" : "FAIL";
+
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
+r.send("");
+
+logout();
+document.getElementById("logout").innerHTML = isAuthenticated() ? "FAIL" : "PASS";
+</script>
+</body>
diff --git a/LayoutTests/http/tests/xmlhttprequest/re-login-async-expected.txt b/LayoutTests/http/tests/xmlhttprequest/re-login-async-expected.txt
new file mode 100644
index 0000000..5f73150
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/re-login-async-expected.txt
@@ -0,0 +1,6 @@
+Test that default credentials aren't used when ones are provided to XHR explicitly.
+
+Login: PASS
+Default: PASS
+Re-login: PASS
+New default: PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/re-login-async.html b/LayoutTests/http/tests/xmlhttprequest/re-login-async.html
new file mode 100644
index 0000000..1bc490c
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/re-login-async.html
@@ -0,0 +1,63 @@
+<body>
+<p>Test that default credentials aren't used when ones are provided to XHR explicitly.</p>
+<span>Login: </span><span id="login">FAIL - Test not run</span><br>
+<span>Default: </span><span id="default">FAIL - Test not run</span><br>
+<span>Re-login: </span><span id="relogin">FAIL - Test not run</span><br>
+<span>New default: </span><span id="newdefault">FAIL - Test not run</span>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+}
+
+var xhr = new XMLHttpRequest;
+
+function f1()
+{
+    // "?login1" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/re-login/resource.php?login1", true, "user1", "pass");
+    xhr.send("");
+    xhr.onload = function() {
+        document.getElementById("login").innerHTML = xhr.responseText == "User: user1, password: pass." ? "PASS" : "FAIL";
+        f2();
+    }
+}
+
+function f2()
+{
+    // "?default" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/re-login/resource.php?default", true);
+    xhr.send("");
+    xhr.onload = function() {
+        document.getElementById("default").innerHTML = xhr.responseText == "User: user1, password: pass." ? "PASS" : "FAIL";
+        f3();
+    }
+}
+
+function f3()
+{
+    // "?login2" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/re-login/resource.php?login2", true, "user2", "pass");
+    xhr.send("");
+    xhr.onload = function() {
+        document.getElementById("relogin").innerHTML = xhr.responseText == "User: user2, password: pass." ? "PASS" : "FAIL";
+        f4();
+    }
+}
+
+function f4()
+{
+    // "?newdefault" is only here for ease of debugging; it doesn't affect behavior.
+    xhr.open("GET", "resources/re-login/resource.php?newdefault", true);
+    xhr.send("");
+    xhr.onload = function() {
+        document.getElementById("newdefault").innerHTML = xhr.responseText == "User: user2, password: pass." ? "PASS" : "FAIL";
+        if (window.layoutTestController)
+            layoutTestController.notifyDone();
+    }
+}
+
+f1();
+
+</script>
+</body>
diff --git a/LayoutTests/http/tests/xmlhttprequest/re-login-expected.txt b/LayoutTests/http/tests/xmlhttprequest/re-login-expected.txt
new file mode 100644
index 0000000..5f73150
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/re-login-expected.txt
@@ -0,0 +1,6 @@
+Test that default credentials aren't used when ones are provided to XHR explicitly.
+
+Login: PASS
+Default: PASS
+Re-login: PASS
+New default: PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/re-login.html b/LayoutTests/http/tests/xmlhttprequest/re-login.html
new file mode 100644
index 0000000..56e6702
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/re-login.html
@@ -0,0 +1,33 @@
+<body>
+<p>Test that default credentials aren't used when ones are provided to XHR explicitly.</p>
+<span>Login: </span><span id="login">FAIL - Test not run</span><br>
+<span>Default: </span><span id="default">FAIL - Test not run</span><br>
+<span>Re-login: </span><span id="relogin">FAIL - Test not run</span><br>
+<span>New default: </span><span id="newdefault">FAIL - Test not run</span>
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+
+var xhr = new XMLHttpRequest;
+// "?login1" is only here for ease of debugging; it doesn't affect behavior.
+xhr.open("GET", "resources/re-login/resource.php?login1", false, "user1", "pass");
+xhr.send("");
+document.getElementById("login").innerHTML = xhr.responseText == "User: user1, password: pass." ? "PASS" : "FAIL";
+
+// "?default" is only here for ease of debugging; it doesn't affect behavior.
+xhr.open("GET", "resources/re-login/resource.php?default", false);
+xhr.send("");
+document.getElementById("default").innerHTML = xhr.responseText == "User: user1, password: pass." ? "PASS" : "FAIL";
+
+// "?login2" is only here for ease of debugging; it doesn't affect behavior.
+xhr.open("GET", "resources/re-login/resource.php?login2", false, "user2", "pass");
+xhr.send("");
+document.getElementById("relogin").innerHTML = xhr.responseText == "User: user2, password: pass." ? "PASS" : "FAIL";
+
+// "?newdefault" is only here for ease of debugging; it doesn't affect behavior.
+xhr.open("GET", "resources/re-login/resource.php?default", false);
+xhr.send("");
+document.getElementById("newdefault").innerHTML = xhr.responseText == "User: user2, password: pass." ? "PASS" : "FAIL";
+
+</script>
+</body>
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/logout/resource.php b/LayoutTests/http/tests/xmlhttprequest/resources/logout/resource.php
new file mode 100644
index 0000000..c86c295
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/resources/logout/resource.php
@@ -0,0 +1,12 @@
+<?php
+  header("Cache-Control: no-cache, no-store");
+
+  if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] != 'user' || $_SERVER['PHP_AUTH_PW'] != 'pass') {
+   header('WWW-Authenticate: Basic realm="Please cancel this authentication dialog"');
+   header('HTTP/1.0 401 Unauthorized');
+   echo 'Authentication canceled';
+   exit;
+  } else {
+   echo "User: {$_SERVER['PHP_AUTH_USER']}, password: {$_SERVER['PHP_AUTH_PW']}.";
+  }
+?>
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/re-login/resource.php b/LayoutTests/http/tests/xmlhttprequest/resources/re-login/resource.php
new file mode 100644
index 0000000..aba1d0e
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/resources/re-login/resource.php
@@ -0,0 +1,12 @@
+<?php
+  header("Cache-Control: no-cache, no-store");
+
+  if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ) {
+   header('WWW-Authenticate: Basic realm="WebKit test re-login"');
+   header('HTTP/1.0 401 Unauthorized');
+   echo 'Authentication canceled';
+   exit;
+  } else {
+   echo "User: {$_SERVER['PHP_AUTH_USER']}, password: {$_SERVER['PHP_AUTH_PW']}.";
+  }
+?>
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index d5adf45..ec458ce 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,58 @@
+2009-10-09  Alexey Proskuryakov  <ap at apple.com>
+
+        Reviewed by Brady Eidson.
+
+        https://bugs.webkit.org/show_bug.cgi?id=30260
+        <rdar://problem/6447115> REGRESSION: Logging out from SAP doesn't work
+
+        Tests: http/tests/xmlhttprequest/logout.html
+               http/tests/xmlhttprequest/re-login-async.html
+               http/tests/xmlhttprequest/re-login.html
+
+        Fix several issues with existing credential handling code.
+
+        * platform/network/CredentialStorage.cpp:
+        (WebCore::pathToDefaultProtectionSpaceMap): Changed the data structure to a simpler one.
+        (WebCore::originsWithCredentials): The reason for two-stage lookup above was that we didn't
+        want to iterate paths for origins that never had credentials associated with them. Changed
+        to use a separate HashSet for this.
+        (WebCore::pathToDefaultProtectionSpaceMap): The concept of default per-path credentials didn't
+        match the spec very well. UAs are supposed to deduce protection space from an URL, and then
+        use whichever credentials are known for this protection space. So, OriginToDefaultBasicCredentialMap
+        is now PathToDefaultProtectionSpaceMap.
+        (WebCore::protectionSpaceMapKeyFromURL): Factored out a helper that extracts a directory
+        URL from a given URL. These directory URLs are what we use as keys in PathToDefaultProtectionSpaceMap.
+        (WebCore::CredentialStorage::set): Updated for above changes.
+        (WebCore::findDefaultProtectionSpaceForURL): Factored out code iterating path length to find
+        a prefix in OriginToDefaultBasicCredentialMap.
+        (WebCore::CredentialStorage::set): Another version of set() can update credentials for a
+        URL default protection space. It does nothing if the given URL doesn't correspond to a known
+        protection space.
+        (WebCore::CredentialStorage::get): Renamed from getDefaultAuthenticationCredential.
+
+        * platform/network/CredentialStorage.h: Made the distinction between methods that use a known
+        protection space and those that deduce one from URL more clear.
+
+        * platform/network/mac/ResourceHandleMac.mm:
+        (WebCore::ResourceHandle::start): Update credentials before starting the request for real.
+        This makes the following pattern work:
+          var req = new XMLHttpRequest("GET", "logout.html", "logout", "logout"); // wrong credentials
+          req.send("");
+          req.abort();
+        Abort() is used here to avoid having UA present an auth dialog after getting a 401 response.
+        Note that one cannot log in using the same method, because there isn't a known protection
+        space for the URL yet in that case, so the added code has no effect.
+        (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Use a correct persistence for
+        calling receivedCredential(). This fixes logging in using an async XHR (withut abort(), of
+        course), and matches sync case.
+        (+[WebCoreSynchronousLoader loadRequest:allowStoredCredentials:returningResponse:error:]):
+        Renamed getDefaultAuthenticationCredential() to get().
+
+        * platform/network/cf/ResourceHandleCFNet.cpp:
+        (WebCore::ResourceHandle::start):
+        (WebCore::WebCoreSynchronousLoader::load):
+        Same changes as in Mac code.
+
 2009-10-09  Enrica Casucci  <enrica at apple.com>
 
         Reviewed by Adele Peterson.
diff --git a/WebCore/platform/network/CredentialStorage.cpp b/WebCore/platform/network/CredentialStorage.cpp
index 407ed5b..f58cff5 100644
--- a/WebCore/platform/network/CredentialStorage.cpp
+++ b/WebCore/platform/network/CredentialStorage.cpp
@@ -43,13 +43,19 @@ static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap()
     return map;
 }
 
-typedef HashMap<String, HashMap<String, Credential> > OriginToDefaultBasicCredentialMap;
-static OriginToDefaultBasicCredentialMap& originToDefaultBasicCredentialMap()
+static HashSet<String> originsWithCredentials()
 {
-    DEFINE_STATIC_LOCAL(OriginToDefaultBasicCredentialMap, map, ());
+    DEFINE_STATIC_LOCAL(HashSet<String>, set, ());
+    return set;
+}
+
+typedef HashMap<String, ProtectionSpace> PathToDefaultProtectionSpaceMap;
+static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap()
+{
+    DEFINE_STATIC_LOCAL(PathToDefaultProtectionSpaceMap, map, ());
     return map;
 }
-    
+
 static String originStringFromURL(const KURL& url)
 {
     if (url.port())
@@ -58,32 +64,37 @@ static String originStringFromURL(const KURL& url)
     return url.protocol() + "://" + url.host() + "/";
 }
 
+static String protectionSpaceMapKeyFromURL(const KURL& url)
+{
+    ASSERT(url.isValid());
+
+    // Remove the last path component that is not a directory to determine the subtree for which credentials will apply.
+    // We keep a leading slash, but remove a trailing one.
+    String directoryURL = url.string().substring(0, url.pathEnd());
+    unsigned directoryURLPathStart = url.pathStart();
+    ASSERT(directoryURL[directoryURLPathStart] == '/');
+    if (directoryURL.length() > directoryURLPathStart + 1) {
+        int index = directoryURL.reverseFind('/');
+        ASSERT(index > 0);
+        directoryURL = directoryURL.substring(0, (static_cast<unsigned>(index) != directoryURLPathStart) ? index : directoryURLPathStart + 1);
+    }
+    ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
+
+    return directoryURL;
+}
+
 void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url)
 {
     ASSERT(url.protocolInHTTPFamily());
     ASSERT(url.isValid());
 
     protectionSpaceToCredentialMap().set(protectionSpace, credential);
-    
+    originsWithCredentials().add(originStringFromURL(url));
+
     ProtectionSpaceAuthenticationScheme scheme = protectionSpace.authenticationScheme();
-    if (url.protocolInHTTPFamily() && (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault)) {
-        String origin = originStringFromURL(url);
-        
-        HashMap<String, Credential> pathToCredentialMap;
-        pair<HashMap<String, HashMap<String, Credential> >::iterator, bool> result = originToDefaultBasicCredentialMap().add(origin, pathToCredentialMap);
-        
-        // Remove the last path component that is not a directory to determine the subpath for which this credential applies.
-        // We keep a leading slash, but remove a trailing one.
-        String path = url.path();
-        ASSERT(path.length() > 0);
-        ASSERT(path[0] == '/');
-        if (path.length() > 1) {
-            int index = path.reverseFind('/');
-            path = path.substring(0, index ? index : 1);
-        }
-        ASSERT(path.length() == 1 || path[path.length() - 1] != '/');
-        
-        result.first->second.set(path, credential);
+    if (scheme == ProtectionSpaceAuthenticationSchemeHTTPBasic || scheme == ProtectionSpaceAuthenticationSchemeDefault) {
+        // The map can contain both a path and its subpath - while redundant, this makes lookups faster.
+        pathToDefaultProtectionSpaceMap().set(protectionSpaceMapKeyFromURL(url), protectionSpace);
     }
 }
 
@@ -92,33 +103,53 @@ Credential CredentialStorage::get(const ProtectionSpace& protectionSpace)
     return protectionSpaceToCredentialMap().get(protectionSpace);
 }
 
-Credential CredentialStorage::getDefaultAuthenticationCredential(const KURL& url)
+static PathToDefaultProtectionSpaceMap::iterator findDefaultProtectionSpaceForURL(const KURL& url)
 {
     ASSERT(url.protocolInHTTPFamily());
-    String origin = originStringFromURL(url);
-    const HashMap<String, Credential>& pathToCredentialMap(originToDefaultBasicCredentialMap().get(origin));
-    if (pathToCredentialMap.isEmpty())
-        return Credential();
-    
-    // Check to see if there is a stored credential for the subpath ancestry of this url.
-    String path = url.path();
-    Credential credential = pathToCredentialMap.get(path);
-    while (credential.isEmpty() && !path.isNull()) {
-        int index = path.reverseFind('/');
-        if (index == 0) {
-            credential = pathToCredentialMap.get("/");
-            break;
-        } else if (index == -1) {
-            // This case should never happen, as all HTTP URL paths should start with a leading /
-            ASSERT_NOT_REACHED();
-            credential = pathToCredentialMap.get(path);
-            break;
-        } else {
-            path = path.substring(0, index);
-            credential = pathToCredentialMap.get(path);
-        }
+    ASSERT(url.isValid());
+
+    PathToDefaultProtectionSpaceMap& map = pathToDefaultProtectionSpaceMap();
+
+    // Don't spend time iterating the path for origins that don't have any credentials.
+    if (!originsWithCredentials().contains(originStringFromURL(url)))
+        return map.end();
+
+    String directoryURL = protectionSpaceMapKeyFromURL(url);
+    unsigned directoryURLPathStart = url.pathStart();
+    while (true) {
+        PathToDefaultProtectionSpaceMap::iterator iter = map.find(directoryURL);
+        if (iter != map.end())
+            return iter;
+
+        if (directoryURL.length() == directoryURLPathStart + 1)  // path is "/" already, cannot shorten it any more
+            return map.end();
+
+        int index = directoryURL.reverseFind('/', -2);
+        ASSERT(index > 0);
+        directoryURL = directoryURL.substring(0, (static_cast<unsigned>(index) == directoryURLPathStart) ? index + 1 : index);
+        ASSERT(directoryURL.length() > directoryURLPathStart);
+        ASSERT(directoryURL.length() == directoryURLPathStart + 1 || directoryURL[directoryURL.length() - 1] != '/');
     }
-    return credential;
+}
+
+bool CredentialStorage::set(const Credential& credential, const KURL& url)
+{
+    ASSERT(url.protocolInHTTPFamily());
+    ASSERT(url.isValid());
+    PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
+    if (iter == pathToDefaultProtectionSpaceMap().end())
+        return false;
+    ASSERT(originsWithCredentials().contains(originStringFromURL(url)));
+    protectionSpaceToCredentialMap().set(iter->second, credential);
+    return true;
+}
+
+Credential CredentialStorage::get(const KURL& url)
+{
+    PathToDefaultProtectionSpaceMap::iterator iter = findDefaultProtectionSpaceForURL(url);
+    if (iter == pathToDefaultProtectionSpaceMap().end())
+        return Credential();
+    return protectionSpaceToCredentialMap().get(iter->second);
 }
 
 } // namespace WebCore
diff --git a/WebCore/platform/network/CredentialStorage.h b/WebCore/platform/network/CredentialStorage.h
index 737efa6..5086f69 100644
--- a/WebCore/platform/network/CredentialStorage.h
+++ b/WebCore/platform/network/CredentialStorage.h
@@ -36,7 +36,11 @@ class CredentialStorage {
 public:
     static void set(const Credential&, const ProtectionSpace&, const KURL&);
     static Credential get(const ProtectionSpace&);
-    static Credential getDefaultAuthenticationCredential(const KURL&);
+
+    // These methods work for authentication schemes that support sending credentials without waiting for a request. E.g., for HTTP Basic authentication scheme
+    // a client should assume that all paths at or deeper than the depth of a known protected resource share are within the same protection space.
+    static bool set(const Credential&, const KURL&); // Returns true if the URL corresponds to a known protection space, so credentials could be updated.
+    static Credential get(const KURL&);
 };
 
 } // namespace WebCore
diff --git a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp
index ea5fcc6..477df9a 100644
--- a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp
+++ b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp
@@ -401,8 +401,18 @@ bool ResourceHandle::start(Frame* frame)
 
     // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
     // try and reuse the credential preemptively, as allowed by RFC 2617.
-    if (!client() || client()->shouldUseCredentialStorage(this) && d->m_request.url().protocolInHTTPFamily())
-        d->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(d->m_request.url());
+    if (!client() || client()->shouldUseCredentialStorage(this) && d->m_request.url().protocolInHTTPFamily()) {
+        if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
+            // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
+            // try and reuse the credential preemptively, as allowed by RFC 2617.
+            d->m_initialCredential = CredentialStorage::get(d->m_request.url());
+        } else {
+            // 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), d->m_request.url());
+        }
+    }
         
     if (!d->m_initialCredential.isEmpty()) {
         String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password());
@@ -769,7 +779,7 @@ RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& reque
         // try and reuse the credential preemptively, as allowed by RFC 2617.
         ResourceRequest requestWithInitialCredential(request);
         if (loader.m_allowStoredCredentials && url.protocolInHTTPFamily())
-            loader.m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(url);
+            loader.m_initialCredential = CredentialStorage::get(url);
 
         if (!loader.m_initialCredential.isEmpty()) {
             String authHeader = "Basic " + encodeBasicAuthorization(loader.m_initialCredential.user(), loader.m_initialCredential.password());
diff --git a/WebCore/platform/network/mac/ResourceHandleMac.mm b/WebCore/platform/network/mac/ResourceHandleMac.mm
index ec60079..a088a8a 100644
--- a/WebCore/platform/network/mac/ResourceHandleMac.mm
+++ b/WebCore/platform/network/mac/ResourceHandleMac.mm
@@ -183,12 +183,21 @@ bool ResourceHandle::start(Frame* frame)
         d->m_request.setURL(urlWithCredentials);
     }
     
-    // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
-    // try and reuse the credential preemptively, as allowed by RFC 2617.
-    if (!client() || client()->shouldUseCredentialStorage(this) && d->m_request.url().protocolInHTTPFamily())
-        d->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(d->m_request.url());
+    if ((!client() || client()->shouldUseCredentialStorage(this)) && d->m_request.url().protocolInHTTPFamily()) {
+        if (d->m_user.isEmpty() && d->m_pass.isEmpty()) {
+            // <rdar://problem/7174050> - For URLs that match the paths of those previously challenged for HTTP Basic authentication, 
+            // try and reuse the credential preemptively, as allowed by RFC 2617.
+            d->m_initialCredential = CredentialStorage::get(d->m_request.url());
+        } else {
+            // 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), d->m_request.url());
+        }
+    }
         
     if (!d->m_initialCredential.isEmpty()) {
+        // FIXME: Support Digest authentication, and Proxy-Authorization.
         String authHeader = "Basic " + encodeBasicAuthorization(d->m_initialCredential.user(), d->m_initialCredential.password());
         d->m_request.addHTTPHeaderField("Authorization", authHeader);
     }
@@ -471,7 +480,7 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall
     if (!d->m_user.isNull() && !d->m_pass.isNull()) {
         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:d->m_user
                                                                    password:d->m_pass
-                                                                persistence:NSURLCredentialPersistenceNone];
+                                                                persistence:NSURLCredentialPersistenceForSession];
         d->m_currentMacChallenge = challenge.nsURLAuthenticationChallenge();
         d->m_currentWebChallenge = challenge;
         receivedCredential(challenge, core(credential));
@@ -1055,7 +1064,7 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen
         // try and reuse the credential preemptively, as allowed by RFC 2617.
         ResourceRequest requestWithInitialCredentials = request;
         if (allowStoredCredentials && url.protocolInHTTPFamily())
-            delegate->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(url);
+            delegate->m_initialCredential = CredentialStorage::get(url);
             
         if (!delegate->m_initialCredential.isEmpty()) {
             String authHeader = "Basic " + encodeBasicAuthorization(delegate->m_initialCredential.user(), delegate->m_initialCredential.password());

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list