Safari 4 cannot be used to update firmware on Linksys routers.
authorbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Sep 2009 20:05:30 +0000 (20:05 +0000)
committerbeidson@apple.com <beidson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Sep 2009 20:05:30 +0000 (20:05 +0000)
<rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160

Reviewed by Alexey Proskuryakov.

WebCore:

In http://trac.webkit.org/changeset/42483, we disabled CFNetwork's session credential storage and
implemented our own in WebCore.

One feature we lost is that CFNetwork would automatically send previously authenticated credentials
with new connections when the paths match, as allowed by RFC 2617 for HTTP Basic and Digest Authentication.

Even though it's optional for User Agents to do this, (some?) Linksys routers rely on this behavior for
HTTP Basic Authentication.  So now WebCore's CredentialStorage will track URL paths for Basic credentials.

We're not making this enhancement for Digest at this time, since we don't know of anything that broke
due to the change in Digest behavior.

Test: http/tests/loading/basic-credentials-sent-automatically.html
      http/tests/loading/basic-auth-resend-wrong-credentials.html

* WebCore.base.exp:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:

* platform/network/Credential.cpp:
(WebCore::Credential::isEmpty):
* platform/network/Credential.h:

Add a slightly more full featured CredentialStore that will track URL paths a credential belongs to:
* platform/network/CredentialStorage.cpp: Added.
(WebCore::protectionSpaceToCredentialMap):
(WebCore::originToDefaultCredentialMap):
(WebCore::originStringFromURL):
(WebCore::CredentialStorage::set):
(WebCore::CredentialStorage::get):
(WebCore::CredentialStorage::getDefaultAuthenticationCredential):
* platform/network/CredentialStorage.h: Added.

Allow ProtectionSpace to be a hash key:
* platform/network/ProtectionSpace.cpp:
(WebCore::ProtectionSpace::ProtectionSpace):
* platform/network/ProtectionSpace.h:
(WebCore::ProtectionSpace::ProtectionSpace):
(WebCore::ProtectionSpace::isHashTableDeletedValue):
* platform/network/ProtectionSpaceHash.h: Added.
(WebCore::ProtectionSpaceHash::hash):
(WebCore::ProtectionSpaceHash::equal):

Allow ResourceHandles to remember the initial credential they used:
* platform/network/ResourceHandleInternal.h:

Allow mutable ResourceRequests to update their platform object if HTTP header fields change:
* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::addHTTPHeaderField):

Remove WebCoreCredentialStorage:
* platform/network/cf/AuthenticationCF.cpp:
* platform/network/cf/AuthenticationCF.h:
* platform/network/mac/AuthenticationMac.h:
* platform/network/mac/AuthenticationMac.mm:

Change ResourceHandleCFNet to try to use stored credentials on new connections, if appropriate:
* platform/network/cf/ResourceHandleCFNet.cpp:
(WebCore::ResourceHandle::start):
(WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Only try the ProtectionSpace key'ed
  Credential if it does not match the initially used Credential.
(WebCore::ResourceHandle::receivedCredential):
(WebCore::WebCoreSynchronousLoader::didReceiveChallenge): Only try the ProtectionSpace key'ed
  Credential if it does not match the initially used Credential.
(WebCore::WebCoreSynchronousLoader::load):

Change ResourceHandleMac to try to use stored credentials on new connections, if appropriate:
* platform/network/mac/ResourceHandleMac.mm:
(WebCore::ResourceHandle::start):
(WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Only try the ProtectionSpace key'ed
  Credential if it does not match the initially used Credential.
(WebCore::ResourceHandle::receivedCredential):
(-[WebCoreSynchronousLoader connection:didReceiveAuthenticationChallenge:]): Only try the
  ProtectionSpace key'ed Credential if it does not match the initially used Credential.
(+[WebCoreSynchronousLoader loadRequest:allowStoredCredentials:returningResponse:error:]):

Add base64-ability to CString:
* platform/text/CString.cpp:
(WebCore::CStringBuffer::base64Encode):
(WebCore::CString::base64Encode):
* platform/text/CString.h:
(WebCore::CStringBuffer::create):
(WebCore::CStringBuffer::CStringBuffer):

WebKit/mac:

Adopt the new WebCore::CredentialStorage in WebKit/Mac.

* Misc/WebDownload.mm:
(-[WebDownloadInternal download:didReceiveAuthenticationChallenge:]):
* Plugins/WebBaseNetscapePluginView.mm:
(WebKit::getAuthenticationInfo):

WebKit/win:

Adopt the new WebCore::CredentialStorage in WebKit/Win.

* WebDownloadCFNet.cpp:
(WebDownload::didReceiveAuthenticationChallenge):

WebKitTools:

Add the ability for DRT to handle authentication challenges.

* DumpRenderTree/LayoutTestController.cpp:
(setAuthenticationPasswordCallback):
(setAuthenticationUsernameCallback):
(setHandlesAuthenticationChallengesCallback):
(LayoutTestController::staticFunctions):

* DumpRenderTree/LayoutTestController.h:
(LayoutTestController::handlesAuthenticationChallenges):
(LayoutTestController::setHandlesAuthenticationChallenges):
(LayoutTestController::authenticationUsername):
(LayoutTestController::setAuthenticationUsername):
(LayoutTestController::authenticationPassword):
(LayoutTestController::setAuthenticationPassword):

* DumpRenderTree/mac/ResourceLoadDelegate.mm:
(-[ResourceLoadDelegate webView:resource:didReceiveAuthenticationChallenge:fromDataSource:]):

* DumpRenderTree/win/ResourceLoadDelegate.cpp:
(ResourceLoadDelegate::didReceiveAuthenticationChallenge):
* DumpRenderTree/win/ResourceLoadDelegate.h:

LayoutTests:

* http/tests/loading/basic-auth-resend-wrong-credentials-expected.txt: Added.
* http/tests/loading/basic-auth-resend-wrong-credentials.html: Added.
* http/tests/loading/basic-credentials-sent-automatically-expected.txt: Added.
* http/tests/loading/basic-credentials-sent-automatically.html: Added.
* http/tests/loading/resources/basic-auth-testing.php: Added.
* http/tests/loading/resources/othersubresources: Added.
* http/tests/loading/resources/othersubresources/protected-resource.php: Added.
* http/tests/loading/resources/protected-resource.php: Added.
* http/tests/loading/resources/subresources: Added.
* http/tests/loading/resources/subresources/protected-resource.php: Added.
* http/tests/loading/resources/test2: Added.
* http/tests/loading/resources/test2/basic-auth-testing.php: Added.
* http/tests/loading/resources/test2/protected-resource.php: Added.
* platform/gtk/Skipped:
* platform/qt/Skipped:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@48363 268f45cc-cd09-0410-ab3c-d52691b4dbfc

45 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials.html [new file with mode: 0644]
LayoutTests/http/tests/loading/basic-credentials-sent-automatically-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/loading/basic-credentials-sent-automatically.html [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/basic-auth-testing.php [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/othersubresources/protected-resource.php [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/protected-resource.php [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/subresources/protected-resource.php [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/test2/basic-auth-testing.php [new file with mode: 0644]
LayoutTests/http/tests/loading/resources/test2/protected-resource.php [new file with mode: 0644]
LayoutTests/platform/gtk/Skipped
LayoutTests/platform/qt/Skipped
WebCore/ChangeLog
WebCore/WebCore.base.exp
WebCore/WebCore.vcproj/WebCore.vcproj
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/platform/network/Credential.cpp
WebCore/platform/network/Credential.h
WebCore/platform/network/CredentialStorage.cpp [new file with mode: 0644]
WebCore/platform/network/CredentialStorage.h [new file with mode: 0644]
WebCore/platform/network/ProtectionSpace.cpp
WebCore/platform/network/ProtectionSpace.h
WebCore/platform/network/ProtectionSpaceHash.h [new file with mode: 0644]
WebCore/platform/network/ResourceHandleInternal.h
WebCore/platform/network/ResourceRequestBase.cpp
WebCore/platform/network/cf/AuthenticationCF.cpp
WebCore/platform/network/cf/AuthenticationCF.h
WebCore/platform/network/cf/ResourceHandleCFNet.cpp
WebCore/platform/network/mac/AuthenticationMac.h
WebCore/platform/network/mac/AuthenticationMac.mm
WebCore/platform/network/mac/ResourceHandleMac.mm
WebCore/platform/text/CString.cpp
WebCore/platform/text/CString.h
WebKit/mac/ChangeLog
WebKit/mac/Misc/WebDownload.mm
WebKit/mac/Plugins/WebBaseNetscapePluginView.mm
WebKit/win/ChangeLog
WebKit/win/WebDownloadCFNet.cpp
WebKitTools/ChangeLog
WebKitTools/DumpRenderTree/LayoutTestController.cpp
WebKitTools/DumpRenderTree/LayoutTestController.h
WebKitTools/DumpRenderTree/mac/ResourceLoadDelegate.mm
WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.cpp
WebKitTools/DumpRenderTree/win/ResourceLoadDelegate.h

index ec1ddab..0cd468d 100644 (file)
@@ -1,3 +1,26 @@
+2009-09-14  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Safari 4 cannot be used to update firmware on Linksys routers.
+        <rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160
+
+        * http/tests/loading/basic-auth-resend-wrong-credentials-expected.txt: Added.
+        * http/tests/loading/basic-auth-resend-wrong-credentials.html: Added.
+        * http/tests/loading/basic-credentials-sent-automatically-expected.txt: Added.
+        * http/tests/loading/basic-credentials-sent-automatically.html: Added.
+        * http/tests/loading/resources/basic-auth-testing.php: Added.
+        * http/tests/loading/resources/othersubresources: Added.
+        * http/tests/loading/resources/othersubresources/protected-resource.php: Added.
+        * http/tests/loading/resources/protected-resource.php: Added.
+        * http/tests/loading/resources/subresources: Added.
+        * http/tests/loading/resources/subresources/protected-resource.php: Added.
+        * http/tests/loading/resources/test2: Added.
+        * http/tests/loading/resources/test2/basic-auth-testing.php: Added.
+        * http/tests/loading/resources/test2/protected-resource.php: Added.
+        * platform/gtk/Skipped:
+        * platform/qt/Skipped:
+
 2009-09-14  Zan Dobersek  <zandobersek@gmail.com>
 
         Reviewed by Gustavo Noronha.
diff --git a/LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials-expected.txt b/LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials-expected.txt
new file mode 100644 (file)
index 0000000..868c4c3
--- /dev/null
@@ -0,0 +1,29 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+<unknown> - didReceiveAuthenticationChallenge - Responding with wrongusername:wrongpassword
+frame "<!--framePath //<!--frame0-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame1-->-->" - didStartProvisionalLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishLoadForFrame
+<unknown> - didReceiveAuthenticationChallenge - Responding with correctusername:correctpassword
+frame "<!--framePath //<!--frame1-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame1-->-->" - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame1-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame1-->-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+This test makes sure that once WebCore preemptively sends out Basic credentials it thinks apply to a new resource, and that resource response with a 401 challenge, that it doesn't try to send the same wrong credentials a second time.
+  
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Authenticated as user: wrongusername password: wrongpassword
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+Authenticated as user: correctusername password: correctpassword
diff --git a/LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials.html b/LayoutTests/http/tests/loading/basic-auth-resend-wrong-credentials.html
new file mode 100644 (file)
index 0000000..4d12549
--- /dev/null
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script>
+
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.dumpChildFramesAsText();
+    layoutTestController.setHandlesAuthenticationChallenges(true);
+    layoutTestController.setAuthenticationUsername("wrongusername");
+    layoutTestController.setAuthenticationPassword("wrongpassword");
+    layoutTestController.waitUntilDone();
+}
+
+function firstFrameLoaded() {
+    if (window.layoutTestController) {
+        layoutTestController.setAuthenticationUsername("correctusername");
+        layoutTestController.setAuthenticationPassword("correctpassword");
+    }
+    var frame = document.createElement("iframe");
+    frame.setAttribute("src", "http://127.0.0.1:8000/loading/resources/test2/basic-auth-testing.php?username=correctusername&password=correctpassword");
+    frame.setAttribute("onload", "secondFrameLoaded()");
+    document.body.appendChild(frame);
+}
+
+function secondFrameLoaded() {
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
+
+</script>
+</head>
+<body>
+This test makes sure that once WebCore preemptively sends out Basic credentials it thinks apply to a new resource, and that resource response with a 401 challenge, that it doesn't try to send the same wrong credentials a second time.<br>
+<iframe src="resources/test2/protected-resource.php" onload="firstFrameLoaded();"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/loading/basic-credentials-sent-automatically-expected.txt b/LayoutTests/http/tests/loading/basic-credentials-sent-automatically-expected.txt
new file mode 100644 (file)
index 0000000..fbcd21a
--- /dev/null
@@ -0,0 +1,57 @@
+main frame - didStartProvisionalLoadForFrame
+main frame - didCommitLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didStartProvisionalLoadForFrame
+main frame - didFinishDocumentLoadForFrame
+<unknown> - didReceiveAuthenticationChallenge - Responding with first:first-pw
+frame "<!--framePath //<!--frame0-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishDocumentLoadForFrame
+main frame - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame1-->-->" - didStartProvisionalLoadForFrame
+frame "<!--framePath //<!--frame0-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame0-->-->" - didFinishLoadForFrame
+<unknown> - didReceiveAuthenticationChallenge - Responding with second:second-pw
+frame "<!--framePath //<!--frame1-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame1-->-->" - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame2-->-->" - didStartProvisionalLoadForFrame
+frame "<!--framePath //<!--frame1-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame1-->-->" - didFinishLoadForFrame
+frame "<!--framePath //<!--frame2-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame2-->-->" - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame3-->-->" - didStartProvisionalLoadForFrame
+frame "<!--framePath //<!--frame2-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame2-->-->" - didFinishLoadForFrame
+frame "<!--framePath //<!--frame3-->-->" - didCommitLoadForFrame
+frame "<!--framePath //<!--frame3-->-->" - didFinishDocumentLoadForFrame
+frame "<!--framePath //<!--frame3-->-->" - didHandleOnloadEventsForFrame
+frame "<!--framePath //<!--frame3-->-->" - didFinishLoadForFrame
+main frame - didFinishLoadForFrame
+This test makes sure that once an HTTP Basic Auth. protected path is authenticated once, urls that emanate from that path automatically have their credentials sent without a challenge.
+The first frame's path is /loading/resources/subresources/protected-resource.php, and we should get a challenge for it.
+It will be authorized with first/first-pw.
+The second frame's path is /loading/resources/protected-resource.php, and we should get a challenge for it, because it does not share a common subpath of the previously authorized resource.
+It will be authorized with second/second-pw.
+The third frame's path is also /loading/resources/protected-resource.php, and we should *not* get a challenge for it because we authorized to this path for the second frame.
+It will be authorized with second/second-pw.
+The fourth frame's path is /loading/resources/othersubresources/protected-resource.php, and we should *not* get a challenge for it, because it has a common subpath with the previously authorized second and third frames.
+It will be authorized with second/second-pw.
+  
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Authenticated as user: first password: first-pw
+
+--------
+Frame: '<!--framePath //<!--frame1-->-->'
+--------
+Authenticated as user: second password: second-pw
+
+--------
+Frame: '<!--framePath //<!--frame2-->-->'
+--------
+Authenticated as user: second password: second-pw
+
+--------
+Frame: '<!--framePath //<!--frame3-->-->'
+--------
+Authenticated as user: second password: second-pw
diff --git a/LayoutTests/http/tests/loading/basic-credentials-sent-automatically.html b/LayoutTests/http/tests/loading/basic-credentials-sent-automatically.html
new file mode 100644 (file)
index 0000000..7598912
--- /dev/null
@@ -0,0 +1,69 @@
+<html>
+<head>
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.dumpChildFramesAsText();
+    layoutTestController.setHandlesAuthenticationChallenges(true);
+    layoutTestController.setAuthenticationUsername("first");
+    layoutTestController.setAuthenticationPassword("first-pw");
+    layoutTestController.waitUntilDone();
+}
+
+function firstFrameLoaded()
+{
+    if (window.layoutTestController) {
+        layoutTestController.setAuthenticationUsername("second");
+        layoutTestController.setAuthenticationPassword("second-pw");
+    }
+    var frame = document.createElement("iframe");
+    frame.setAttribute("src", "http://127.0.0.1:8000/loading/resources/protected-resource.php");
+    frame.setAttribute("onload", "secondFrameLoaded()");
+    document.body.appendChild(frame);
+}
+
+function secondFrameLoaded()
+{
+    if (window.layoutTestController) {
+        layoutTestController.setAuthenticationUsername("third");
+        layoutTestController.setAuthenticationPassword("third-pw");
+    }
+    var frame = document.createElement("iframe");
+    frame.setAttribute("src", "http://127.0.0.1:8000/loading/resources/protected-resource.php");
+    frame.setAttribute("onload", "thirdFrameLoaded()");
+    document.body.appendChild(frame);
+}
+
+function thirdFrameLoaded()
+{
+    if (window.layoutTestController) {
+        layoutTestController.setAuthenticationUsername("fourth");
+        layoutTestController.setAuthenticationPassword("fourth-pw");
+    }
+    var frame = document.createElement("iframe");
+    frame.setAttribute("src", "http://127.0.0.1:8000/loading/resources/othersubresources/protected-resource.php");
+    frame.setAttribute("onload", "fourthFrameLoaded()");
+    document.body.appendChild(frame);
+}
+
+function fourthFrameLoaded()
+{
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
+
+</script>
+</head>
+<body>
+This test makes sure that once an HTTP Basic Auth. protected path is authenticated once, urls that emanate from that path automatically have their credentials sent without a challenge.<br>
+The first frame's path is /loading/resources/subresources/protected-resource.php, and we should get a challenge for it.<br>
+It will be authorized with first/first-pw.<br>
+The second frame's path is /loading/resources/protected-resource.php, and we should get a challenge for it, because it does not share a common subpath of the previously authorized resource.<br>
+It will be authorized with second/second-pw.<br>
+The third frame's path is also /loading/resources/protected-resource.php, and we should *not* get a challenge for it because we authorized to this path for the second frame.<br>
+It will be authorized with second/second-pw.<br>
+The fourth frame's path is /loading/resources/othersubresources/protected-resource.php, and we should *not* get a challenge for it, because it has a common subpath with the previously authorized second and third frames.<br>
+It will be authorized with second/second-pw.<br>
+<iframe src="resources/subresources/protected-resource.php" onload="firstFrameLoaded();"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/loading/resources/basic-auth-testing.php b/LayoutTests/http/tests/loading/resources/basic-auth-testing.php
new file mode 100644 (file)
index 0000000..2ecd612
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+$expectedUsername = isset($_GET['username']) ? $_GET['username'] : 'username';
+$expectedPassword = isset($_GET['password']) ? $_GET['password'] : 'password';
+$realm = isset($_GET['realm']) ? $_GET['realm'] : $_SERVER['REQUEST_URI'];
+
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] != $expectedUsername ||  
+    !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_PW'] != $expectedPassword) {
+    header("WWW-Authenticate: Basic realm=\"" . $realm . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    print 'Sent username:password of (' . $_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW'] . ') which is not what was expected';
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
diff --git a/LayoutTests/http/tests/loading/resources/othersubresources/protected-resource.php b/LayoutTests/http/tests/loading/resources/othersubresources/protected-resource.php
new file mode 100644 (file)
index 0000000..1537d03
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
+    header("WWW-authenticate: Basic realm=\"" . $_SERVER['REQUEST_URI'] . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
diff --git a/LayoutTests/http/tests/loading/resources/protected-resource.php b/LayoutTests/http/tests/loading/resources/protected-resource.php
new file mode 100644 (file)
index 0000000..1537d03
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
+    header("WWW-authenticate: Basic realm=\"" . $_SERVER['REQUEST_URI'] . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
diff --git a/LayoutTests/http/tests/loading/resources/subresources/protected-resource.php b/LayoutTests/http/tests/loading/resources/subresources/protected-resource.php
new file mode 100644 (file)
index 0000000..1537d03
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
+    header("WWW-authenticate: Basic realm=\"" . $_SERVER['REQUEST_URI'] . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
diff --git a/LayoutTests/http/tests/loading/resources/test2/basic-auth-testing.php b/LayoutTests/http/tests/loading/resources/test2/basic-auth-testing.php
new file mode 100644 (file)
index 0000000..2ecd612
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+$expectedUsername = isset($_GET['username']) ? $_GET['username'] : 'username';
+$expectedPassword = isset($_GET['password']) ? $_GET['password'] : 'password';
+$realm = isset($_GET['realm']) ? $_GET['realm'] : $_SERVER['REQUEST_URI'];
+
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER']) || $_SERVER['PHP_AUTH_USER'] != $expectedUsername ||  
+    !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_PW'] != $expectedPassword) {
+    header("WWW-Authenticate: Basic realm=\"" . $realm . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    print 'Sent username:password of (' . $_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW'] . ') which is not what was expected';
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
diff --git a/LayoutTests/http/tests/loading/resources/test2/protected-resource.php b/LayoutTests/http/tests/loading/resources/test2/protected-resource.php
new file mode 100644 (file)
index 0000000..1537d03
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+header("Cache-Control: no-store");
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
+    header("WWW-authenticate: Basic realm=\"" . $_SERVER['REQUEST_URI'] . "\"");
+    header('HTTP/1.0 401 Unauthorized');
+    exit;
+}
+?>
+Authenticated as user: <?php print (string)$_SERVER['PHP_AUTH_USER']?> password: <?php print (string)$_SERVER['PHP_AUTH_PW']?>
index 752a8b4..8db2ec5 100644 (file)
@@ -5724,3 +5724,6 @@ http/tests/navigation/back-send-referrer.html
 # XHR sends sometimes yield null bytes sent instead of 0.
 http/tests/xmlhttprequest/workers/shared-worker-methods.html
 http/tests/xmlhttprequest/workers/shared-worker-methods-async.html
+
+# No authentication challenge handling
+http/tests/loading/basic-credentials-sent-automatically.html
index 3b09035..223482c 100644 (file)
@@ -4838,3 +4838,6 @@ fast/workers/worker-context-multi-port.html
 
 # No User Scripts
 userscripts
+
+# No authentication challenge handling
+http/tests/loading/basic-credentials-sent-automatically.html
index aa860a3..644fb91 100644 (file)
@@ -1,3 +1,94 @@
+2009-09-14  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Safari 4 cannot be used to update firmware on Linksys routers.
+        <rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160
+
+        In http://trac.webkit.org/changeset/42483, we disabled CFNetwork's session credential storage and 
+        implemented our own in WebCore.
+
+        One feature we lost is that CFNetwork would automatically send previously authenticated credentials
+        with new connections when the paths match, as allowed by RFC 2617 for HTTP Basic and Digest Authentication.  
+
+        Even though it's optional for User Agents to do this, (some?) Linksys routers rely on this behavior for
+        HTTP Basic Authentication.  So now WebCore's CredentialStorage will track URL paths for Basic credentials.
+
+        We're not making this enhancement for Digest at this time, since we don't know of anything that broke
+        due to the change in Digest behavior.
+
+        Test: http/tests/loading/basic-credentials-sent-automatically.html
+              http/tests/loading/basic-auth-resend-wrong-credentials.html
+
+        * WebCore.base.exp:
+        * WebCore.vcproj/WebCore.vcproj:
+        * WebCore.xcodeproj/project.pbxproj:
+
+        * platform/network/Credential.cpp:
+        (WebCore::Credential::isEmpty):
+        * platform/network/Credential.h:
+
+        Add a slightly more full featured CredentialStore that will track URL paths a credential belongs to:
+        * platform/network/CredentialStorage.cpp: Added.
+        (WebCore::protectionSpaceToCredentialMap):
+        (WebCore::originToDefaultCredentialMap):
+        (WebCore::originStringFromURL):
+        (WebCore::CredentialStorage::set):
+        (WebCore::CredentialStorage::get):
+        (WebCore::CredentialStorage::getDefaultAuthenticationCredential):
+        * platform/network/CredentialStorage.h: Added.
+
+        Allow ProtectionSpace to be a hash key:
+        * platform/network/ProtectionSpace.cpp:
+        (WebCore::ProtectionSpace::ProtectionSpace):
+        * platform/network/ProtectionSpace.h:
+        (WebCore::ProtectionSpace::ProtectionSpace):
+        (WebCore::ProtectionSpace::isHashTableDeletedValue):
+        * platform/network/ProtectionSpaceHash.h: Added.
+        (WebCore::ProtectionSpaceHash::hash):
+        (WebCore::ProtectionSpaceHash::equal):
+
+        Allow ResourceHandles to remember the initial credential they used:
+        * platform/network/ResourceHandleInternal.h:
+
+        Allow mutable ResourceRequests to update their platform object if HTTP header fields change:
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::addHTTPHeaderField):
+
+        Remove WebCoreCredentialStorage:
+        * platform/network/cf/AuthenticationCF.cpp:
+        * platform/network/cf/AuthenticationCF.h:
+        * platform/network/mac/AuthenticationMac.h:
+        * platform/network/mac/AuthenticationMac.mm:
+
+        Change ResourceHandleCFNet to try to use stored credentials on new connections, if appropriate:
+        * platform/network/cf/ResourceHandleCFNet.cpp:
+        (WebCore::ResourceHandle::start):
+        (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Only try the ProtectionSpace key'ed
+          Credential if it does not match the initially used Credential.
+        (WebCore::ResourceHandle::receivedCredential):
+        (WebCore::WebCoreSynchronousLoader::didReceiveChallenge): Only try the ProtectionSpace key'ed
+          Credential if it does not match the initially used Credential.
+        (WebCore::WebCoreSynchronousLoader::load):
+
+        Change ResourceHandleMac to try to use stored credentials on new connections, if appropriate:
+        * platform/network/mac/ResourceHandleMac.mm:
+        (WebCore::ResourceHandle::start):
+        (WebCore::ResourceHandle::didReceiveAuthenticationChallenge): Only try the ProtectionSpace key'ed
+          Credential if it does not match the initially used Credential.
+        (WebCore::ResourceHandle::receivedCredential):
+        (-[WebCoreSynchronousLoader connection:didReceiveAuthenticationChallenge:]): Only try the 
+          ProtectionSpace key'ed Credential if it does not match the initially used Credential.
+        (+[WebCoreSynchronousLoader loadRequest:allowStoredCredentials:returningResponse:error:]):
+
+        Add base64-ability to CString:
+        * platform/text/CString.cpp:
+        (WebCore::CStringBuffer::base64Encode):
+        (WebCore::CString::base64Encode):
+        * platform/text/CString.h:
+        (WebCore::CStringBuffer::create):
+        (WebCore::CStringBuffer::CStringBuffer):
+
 2009-09-12  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Dan Bernstein.
index 74cf335..bb411fe 100644 (file)
@@ -311,11 +311,11 @@ __ZN7WebCore14ResourceHandle12releaseProxyEv
 __ZN7WebCore14ResourceHandle20forceContentSniffingEv
 __ZN7WebCore14ResourceLoader14cancelledErrorEv
 __ZN7WebCore14ResourceLoader19setShouldBufferDataEb
-__ZN7WebCore14SecurityOrigin6createERKNS_4KURLE
 __ZN7WebCore14SecurityOrigin16createFromStringERKNS_6StringE
 __ZN7WebCore14SecurityOrigin24registerURLSchemeAsLocalERKNS_6StringE
 __ZN7WebCore14SecurityOrigin25whiteListAccessFromOriginERKS0_RKNS_6StringES5_b
 __ZN7WebCore14SecurityOrigin27resetOriginAccessWhiteListsEv
+__ZN7WebCore14SecurityOrigin6createERKNS_4KURLE
 __ZN7WebCore15ArchiveResource6createEN3WTF10PassRefPtrINS_12SharedBufferEEERKNS_4KURLERKNS_6StringESA_SA_RKNS_16ResourceResponseE
 __ZN7WebCore15BackForwardList10removeItemEPNS_11HistoryItemE
 __ZN7WebCore15BackForwardList10setEnabledEb
@@ -396,6 +396,7 @@ __ZN7WebCore16createFullMarkupEPKNS_5RangeE
 __ZN7WebCore16enclosingIntRectERK7_NSRect
 __ZN7WebCore16isEndOfParagraphERKNS_15VisiblePositionE
 __ZN7WebCore16threadGlobalDataEv
+__ZN7WebCore17CredentialStorage3getERKNS_15ProtectionSpaceE
 __ZN7WebCore17DOMImplementation14isTextMIMETypeERKNS_6StringE
 __ZN7WebCore17GlyphPageTreeNode18treeGlyphPageCountEv
 __ZN7WebCore17HTMLPlugInElement11getNPObjectEv
@@ -456,7 +457,6 @@ __ZN7WebCore23ApplicationCacheStorage5emptyEv
 __ZN7WebCore23ReplaceSelectionCommandC1EPNS_8DocumentEN3WTF10PassRefPtrINS_16DocumentFragmentEEEbbbbbNS_10EditActionE
 __ZN7WebCore23createFragmentFromNodesEPNS_8DocumentERKN3WTF6VectorIPNS_4NodeELm0EEE
 __ZN7WebCore24BinaryPropertyListWriter17writePropertyListEv
-__ZN7WebCore24WebCoreCredentialStorage9m_storageE
 __ZN7WebCore24createFragmentFromMarkupEPNS_8DocumentERKNS_6StringES4_
 __ZN7WebCore24decodeURLEscapeSequencesERKNS_6StringE
 __ZN7WebCore24notifyHistoryItemChangedE
@@ -481,6 +481,7 @@ __ZN7WebCore31applicationIsMicrosoftMessengerEv
 __ZN7WebCore32plainTextToMallocAllocatedBufferEPKNS_5RangeERjb
 __ZN7WebCore33setDefaultThreadViolationBehaviorENS_23ThreadViolationBehaviorENS_20ThreadViolationRoundE
 __ZN7WebCore36InitializeLoggingChannelsIfNecessaryEv
+__ZN7WebCore3macERKNS_10CredentialE
 __ZN7WebCore3macERKNS_23AuthenticationChallengeE
 __ZN7WebCore4Font11setCodePathENS0_8CodePathE
 __ZN7WebCore4Font18shouldUseSmoothingEv
@@ -514,6 +515,7 @@ __ZN7WebCore4Page9goForwardEv
 __ZN7WebCore4Page9initGroupEv
 __ZN7WebCore4PageC1EPNS_12ChromeClientEPNS_17ContextMenuClientEPNS_12EditorClientEPNS_10DragClientEPNS_15InspectorClientE
 __ZN7WebCore4PageD1Ev
+__ZN7WebCore4coreEP20NSURLProtectionSpace
 __ZN7WebCore5Cache11setDisabledEb
 __ZN7WebCore5Cache13getStatisticsEv
 __ZN7WebCore5Cache13setCapacitiesEjjj
@@ -744,10 +746,10 @@ __ZN7WebCore9HTMLNames9scriptTagE
 __ZN7WebCore9PageCache11setCapacityEi
 __ZN7WebCore9PageCache27releaseAutoreleasedPagesNowEv
 __ZN7WebCore9PageGroup13addUserScriptERKNS_6StringERKNS_4KURLERKN3WTF6VectorIS1_Lm0EEEjNS_23UserScriptInjectionTimeE
-__ZN7WebCore9PageGroup17addUserStyleSheetERKNS_6StringERKNS_4KURLERKN3WTF6VectorIS1_Lm0EEEj
 __ZN7WebCore9PageGroup14addVisitedLinkEPKtm
-__ZN7WebCore9PageGroup20removeAllUserContentEv
+__ZN7WebCore9PageGroup17addUserStyleSheetERKNS_6StringERKNS_4KURLERKN3WTF6VectorIS1_Lm0EEEj
 __ZN7WebCore9PageGroup17closeLocalStorageEv
+__ZN7WebCore9PageGroup20removeAllUserContentEv
 __ZN7WebCore9PageGroup21removeAllVisitedLinksEv
 __ZN7WebCore9PageGroup25removeUserContentForWorldEj
 __ZN7WebCore9PageGroup26setShouldTrackVisitedLinksEb
index 8460e3a..4c59703 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\platform\network\CredentialStorage.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
+                                       RelativePath="..\platform\network\CredentialStorage.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\platform\network\FormData.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath="..\platform\network\ProtectionSpaceHash.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath="..\platform\network\ResourceErrorBase.cpp"\r
                                        >\r
                                </File>\r
index 2956496..2c7be1d 100644 (file)
                519611780CAC56A80010A80C /* DatabaseThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 519611620CAC56570010A80C /* DatabaseThread.h */; };
                519611EA0CAC749C0010A80C /* DatabaseTask.h in Headers */ = {isa = PBXBuildFile; fileRef = 519611E80CAC749C0010A80C /* DatabaseTask.h */; };
                519611EB0CAC749C0010A80C /* DatabaseTask.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 519611E90CAC749C0010A80C /* DatabaseTask.cpp */; };
+               51A052331058774F00CC9E95 /* CredentialStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A052311058774F00CC9E95 /* CredentialStorage.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               51A052341058774F00CC9E95 /* CredentialStorage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51A052321058774F00CC9E95 /* CredentialStorage.cpp */; };
+               51A052561058874000CC9E95 /* ProtectionSpaceHash.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A052551058874000CC9E95 /* ProtectionSpaceHash.h */; settings = {ATTRIBUTES = (Private, ); }; };
                51A45B560CAD7FD7000D2BE9 /* DatabaseAuthorizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 51A45B540CAD7FD7000D2BE9 /* DatabaseAuthorizer.h */; };
                51A45B570CAD7FD7000D2BE9 /* DatabaseAuthorizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51A45B550CAD7FD7000D2BE9 /* DatabaseAuthorizer.cpp */; };
                51A9267C0D53F0570063ECC2 /* OriginQuotaManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 51A926780D53F0570063ECC2 /* OriginQuotaManager.cpp */; };
                519611E90CAC749C0010A80C /* DatabaseTask.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseTask.cpp; sourceTree = "<group>"; };
                519FE0A10DAD446E00A08F21 /* HTMLAttributeNames.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLAttributeNames.in; sourceTree = "<group>"; };
                519FE0A20DAD446E00A08F21 /* HTMLTagNames.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLTagNames.in; sourceTree = "<group>"; };
+               51A052311058774F00CC9E95 /* CredentialStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CredentialStorage.h; sourceTree = "<group>"; };
+               51A052321058774F00CC9E95 /* CredentialStorage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CredentialStorage.cpp; sourceTree = "<group>"; };
+               51A052551058874000CC9E95 /* ProtectionSpaceHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProtectionSpaceHash.h; sourceTree = "<group>"; };
                51A45B540CAD7FD7000D2BE9 /* DatabaseAuthorizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DatabaseAuthorizer.h; sourceTree = "<group>"; };
                51A45B550CAD7FD7000D2BE9 /* DatabaseAuthorizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DatabaseAuthorizer.cpp; sourceTree = "<group>"; };
                51A926780D53F0570063ECC2 /* OriginQuotaManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OriginQuotaManager.cpp; sourceTree = "<group>"; };
                656B84D70AEA1CE900A095B4 /* network */ = {
                        isa = PBXGroup;
                        children = (
-                               510D4A2D103165EE0049EA54 /* SocketStreamErrorBase.cpp */,
-                               510D4A2E103165EE0049EA54 /* SocketStreamErrorBase.h */,
-                               510D4A30103165EE0049EA54 /* SocketStreamHandleBase.cpp */,
-                               510D4A31103165EE0049EA54 /* SocketStreamHandleBase.h */,
-                               510D4A32103165EE0049EA54 /* SocketStreamHandleClient.h */,
                                B2F34FE70E82F81700F627CD /* cf */,
                                656B84E70AEA1DAE00A095B4 /* mac */,
                                934F71370D5A6EFF00018D69 /* AuthenticationChallengeBase.cpp */,
                                934F71390D5A6F1000018D69 /* AuthenticationChallengeBase.h */,
                                514C76580CE923A1007EF3CD /* Credential.cpp */,
                                514C76590CE923A1007EF3CD /* Credential.h */,
+                               51A052321058774F00CC9E95 /* CredentialStorage.cpp */,
+                               51A052311058774F00CC9E95 /* CredentialStorage.h */,
                                B2F34FE50E82F81400F627CD /* DNS.h */,
                                514C765A0CE923A1007EF3CD /* FormData.cpp */,
                                514C765B0CE923A1007EF3CD /* FormData.h */,
                                1A7FA6180DDA3B3A0028F8A5 /* NetworkStateNotifier.h */,
                                514C765F0CE923A1007EF3CD /* ProtectionSpace.cpp */,
                                514C76600CE923A1007EF3CD /* ProtectionSpace.h */,
+                               51A052551058874000CC9E95 /* ProtectionSpaceHash.h */,
                                934F713D0D5A6F2800018D69 /* ResourceErrorBase.cpp */,
                                934F713B0D5A6F1900018D69 /* ResourceErrorBase.h */,
                                514C76630CE923A1007EF3CD /* ResourceHandle.cpp */,
                                514C76680CE923A1007EF3CD /* ResourceRequestBase.h */,
                                514C76690CE923A1007EF3CD /* ResourceResponseBase.cpp */,
                                514C766A0CE923A1007EF3CD /* ResourceResponseBase.h */,
+                               510D4A2D103165EE0049EA54 /* SocketStreamErrorBase.cpp */,
+                               510D4A2E103165EE0049EA54 /* SocketStreamErrorBase.h */,
+                               510D4A30103165EE0049EA54 /* SocketStreamHandleBase.cpp */,
+                               510D4A31103165EE0049EA54 /* SocketStreamHandleBase.h */,
+                               510D4A32103165EE0049EA54 /* SocketStreamHandleClient.h */,
                        );
                        path = network;
                        sourceTree = "<group>";
                                49EED1451051969400099FAB /* JSCanvasRenderingContext2D.h in Headers */,
                                49EED1471051969400099FAB /* JSCanvasRenderingContext3D.h in Headers */,
                                59C77F2B10545B3B00506104 /* GeolocationServiceMock.h in Headers */,
+                               51A052331058774F00CC9E95 /* CredentialStorage.h in Headers */,
+                               51A052561058874000CC9E95 /* ProtectionSpaceHash.h in Headers */,
                                BC8BF151105813BF00A40A07 /* UserStyleSheet.h in Headers */,
                                BC8BF15A1058141800A40A07 /* UserStyleSheetTypes.h in Headers */,
                                510D4A4F103177A20049EA54 /* WebSocketChannel.h in Headers */,
                                49EED14F1051971A00099FAB /* JSCanvasRenderingContext3DCustom.cpp in Sources */,
                                49EED1501051971A00099FAB /* JSCanvasRenderingContextCustom.cpp in Sources */,
                                59C77F2A10545B3B00506104 /* GeolocationServiceMock.cpp in Sources */,
+                               51A052341058774F00CC9E95 /* CredentialStorage.cpp in Sources */,
                                510D4A4E103177A20049EA54 /* WebSocketChannel.cpp in Sources */,
                                51ABAE441043AB4A008C5260 /* WebSocketHandshake.cpp in Sources */,
                        );
index 4743959..caca785 100644 (file)
@@ -43,6 +43,11 @@ Credential::Credential(const String& user, const String& password, CredentialPer
     , m_persistence(persistence)
 {
 }
+
+bool Credential::isEmpty()
+{
+    return m_user.isEmpty() && m_password.isEmpty();
+}
     
 const String& Credential::user() const
 { 
index 4d80490..ca4a45a 100644 (file)
@@ -41,6 +41,8 @@ public:
     Credential();
     Credential(const String& user, const String& password, CredentialPersistence);
     
+    bool isEmpty();
+    
     const String& user() const;
     const String& password() const;
     bool hasPassword() const;
diff --git a/WebCore/platform/network/CredentialStorage.cpp b/WebCore/platform/network/CredentialStorage.cpp
new file mode 100644 (file)
index 0000000..b7f4c01
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "CredentialStorage.h"
+
+#include "CString.h"
+#include "Credential.h"
+#include "KURL.h"
+#include "ProtectionSpaceHash.h"
+#include "StringHash.h"
+
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+typedef HashMap<ProtectionSpace, Credential> ProtectionSpaceToCredentialMap;
+static ProtectionSpaceToCredentialMap& protectionSpaceToCredentialMap()
+{
+    DEFINE_STATIC_LOCAL(ProtectionSpaceToCredentialMap, map, ());
+    return map;
+}
+
+typedef HashMap<String, HashMap<String, Credential> > OriginToDefaultBasicCredentialMap;
+static OriginToDefaultBasicCredentialMap& originToDefaultBasicCredentialMap()
+{
+    DEFINE_STATIC_LOCAL(OriginToDefaultBasicCredentialMap, map, ());
+    return map;
+}
+    
+static String originStringFromURL(const KURL& url)
+{
+    if (url.port())
+        return url.protocol() + "://" + url.host() + String::format(":%i/", url.port());
+    
+    return url.protocol() + "://" + url.host() + "/";
+}
+
+void CredentialStorage::set(const Credential& credential, const ProtectionSpace& protectionSpace, const KURL& url)
+{
+    protectionSpaceToCredentialMap().set(protectionSpace, credential);
+    
+    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.
+        String path = url.path();
+        if (!path.endsWith("/")) {
+            int index = path.reverseFind('/');
+            if (index != -1)
+                path = path.substring(0, index);
+        }
+        if (path.endsWith("/") && path.length() > 1)
+            path = path.substring(0, path.length() - 1);
+        
+        result.first->second.set(path, credential);
+    }
+}
+
+Credential CredentialStorage::get(const ProtectionSpace& protectionSpace)
+{
+    return protectionSpaceToCredentialMap().get(protectionSpace);
+}
+
+Credential CredentialStorage::getDefaultAuthenticationCredential(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) {
+            path = String();
+            credential = pathToCredentialMap.get("/");
+        } 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);
+            path = String();
+        } else {
+            path = path.substring(0, index);
+            credential = pathToCredentialMap.get(path);
+        }
+    }
+    return credential;
+}
+
+} // namespace WebCore
diff --git a/WebCore/platform/network/CredentialStorage.h b/WebCore/platform/network/CredentialStorage.h
new file mode 100644 (file)
index 0000000..737efa6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef SessionCredentialStorage_h
+#define SessionCredentialStorage_h
+
+namespace WebCore {
+
+class Credential;
+class KURL;
+class ProtectionSpace;
+
+class CredentialStorage {
+public:
+    static void set(const Credential&, const ProtectionSpace&, const KURL&);
+    static Credential get(const ProtectionSpace&);
+    static Credential getDefaultAuthenticationCredential(const KURL&);
+};
+
+} // namespace WebCore
+
+#endif // SessionCredentialStorage_h
index bd73558..d04bcbe 100644 (file)
@@ -37,7 +37,11 @@ namespace WebCore {
 // combined with the semantics of the String(NSString*) constructor
 ProtectionSpace::ProtectionSpace()
     : m_host("")
+    , m_port(0)
+    , m_serverType(ProtectionSpaceServerHTTP)
     , m_realm("")
+    , m_authenticationScheme(ProtectionSpaceAuthenticationSchemeDefault)
+    , m_isHashTableDeletedValue(false)
 {
 }
  
@@ -49,6 +53,7 @@ ProtectionSpace::ProtectionSpace(const String& host, int port, ProtectionSpaceSe
     , m_serverType(serverType)
     , m_realm(realm.length() ? realm : "")
     , m_authenticationScheme(authenticationScheme)
+    , m_isHashTableDeletedValue(false)
 {    
 }
     
index 9a73cff..126b499 100644 (file)
@@ -54,6 +54,10 @@ class ProtectionSpace {
 public:
     ProtectionSpace();
     ProtectionSpace(const String& host, int port, ProtectionSpaceServerType, const String& realm, ProtectionSpaceAuthenticationScheme);
+
+    // Hash table deleted values, which are only constructed and never copied or destroyed.
+    ProtectionSpace(WTF::HashTableDeletedValueType) : m_isHashTableDeletedValue(true) { }
+    bool isHashTableDeletedValue() const { return m_isHashTableDeletedValue; }
     
     const String& host() const;
     int port() const;
@@ -70,10 +74,12 @@ private:
     ProtectionSpaceServerType m_serverType;
     String m_realm;
     ProtectionSpaceAuthenticationScheme m_authenticationScheme;
+    bool m_isHashTableDeletedValue;
 };
 
 bool operator==(const ProtectionSpace& a, const ProtectionSpace& b);
 inline bool operator!=(const ProtectionSpace& a, const ProtectionSpace& b) { return !(a == b); }
     
-}
-#endif
+} // namespace WebCore
+
+#endif // ProtectionSpace_h
diff --git a/WebCore/platform/network/ProtectionSpaceHash.h b/WebCore/platform/network/ProtectionSpaceHash.h
new file mode 100644 (file)
index 0000000..6f68b5b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2009 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef ProtectionSpaceHash_h
+#define ProtectionSpaceHash_h
+
+#include "ProtectionSpace.h"
+
+namespace WebCore {
+
+struct ProtectionSpaceHash {
+    static unsigned hash(const ProtectionSpace& protectionSpace)
+    { 
+        unsigned hashCodes[5] = {
+            protectionSpace.host().impl() ? protectionSpace.host().impl()->hash() : 0, 
+            protectionSpace.port(), 
+            protectionSpace.serverType(),
+            protectionSpace.realm().impl() ? protectionSpace.realm().impl()->hash() : 0,
+            protectionSpace.authenticationScheme()
+        };
+
+        return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar));    
+    }
+    
+    static bool equal(const ProtectionSpace& a, const ProtectionSpace& b) { return a == b; }
+    static const bool safeToCompareToEmptyOrDeleted = false;
+};
+
+} // namespace WebCore
+
+namespace WTF {
+
+    // WebCore::ProtectionSpaceHash is the default hash for ProtectionSpace
+    template<> struct HashTraits<WebCore::ProtectionSpace> : GenericHashTraits<WebCore::ProtectionSpace> {
+        static const bool emptyValueIsZero = true;
+        static void constructDeletedValue(WebCore::ProtectionSpace& slot) { new (&slot) WebCore::ProtectionSpace(HashTableDeletedValue); }
+        static bool isDeletedValue(const WebCore::ProtectionSpace& slot) { return slot.isHashTableDeletedValue(); }
+    };
+
+    template<typename T> struct DefaultHash;
+    template<> struct DefaultHash<WebCore::ProtectionSpace> {
+        typedef WebCore::ProtectionSpaceHash Hash;
+    };
+
+} // namespace WTF
+
+
+#endif // ProtectionSpaceHash_h
index 091a26b..1733dea 100644 (file)
@@ -150,6 +150,8 @@ namespace WebCore {
         String m_user;
         String m_pass;
         
+        Credential m_initialCredential;
+        
         int status;
 
         bool m_defersLoading;
index c9b6667..a651a3f 100644 (file)
@@ -274,6 +274,9 @@ void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const Str
     pair<HTTPHeaderMap::iterator, bool> result = m_httpHeaderFields.add(name, value); 
     if (!result.second)
         result.first->second += "," + value;
+
+    if (url().protocolInHTTPFamily())
+        m_platformRequestUpdated = false;
 }
 
 void ResourceRequestBase::addHTTPHeaderFields(const HTTPHeaderMap& headerFields)
index 51d60a4..bb05a39 100644 (file)
@@ -37,8 +37,6 @@
 
 namespace WebCore {
 
-CFMutableDictionaryRef WebCoreCredentialStorage::m_storage;
-
 AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace,
                                                  const Credential& proposedCredential,
                                                  unsigned previousFailureCount,
index d3fc014..55f3e5f 100644 (file)
@@ -43,26 +43,6 @@ CFURLProtectionSpaceRef createCF(const ProtectionSpace&);
 Credential core(CFURLCredentialRef);
 ProtectionSpace core(CFURLProtectionSpaceRef);
 
-class WebCoreCredentialStorage {
-public:
-    static void set(CFURLProtectionSpaceRef protectionSpace, CFURLCredentialRef credential)
-    {
-        if (!m_storage)
-            m_storage = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-        CFDictionarySetValue(m_storage, protectionSpace, credential);
-    }
-
-    static CFURLCredentialRef get(CFURLProtectionSpaceRef protectionSpace)
-    {
-        if (!m_storage)
-            return 0;
-        return (CFURLCredentialRef)CFDictionaryGetValue(m_storage, protectionSpace);
-    }
-
-private:
-    static CFMutableDictionaryRef m_storage;
-};
-
 }
 
 #endif
index e348f83..40a0b48 100644 (file)
@@ -31,8 +31,9 @@
 
 #include "AuthenticationCF.h"
 #include "AuthenticationChallenge.h"
-#include "CookieStorageWin.h"
 #include "CString.h"
+#include "CookieStorageWin.h"
+#include "CredentialStorage.h"
 #include "DocLoader.h"
 #include "Frame.h"
 #include "FrameLoader.h"
@@ -79,6 +80,8 @@ private:
     RetainPtr<CFURLRef> m_url;
     RetainPtr<CFStringRef> m_user;
     RetainPtr<CFStringRef> m_pass;
+    // Store the preemptively used initial credential so that if we get an authentication challenge, we won't use the same one again.
+    Credential m_initialCredential;
     bool m_allowStoredCredentials;
     ResourceResponse& m_response;
     RetainPtr<CFMutableDataRef> m_data;
@@ -374,6 +377,19 @@ 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 (!d->m_initialCredential.isEmpty()) {
+        // Apply basic auth header
+        String unencoded = d->m_initialCredential.user() + ":" + d->m_initialCredential.password();
+        CString encoded = unencoded.utf8().base64Encode();
+        String authHeader = String::format("Basic %s", encoded.data());
+        d->m_request.addHTTPHeaderField("Authorization", authHeader);
+    }
+
     RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff));
 
     CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, WebCore::willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0};
@@ -441,7 +457,12 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall
         RetainPtr<CFStringRef> pass(AdoptCF, d->m_pass.createCFString());
         RetainPtr<CFURLCredentialRef> credential(AdoptCF,
             CFURLCredentialCreate(kCFAllocatorDefault, user.get(), pass.get(), 0, kCFURLCredentialPersistenceNone));
-        WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), credential.get());
+        
+        KURL urlToStore;
+        if (challenge.failureResponse().httpStatusCode() == 401)
+            urlToStore = d->m_request.url()
+        CredentialStorage::set(core(credential.get()), challenge.protectionSpace(), urlToStore);
+        
         CFURLConnectionUseCredential(d->m_connection.get(), credential.get(), challenge.cfURLAuthChallengeRef());
         d->m_user = String();
         d->m_pass = String();
@@ -450,10 +471,11 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall
     }
 
     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
-        CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()));
-        if (credential) {
-            ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
-            CFURLConnectionUseCredential(d->m_connection.get(), credential, challenge.cfURLAuthChallengeRef());
+        Credential credential = CredentialStorage::get(challenge.protectionSpace());
+        if (!credential.isEmpty() && credential != d->m_initialCredential) {
+            ASSERT(credential.persistence() == CredentialPersistenceNone);
+            RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
+            CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
             return;
         }
     }
@@ -478,7 +500,12 @@ void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge
         // to ignore it for a particular request (short of removing it altogether).
         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(webCredential));
-        WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge.cfURLAuthChallengeRef()), cfCredential.get());
+        
+        KURL urlToStore;
+        if (challenge.failureResponse().httpStatusCode() == 401)
+            urlToStore = d->m_request.url()        
+        CredentialStorage::set(webCredential, challenge.protectionSpace(), urlToStore);
+
         CFURLConnectionUseCredential(d->m_connection.get(), cfCredential.get(), challenge.cfURLAuthChallengeRef());
     } else {
         RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
@@ -659,19 +686,28 @@ void WebCoreSynchronousLoader::didReceiveChallenge(CFURLConnectionRef conn, CFUR
     WebCoreSynchronousLoader* loader = static_cast<WebCoreSynchronousLoader*>(const_cast<void*>(clientInfo));
 
     if (loader->m_user && loader->m_pass) {
-        RetainPtr<CFURLCredentialRef> credential(AdoptCF,
-            CFURLCredentialCreate(kCFAllocatorDefault, loader->m_user.get(), loader->m_pass.get(), 0, kCFURLCredentialPersistenceNone));
-        WebCoreCredentialStorage::set(CFURLAuthChallengeGetProtectionSpace(challenge), credential.get());
-        CFURLConnectionUseCredential(conn, credential.get(), challenge);
+        Credential credential(loader->m_user.get(), loader->m_pass.get(), CredentialPersistenceNone);
+        RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
+        
+        CFURLResponseRef urlResponse = CFURLAuthChallengeGetFailureResponse(challenge);
+        CFHTTPMessageRef httpResponse = urlResponse ? CFURLResponseGetHTTPResponse(urlResponse) : 0;
+        KURL urlToStore;
+        if (httpResponse && CFHTTPMessageGetResponseStatusCode(httpResponse) == 401)
+            urlToStore = loader->m_url;
+            
+        CredentialStorage::set(credential, core(CFURLAuthChallengeGetProtectionSpace(challenge)), urlToStore);
+        
+        CFURLConnectionUseCredential(conn, cfCredential.get(), challenge);
         loader->m_user = 0;
         loader->m_pass = 0;
         return;
     }
     if (!CFURLAuthChallengeGetPreviousFailureCount(challenge) && loader->m_allowStoredCredentials) {
-        CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge));
-        if (credential) {
-            ASSERT(CFURLCredentialGetPersistence(credential) == kCFURLCredentialPersistenceNone);
-            CFURLConnectionUseCredential(conn, credential, challenge);
+        Credential credential = CredentialStorage::get(core(CFURLAuthChallengeGetProtectionSpace(challenge)));
+        if (!credential.isEmpty() && credential != loader->m_initialCredential) {
+            ASSERT(credential.persistence() == CredentialPersistenceNone);
+            RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
+            CFURLConnectionUseCredential(conn, cfCredential.get(), challenge);
             return;
         }
     }
@@ -707,8 +743,23 @@ RetainPtr<CFDataRef> WebCoreSynchronousLoader::load(const ResourceRequest& reque
         ResourceRequest requestWithoutCredentials(request);
         requestWithoutCredentials.removeCredentials();
         cfRequest.adoptCF(makeFinalRequest(requestWithoutCredentials, ResourceHandle::shouldContentSniffURL(requestWithoutCredentials.url())));
-    } else
-        cfRequest.adoptCF(makeFinalRequest(request, ResourceHandle::shouldContentSniffURL(request.url())));
+    } else {
+        // <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.
+        ResourceRequest requestWithInitialCredential(request);
+        if (loader.m_allowStoredCredentials && url.protocolInHTTPFamily())
+            m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(url);
+
+        if (!m_initialCredential.isEmpty()) {
+            // Apply basic auth header
+            String unencoded = m_initialCredential.user() + ":" + m_initialCredential.password();
+            CString encoded = unencoded.utf8().base64Encode();
+            String authHeader = String::format("Basic %s", encoded.data());
+            requestWithInitialCredential.addHTTPHeaderField("Authorization", authHeader);
+        }
+
+        cfRequest.adoptCF(makeFinalRequest(requestWithInitialCredential, ResourceHandle::shouldContentSniffURL(requestWithInitialCredential.url())));
+    }
 
     CFURLConnectionClient_V3 client = { 3, &loader, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, 0, didFinishLoading, didFail, 0, didReceiveChallenge, 0, shouldUseCredentialStorage, 0 };
     RetainPtr<CFURLConnectionRef> connection(AdoptCF, CFURLConnectionCreate(kCFAllocatorDefault, cfRequest.get(), reinterpret_cast<CFURLConnectionClient*>(&client)));
index 7fb6bee..f55ac24 100644 (file)
@@ -45,24 +45,6 @@ AuthenticationChallenge core(NSURLAuthenticationChallenge *);
 ProtectionSpace core(NSURLProtectionSpace *);
 Credential core(NSURLCredential *);
 
-class WebCoreCredentialStorage {
-public:
-    static void set(NSURLCredential *credential, NSURLProtectionSpace *protectionSpace)
-    {
-        if (!m_storage)
-            m_storage = [[NSMutableDictionary alloc] init];
-        [m_storage setObject:credential forKey:protectionSpace];
-    }
-
-    static NSURLCredential *get(NSURLProtectionSpace *protectionSpace)
-    {
-        return static_cast<NSURLCredential *>([m_storage objectForKey:protectionSpace]);
-    }
-
-private:
-    static NSMutableDictionary* m_storage;
-};
-
 }
 #endif
 
index 961a79d..355931d 100644 (file)
@@ -36,9 +36,6 @@
 
 namespace WebCore {
 
-
-NSMutableDictionary* WebCoreCredentialStorage::m_storage;
-
 AuthenticationChallenge::AuthenticationChallenge(const ProtectionSpace& protectionSpace,
                                                  const Credential& proposedCredential,
                                                  unsigned previousFailureCount,
index bfa83a3..4391970 100644 (file)
@@ -29,6 +29,8 @@
 #import "AuthenticationChallenge.h"
 #import "AuthenticationMac.h"
 #import "BlockExceptions.h"
+#import "CString.h"
+#import "CredentialStorage.h"
 #import "DocLoader.h"
 #import "FormDataStreamMac.h"
 #import "Frame.h"
@@ -74,6 +76,8 @@ using namespace WebCore;
     NSURL *m_url;
     NSString *m_user;
     NSString *m_pass;
+    // Store the preemptively used initial credential so that if we get an authentication challenge, we won't use the same one again.
+    Credential m_initialCredential;
     BOOL m_allowStoredCredentials;
     NSURLResponse *m_response;
     NSMutableData *m_data;
@@ -167,6 +171,19 @@ bool ResourceHandle::start(Frame* frame)
         urlWithCredentials.setPass(d->m_pass);
         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 (!d->m_initialCredential.isEmpty()) {
+        // Apply basic auth header
+        String unencoded = d->m_initialCredential.user() + ":" + d->m_initialCredential.password();
+        CString encoded = unencoded.utf8().base64Encode();
+        String authHeader = String::format("Basic %s", encoded.data());
+        d->m_request.addHTTPHeaderField("Authorization", authHeader);
+    }
 
     if (!ResourceHandle::didSendBodyDataDelegateExists())
         associateStreamWithResourceHandle([d->m_request.nsURLRequest() HTTPBodyStream], this);
@@ -458,9 +475,9 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall
     }
 
     if (!challenge.previousFailureCount() && (!client() || client()->shouldUseCredentialStorage(this))) {
-        NSURLCredential *credential = WebCoreCredentialStorage::get([mac(challenge) protectionSpace]);
-        if (credential) {
-            [challenge.sender() useCredential:credential forAuthenticationChallenge:mac(challenge)];
+        Credential credential = CredentialStorage::get(challenge.protectionSpace());
+        if (!credential.isEmpty() && credential != d->m_initialCredential) {
+            [challenge.sender() useCredential:mac(credential) forAuthenticationChallenge:mac(challenge)];
             return;
         }
     }
@@ -495,7 +512,11 @@ void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge
     if (credential.persistence() == CredentialPersistenceNone) {
         // NSURLCredentialPersistenceNone doesn't work on Tiger, so we have to use session persistence.
         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceForSession);
-        WebCoreCredentialStorage::set(mac(webCredential), [d->m_currentMacChallenge protectionSpace]);
+        KURL urlToStore;
+        if (challenge.failureResponse().httpStatusCode() == 401)
+            urlToStore = d->m_request.url();
+        CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
+        
         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
     } else
 #else
@@ -504,7 +525,10 @@ void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge
         // to ignore it for a particular request (short of removing it altogether).
         // <rdar://problem/6867598> gallery.me.com is temporarily whitelisted, so that QuickTime plug-in could see the credentials.
         Credential webCredential(credential.user(), credential.password(), CredentialPersistenceNone);
-        WebCoreCredentialStorage::set(mac(webCredential), [d->m_currentMacChallenge protectionSpace]);
+        KURL urlToStore;
+        if (challenge.failureResponse().httpStatusCode() == 401)
+            urlToStore = d->m_request.url();
+        CredentialStorage::set(webCredential, core([d->m_currentMacChallenge protectionSpace]), urlToStore);
         [[d->m_currentMacChallenge sender] useCredential:mac(webCredential) forAuthenticationChallenge:d->m_currentMacChallenge];
     } else
 #endif
@@ -898,7 +922,11 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen
         NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:m_user
                                                                    password:m_pass
                                                                 persistence:NSURLCredentialPersistenceNone];
-        WebCoreCredentialStorage::set(credential, [challenge protectionSpace]);
+        KURL urlToStore;
+        if ([[challenge failureResponse] isKindOfClass:[NSHTTPURLResponse class]] && [(NSHTTPURLResponse*)[challenge failureResponse] statusCode] == 401)
+            urlToStore = m_url;
+        CredentialStorage::set(core(credential), core([challenge protectionSpace]), urlToStore);
+        
         [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
         [credential release];
         [m_user release];
@@ -908,10 +936,10 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen
         return;
     }
     if ([challenge previousFailureCount] == 0 && m_allowStoredCredentials) {
-        NSURLCredential *credential = WebCoreCredentialStorage::get([challenge protectionSpace]);
-        ASSERT([credential persistence] == NSURLCredentialPersistenceNone);
-        if (credential) {
-            [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
+        Credential credential = CredentialStorage::get(core([challenge protectionSpace]));
+        ASSERT(credential.persistence() == CredentialPersistenceNone);
+        if (!credential.isEmpty() && credential != m_initialCredential) {
+            [[challenge sender] useCredential:mac(credential) forAuthenticationChallenge:challenge];
             return;
         }
     }
@@ -975,21 +1003,35 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen
 {
     WebCoreSynchronousLoader *delegate = [[WebCoreSynchronousLoader alloc] init];
 
-    NSURL *url = [request URL];
-    delegate->m_user = [[url user] copy];
-    delegate->m_pass = [[url password] copy];
+    KURL url([request URL]);
+    delegate->m_user = [nsStringNilIfEmpty(url.user()) retain];
+    delegate->m_pass = [nsStringNilIfEmpty(url.pass()) retain];
     delegate->m_allowStoredCredentials = allowStoredCredentials;
 
     NSURLConnection *connection;
 
     // Take user/pass out of the URL.
     // Credentials for ftp can only be passed in URL, the connection:didReceiveAuthenticationChallenge: delegate call won't be made.
-    if ((delegate->m_user || delegate->m_pass) && KURL(url).protocolInHTTPFamily()) {
+    if ((delegate->m_user || delegate->m_pass) && url.protocolInHTTPFamily()) {
         ResourceRequest requestWithoutCredentials = request;
         requestWithoutCredentials.removeCredentials();
         connection = [[NSURLConnection alloc] initWithRequest:requestWithoutCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
-    } else
-        connection = [[NSURLConnection alloc] initWithRequest:request delegate:delegate startImmediately:NO];
+    } else {
+        // <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.
+        ResourceRequest requestWithInitialCredentials = request;
+        if (allowStoredCredentials && url.protocolInHTTPFamily())
+            delegate->m_initialCredential = CredentialStorage::getDefaultAuthenticationCredential(url);
+            
+        if (!delegate->m_initialCredential.isEmpty()) {
+            // Apply basic auth header
+            String unencoded = delegate->m_initialCredential.user() + ":" + delegate->m_initialCredential.password();
+            CString encoded = unencoded.utf8().base64Encode();
+            String authHeader = String::format("Basic %s", encoded.data());
+            requestWithInitialCredentials.addHTTPHeaderField("Authorization", authHeader);
+        }
+        connection = [[NSURLConnection alloc] initWithRequest:requestWithInitialCredentials.nsURLRequest() delegate:delegate startImmediately:NO];
+    }
 
     [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:WebCoreSynchronousLoaderRunLoopMode];
     [connection start];
index 90990f8..5b8ac58 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2006, 2008, 2009 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "config.h"
 #include "CString.h"
 
+#include "Base64.h"
+
 using std::min;
 
 namespace WebCore {
 
+PassRefPtr<CStringBuffer> CStringBuffer::base64Encode()
+{
+    Vector<char> encoded;
+    WebCore::base64Encode(m_vector, encoded);
+    return CStringBuffer::create(encoded);
+}
+
 CString::CString(const char* str)
 {
     init(str, strlen(str));
@@ -90,6 +99,11 @@ void CString::copyBufferIfNeeded()
     memcpy(m_buffer->mutableData(), m_temp->data(), len);
 }
 
+CString CString::base64Encode()
+{
+    return CString(m_buffer->base64Encode().get());
+}
+
 bool operator==(const CString& a, const CString& b)
 {
     if (a.isNull() != b.isNull())
index f084ddf..2d1cc2b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2006, 2008, 2009 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,9 +43,12 @@ namespace WebCore {
         friend class CString;
 
         static PassRefPtr<CStringBuffer> create(unsigned length) { return adoptRef(new CStringBuffer(length)); }
+        static PassRefPtr<CStringBuffer> create(const Vector<char>& vectorToAdopt) { return adoptRef(new CStringBuffer(vectorToAdopt)); }
         CStringBuffer(unsigned length) : m_vector(length) { }
+        CStringBuffer(const Vector<char>& vectorToAdopt) : m_vector(vectorToAdopt) { }
         char* mutableData() { return m_vector.data(); }
 
+        PassRefPtr<CStringBuffer> base64Encode();
         Vector<char> m_vector;
     };
 
@@ -59,6 +62,8 @@ namespace WebCore {
         CString(CStringBuffer* buffer) : m_buffer(buffer) { }
         static CString newUninitialized(size_t length, char*& characterBuffer);
 
+        CString base64Encode();
+        
         const char* data() const;
         char* mutableData();
         unsigned length() const;
index 7fcff4c..7270b0f 100644 (file)
@@ -1,3 +1,17 @@
+2009-09-14  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Safari 4 cannot be used to update firmware on Linksys routers.
+        <rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160
+
+        Adopt the new WebCore::CredentialStorage in WebKit/Mac.
+
+        * Misc/WebDownload.mm:
+        (-[WebDownloadInternal download:didReceiveAuthenticationChallenge:]):
+        * Plugins/WebBaseNetscapePluginView.mm:
+        (WebKit::getAuthenticationInfo):
+
 2009-09-12  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Anders Carlsson.
index 978465a..01ed767 100644 (file)
@@ -31,6 +31,9 @@
 #import <Foundation/NSURLAuthenticationChallenge.h>
 #import <Foundation/NSURLDownload.h>
 #import <WebCore/AuthenticationMac.h>
+#import <WebCore/Credential.h>
+#import <WebCore/CredentialStorage.h>
+#import <WebCore/ProtectionSpace.h>
 #import <WebKit/WebPanelAuthenticationHandler.h>
 #import <wtf/Assertions.h>
 
@@ -111,7 +114,7 @@ using namespace WebCore;
 {
     // Try previously stored credential first.
     if (![challenge previousFailureCount]) {
-        NSURLCredential *credential = WebCoreCredentialStorage::get([challenge protectionSpace]);
+        NSURLCredential *credential = mac(CredentialStorage::get(core([challenge protectionSpace])));
         if (credential) {
             [[challenge sender] useCredential:credential forAuthenticationChallenge:challenge];
             return;
index 3aae96d..2b3f394 100644 (file)
@@ -43,6 +43,8 @@
 
 #import <WebCore/WebCoreObjCExtras.h>
 #import <WebCore/AuthenticationMac.h>
+#import <WebCore/Credential.h>
+#import <WebCore/CredentialStorage.h>
 #import <WebCore/CString.h>
 #import <WebCore/Document.h>
 #import <WebCore/Element.h>
@@ -50,6 +52,7 @@
 #import <WebCore/FrameLoader.h>
 #import <WebCore/HTMLPlugInElement.h>
 #import <WebCore/Page.h>
+#import <WebCore/ProtectionSpace.h>
 #import <WebCore/RenderView.h>
 #import <WebKit/DOMPrivate.h>
 #import <runtime/InitializeThreading.h>
@@ -867,7 +870,7 @@ bool getAuthenticationInfo(const char* protocolStr, const char* hostStr, int32_t
     
     RetainPtr<NSURLProtectionSpace> protectionSpace(AdoptNS, [[NSURLProtectionSpace alloc] initWithHost:host port:port protocol:protocol realm:realm authenticationMethod:authenticationMethod]);
     
-    NSURLCredential *credential = WebCoreCredentialStorage::get(protectionSpace.get());
+    NSURLCredential *credential = mac(CredentialStorage::get(core(protectionSpace.get())));
     if (!credential)
         credential = [[NSURLCredentialStorage sharedCredentialStorage] defaultCredentialForProtectionSpace:protectionSpace.get()];
     if (!credential)
index ef8f691..756b8d1 100644 (file)
@@ -1,3 +1,15 @@
+2009-09-14  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Safari 4 cannot be used to update firmware on Linksys routers.
+        <rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160
+
+        Adopt the new WebCore::CredentialStorage in WebKit/Win.
+
+        * WebDownloadCFNet.cpp:
+        (WebDownload::didReceiveAuthenticationChallenge):
+
 2009-09-11  Steve Falkenburg  <sfalken@apple.com>
 
         Reviewed by Dan Bernstein.
index 0df738e..c2605ac 100644 (file)
@@ -47,6 +47,7 @@
 #pragma warning(push, 0)
 #include <WebCore/AuthenticationCF.h>
 #include <WebCore/BString.h>
+#include <WebCore/CredentialStorage.h>
 #include <WebCore/ResourceError.h>
 #include <WebCore/ResourceHandle.h>
 #include <WebCore/ResourceRequest.h>
@@ -381,9 +382,10 @@ void WebDownload::didReceiveAuthenticationChallenge(CFURLAuthChallengeRef challe
 {
     // Try previously stored credential first.
     if (!CFURLAuthChallengeGetPreviousFailureCount(challenge)) {
-        CFURLCredentialRef credential = WebCoreCredentialStorage::get(CFURLAuthChallengeGetProtectionSpace(challenge));
-        if (credential) {
-            CFURLDownloadUseCredential(m_download.get(), credential, challenge);
+        Credential credential = CredentialStorage::get(core(CFURLAuthChallengeGetProtectionSpace(challenge)));
+        if (!credential.isEmpty()) {
+            RetainPtr<CFURLCredentialRef> cfCredential(AdoptCF, createCF(credential));
+            CFURLDownloadUseCredential(m_download.get(), cfCredential.get(), challenge);
             return;
         }
     }
index 9bd64f6..a8f3303 100644 (file)
@@ -1,3 +1,33 @@
+2009-09-14  Brady Eidson  <beidson@apple.com>
+
+        Reviewed by Alexey Proskuryakov.
+
+        Safari 4 cannot be used to update firmware on Linksys routers.
+        <rdar://problem/7174050> and https://bugs.webkit.org/show_bug.cgi?id=29160
+
+        Add the ability for DRT to handle authentication challenges.
+
+        * DumpRenderTree/LayoutTestController.cpp:
+        (setAuthenticationPasswordCallback):
+        (setAuthenticationUsernameCallback):
+        (setHandlesAuthenticationChallengesCallback):
+        (LayoutTestController::staticFunctions):
+
+        * DumpRenderTree/LayoutTestController.h:
+        (LayoutTestController::handlesAuthenticationChallenges):
+        (LayoutTestController::setHandlesAuthenticationChallenges):
+        (LayoutTestController::authenticationUsername):
+        (LayoutTestController::setAuthenticationUsername):
+        (LayoutTestController::authenticationPassword):
+        (LayoutTestController::setAuthenticationPassword):
+
+        * DumpRenderTree/mac/ResourceLoadDelegate.mm:
+        (-[ResourceLoadDelegate webView:resource:didReceiveAuthenticationChallenge:fromDataSource:]):
+
+        * DumpRenderTree/win/ResourceLoadDelegate.cpp:
+        (ResourceLoadDelegate::didReceiveAuthenticationChallenge):
+        * DumpRenderTree/win/ResourceLoadDelegate.h:
+
 2009-09-12  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Dan Bernstein.
index 6be67ab..6e98208 100644 (file)
@@ -574,6 +574,44 @@ static JSValueRef setAppCacheMaximumSizeCallback(JSContextRef context, JSObjectR
 
 }
 
+static JSValueRef setAuthenticationPasswordCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    // Has mac & windows implementation
+    if (argumentCount < 1)
+        return JSValueMakeUndefined(context);
+
+    JSRetainPtr<JSStringRef> password(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    size_t maxLength = JSStringGetMaximumUTF8CStringSize(password.get());
+    char passwordBuffer[maxLength + 1];
+    JSStringGetUTF8CString(password.get(), passwordBuffer, maxLength + 1);
+    
+    LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+    controller->setAuthenticationPassword(passwordBuffer);
+
+    return JSValueMakeUndefined(context);
+}
+
+static JSValueRef setAuthenticationUsernameCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    // Has mac & windows implementation
+    if (argumentCount < 1)
+        return JSValueMakeUndefined(context);
+
+    JSRetainPtr<JSStringRef> username(Adopt, JSValueToStringCopy(context, arguments[0], exception));
+    ASSERT(!*exception);
+
+    size_t maxLength = JSStringGetMaximumUTF8CStringSize(username.get());
+    char usernameBuffer[maxLength + 1];
+    JSStringGetUTF8CString(username.get(), usernameBuffer, maxLength + 1);
+    
+    LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+    controller->setAuthenticationUsername(usernameBuffer);
+
+    return JSValueMakeUndefined(context);
+}
+
 static JSValueRef setAuthorAndUserStylesEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     // Has mac & windows implementation
@@ -672,6 +710,18 @@ static JSValueRef setGeolocationPermissionCallback(JSContextRef context, JSObjec
     return JSValueMakeUndefined(context);
 }
 
+static JSValueRef setHandlesAuthenticationChallengesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    // Has mac & windows implementation
+    if (argumentCount < 1)
+        return JSValueMakeUndefined(context);
+
+    LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
+    controller->setHandlesAuthenticationChallenges(JSValueToBoolean(context, arguments[0]));
+
+    return JSValueMakeUndefined(context);
+}
+
 static JSValueRef setIconDatabaseEnabledCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     // Has mac & windows implementation
@@ -1103,20 +1153,23 @@ JSStaticFunction* LayoutTestController::staticFunctions()
         { "repaintSweepHorizontally", repaintSweepHorizontallyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setAcceptsEditing", setAcceptsEditingCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setAlwaysAcceptCookies", setAlwaysAcceptCookiesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
-        { "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setAppCacheMaximumSize", setAppCacheMaximumSizeCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 
+        { "setAuthenticationPassword", setAuthenticationPasswordCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "setAuthenticationUsername", setAuthenticationUsernameCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "setAuthorAndUserStylesEnabled", setAuthorAndUserStylesEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setCallCloseOnWebViews", setCallCloseOnWebViewsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setCanOpenWindows", setCanOpenWindowsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setCacheModel", setCacheModelCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setCloseRemainingWindowsWhenComplete", setCloseRemainingWindowsWhenCompleteCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setCustomPolicyDelegate", setCustomPolicyDelegateCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setDatabaseQuota", setDatabaseQuotaCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 
-        { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
-        { "setMockGeolocationError", setMockGeolocationErrorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setGeolocationPermission", setGeolocationPermissionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "setHandlesAuthenticationChallenges", setHandlesAuthenticationChallengesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setIconDatabaseEnabled", setIconDatabaseEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setJavaScriptProfilingEnabled", setJavaScriptProfilingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setMainFrameIsFirstResponder", setMainFrameIsFirstResponderCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "setMockGeolocationPosition", setMockGeolocationPositionCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "setMockGeolocationError", setMockGeolocationErrorCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setPersistentUserStyleSheetLocation", setPersistentUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setPopupBlockingEnabled", setPopupBlockingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setPrivateBrowsingEnabled", setPrivateBrowsingEnabledCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
index f4a3175..fa81acb 100644 (file)
@@ -174,6 +174,15 @@ public:
 
     bool alwaysAcceptCookies() const { return m_alwaysAcceptCookies; }
     void setAlwaysAcceptCookies(bool alwaysAcceptCookies);
+    
+    bool handlesAuthenticationChallenges() const { return m_handlesAuthenticationChallenges; }
+    void setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges) { m_handlesAuthenticationChallenges = handlesAuthenticationChallenges; }
+    
+    const std::string& authenticationUsername() const { return m_authenticationUsername; }
+    void setAuthenticationUsername(std::string username) { m_authenticationUsername = username; }
+    
+    const std::string& authenticationPassword() const { return m_authenticationPassword; }
+    void setAuthenticationPassword(std::string password) { m_authenticationPassword = password; }
 
     bool globalFlag() const { return m_globalFlag; }
     void setGlobalFlag(bool globalFlag) { m_globalFlag = globalFlag; }
@@ -225,7 +234,10 @@ private:
     bool m_globalFlag;
     bool m_isGeolocationPermissionSet;
     bool m_geolocationPermission;
+    bool m_handlesAuthenticationChallenges;
 
+    std::string m_authenticationUsername;
+    std::string m_authenticationPassword; 
     std::string m_testPathOrURL;
     std::string m_expectedPixelHash;    // empty string if no hash
     
index 0089f7f..3aaba59 100644 (file)
 
 - (void)webView:(WebView *)wv resource:(id)identifier didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource
 {
+    if (!gLayoutTestController->handlesAuthenticationChallenges())
+        return;
+    
+    const char* user = gLayoutTestController->authenticationUsername().c_str();
+    NSString *nsUser = [NSString stringWithFormat:@"%s", user ? user : ""];
+
+    const char* password = gLayoutTestController->authenticationPassword().c_str();
+    NSString *nsPassword = [NSString stringWithFormat:@"%s", password ? password : ""];
+
+    NSString *string = [NSString stringWithFormat:@"%@ - didReceiveAuthenticationChallenge - Responding with %@:%@", identifier, nsUser, nsPassword];
+    printf("%s\n", [string UTF8String]);
+    
+    [[challenge sender] useCredential:[NSURLCredential credentialWithUser:nsUser password:nsPassword persistence:NSURLCredentialPersistenceForSession]
+                              forAuthenticationChallenge:challenge];
 }
 
 - (void)webView:(WebView *)wv resource:(id)identifier didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge fromDataSource:(WebDataSource *)dataSource
index 06476e7..0cd61da 100644 (file)
 
 #include "DumpRenderTree.h"
 #include "LayoutTestController.h"
+#include <comutil.h>
+#include <WebKit/WebKitCOMAPI.h>
 #include <wtf/HashMap.h>
 #include <wtf/Vector.h>
 #include <sstream>
 
+
 using std::wstring;
 using std::wiostream;
 
@@ -251,6 +254,33 @@ HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::willSendRequest(
     return S_OK;
 }
 
+HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveAuthenticationChallenge( 
+    /* [in] */ IWebView *webView,
+    /* [in] */ unsigned long identifier,
+    /* [in] */ IWebURLAuthenticationChallenge *challenge,
+    /* [in] */ IWebDataSource *dataSource)
+{
+    if (!gLayoutTestController->handlesAuthenticationChallenges())
+        return;
+    
+    const char* user = gLayoutTestController->authenticationUsername().c_str();
+    const char* password = gLayoutTestController->authenticationPassword().c_str();
+
+    printf("%s - didReceiveAuthenticationChallenge - Responding with %s:%s\n", descriptionSuitableForTestResult(identifier).c_str(), user, password);
+    
+    COMPtr<IWebURLAuthenticationChallengeSender> sender;
+    if (!challenge || FAILED(challenge->QueryInterface(&sender)))
+        return E_FAIL;
+        
+    COMPtr<IWebURLCredential> credential;
+    if (FAILED(WebKitCreateInstance(CLSID_WebURLCredential, 0, IID_IWebURLCredential, (void**)&credential)))
+        return E_FAIL;
+    credential->initWithUser(_bstr_t(user), _bstr_t(password), WebURLCredentialPersistenceForSession);
+
+    sender->useCredential(credential.get(), challenge);
+    return S_OK;
+}
+
 HRESULT STDMETHODCALLTYPE ResourceLoadDelegate::didReceiveResponse(
     /* [in] */ IWebView* webView, 
     /* [in] */ unsigned long identifier, 
index c708147..924727b 100644 (file)
@@ -60,7 +60,7 @@ public:
         /* [in] */ IWebView *webView,
         /* [in] */ unsigned long identifier,
         /* [in] */ IWebURLAuthenticationChallenge *challenge,
-        /* [in] */ IWebDataSource *dataSource) { return E_NOTIMPL; }
+        /* [in] */ IWebDataSource *dataSource);
         
     virtual HRESULT STDMETHODCALLTYPE didCancelAuthenticationChallenge( 
         /* [in] */ IWebView *webView,