diff options
51 files changed, 1181 insertions, 291 deletions
diff --git a/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon-expected.txt b/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon-expected.txt new file mode 100644 index 0000000..e3bf3f6 --- /dev/null +++ b/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon-expected.txt @@ -0,0 +1,11 @@ +Test for <rdar://problem/5666078> Cookie parsing terminates at the first semicolon, ignoring quotes (16699) + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +Check that setting a cookie with a semi-colon in a duoble-quoted value works +PASS cookie is 'disorder="477beccb;richard"'. +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon.html b/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon.html new file mode 100644 index 0000000..0b06db7 --- /dev/null +++ b/LayoutTests/http/tests/cookies/double-quoted-value-with-semi-colon.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="resources/cookies-test-style.css"> +<script src="resources/cookies-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="script-tests/double-quoted-value-with-semi-colon.js"></script> +<script src="resources/cookies-test-post.js"></script> +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/multiple-cookies-expected.txt b/LayoutTests/http/tests/cookies/multiple-cookies-expected.txt new file mode 100644 index 0000000..35b58c1 --- /dev/null +++ b/LayoutTests/http/tests/cookies/multiple-cookies-expected.txt @@ -0,0 +1,13 @@ +This test checks that mulitple cookies are correctly set. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +Check setting several cookies without clearing. +PASS cookie is 'test=foobar'. +PASS cookie is 'test2=foobar; test=foobar'. +PASS cookie is 'test2=foobar; test3=foobar; test=foobar'. +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/http/tests/cookies/multiple-cookies.html b/LayoutTests/http/tests/cookies/multiple-cookies.html new file mode 100644 index 0000000..e5b14f3 --- /dev/null +++ b/LayoutTests/http/tests/cookies/multiple-cookies.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="resources/cookies-test-style.css"> +<script src="resources/cookies-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="script-tests/multiple-cookies.js"></script> +<script src="resources/cookies-test-post.js"></script> +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/resources/clearCookies.cgi b/LayoutTests/http/tests/cookies/resources/clearCookies.cgi new file mode 100755 index 0000000..27896b7 --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/clearCookies.cgi @@ -0,0 +1,27 @@ +#!/usr/bin/perl -wT +use strict; + +print "Content-Type: text/plain\n"; +print "Cache-Control: no-store\n"; +print 'Cache-Control: no-cache="set-cookie"' . "\n"; + +my $cookie = $ENV{"HTTP_CLEAR_COOKIE"}; + +if ($cookie =~ /Max-Age/i) { + $cookie =~ s/Max-Age *= *[0-9]+/Max-Age=0/i; +} else { + $cookie .= ";" unless ($cookie =~ m/;$/); + $cookie .= " " unless ($cookie =~ m/ $/); + $cookie .= "Max-Age=0"; +} + +if ($cookie =~ /Expires/i) { + # Set the "Expires" field to UNIX epoch + $cookie =~ s/Expires *= *[^;]+/Expires=Thu, 01 Jan 1970 00:00:00 GMT/i; +} else { + $cookie .= ";" unless ($cookie =~ m/;$/); + $cookie .= " " unless ($cookie =~ m/ $/); + $cookie .= "Expires=Thu, 01 Jan 1970 00:00:00 GMT"; +} + +print "Set-Cookie: $cookie\n\n"; diff --git a/LayoutTests/http/tests/cookies/resources/cookie-utility.php b/LayoutTests/http/tests/cookies/resources/cookie-utility.php new file mode 100644 index 0000000..a33e7ac --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/cookie-utility.php @@ -0,0 +1,40 @@ +<?php +parse_str($_SERVER["QUERY_STRING"]); + +function deleteCookie($value, $name) +{ + setcookie($name, "deleted", time() - 86400, '/'); +} + +if ($queryfunction == "deleteCookies") { + array_walk($_COOKIE, deleteCookie); + echo "Deleted all cookies"; + return; +} + +if ($queryfunction == "setFooCookie") { + setcookie("foo", "awesomevalue", time() + 86400, '/'); + echo "Set the foo cookie"; + return; +} + +if ($queryfunction == "setFooAndBarCookie") { + setcookie("foo", "awesomevalue", time() + 86400, '/'); + setcookie("bar", "anotherawesomevalue", time() + 86400, '/'); + echo "Set the foo and bar cookies"; + return; +} + +// Default for any other string is echo cookies. +function echoCookie($value, $name) +{ + echo "$name = $value\n"; +} + +function echoAllCookies() +{ + echo "Cookies are:\n"; + array_walk($_COOKIE, echoCookie); +} + +?> diff --git a/LayoutTests/http/tests/cookies/resources/cookies-test-post.js b/LayoutTests/http/tests/cookies/resources/cookies-test-post.js new file mode 100644 index 0000000..6970120 --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/cookies-test-post.js @@ -0,0 +1,5 @@ +shouldBeTrue("successfullyParsed"); +debug('<br /><span class="pass">TEST COMPLETE</span>'); + +// Make sure that we do not leak any cookies. +clearCookies(); diff --git a/LayoutTests/http/tests/cookies/resources/cookies-test-pre.js b/LayoutTests/http/tests/cookies/resources/cookies-test-pre.js new file mode 100644 index 0000000..80b0ff1 --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/cookies-test-pre.js @@ -0,0 +1,239 @@ +if (window.layoutTestController) + layoutTestController.dumpAsText(); + +function description(msg) +{ + // For MSIE 6 compatibility + var span = document.createElement("span"); + span.innerHTML = '<p>' + msg + '</p><p>On success, you will see a series of "<span class="pass">PASS</span>" messages, followed by "<span class="pass">TEST COMPLETE</span>".</p>'; + var description = document.getElementById("description"); + if (description.firstChild) + description.replaceChild(span, description.firstChild); + else + description.appendChild(span); +} + +function debug(msg) +{ + var span = document.createElement("span"); + document.getElementById("console").appendChild(span); // insert it first so XHTML knows the namespace + span.innerHTML = msg + '<br />'; +} + +function escapeHTML(text) +{ + return text.replace(/&/g, "&").replace(/</g, "<"); +} + +function testPassed(msg) +{ + debug('<span><span class="pass">PASS</span> ' + escapeHTML(msg) + '</span>'); +} + +function testFailed(msg) +{ + debug('<span><span class="fail">FAIL</span> ' + escapeHTML(msg) + '</span>'); +} + +function areArraysEqual(_a, _b) +{ + if (_a.length !== _b.length) + return false; + for (var i = 0; i < _a.length; i++) + if (_a[i] !== _b[i]) + return false; + return true; +} + +function isMinusZero(n) +{ + // the only way to tell 0 from -0 in JS is the fact that 1/-0 is + // -Infinity instead of Infinity + return n === 0 && 1/n < 0; +} + +function isResultCorrect(_actual, _expected) +{ + if (_expected === 0) + return _actual === _expected && (1/_actual) === (1/_expected); + if (_actual === _expected) + return true; + if (typeof(_expected) == "number" && isNaN(_expected)) + return typeof(_actual) == "number" && isNaN(_actual); + if (Object.prototype.toString.call(_expected) == Object.prototype.toString.call([])) + return areArraysEqual(_actual, _expected); + return false; +} + +function stringify(v) +{ + if (v === 0 && 1/v < 0) + return "-0"; + else return "" + v; +} + +function shouldBe(_a, _b) +{ + if (typeof _a != "string" || typeof _b != "string") + debug("WARN: shouldBe() expects string arguments"); + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + var _bv = eval(_b); + + if (exception) + testFailed(_a + " should be " + _bv + ". Threw exception " + exception); + else if (isResultCorrect(_av, _bv)) + testPassed(_a + " is " + _b); + else if (typeof(_av) == typeof(_bv)) + testFailed(_a + " should be " + _bv + ". Was " + stringify(_av) + "."); + else + testFailed(_a + " should be " + _bv + " (of type " + typeof _bv + "). Was " + _av + " (of type " + typeof _av + ")."); +} + +function shouldBeTrue(_a) { shouldBe(_a, "true"); } +function shouldBeFalse(_a) { shouldBe(_a, "false"); } +function shouldBeNaN(_a) { shouldBe(_a, "NaN"); } +function shouldBeNull(_a) { shouldBe(_a, "null"); } + +function shouldBeEqualToString(a, b) +{ + var unevaledString = '"' + b.replace(/"/g, "\"") + '"'; + shouldBe(a, unevaledString); +} + +function shouldBeUndefined(_a) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + if (exception) + testFailed(_a + " should be undefined. Threw exception " + exception); + else if (typeof _av == "undefined") + testPassed(_a + " is undefined."); + else + testFailed(_a + " should be undefined. Was " + _av); +} + +function shouldThrow(_a, _e) +{ + var exception; + var _av; + try { + _av = eval(_a); + } catch (e) { + exception = e; + } + + var _ev; + if (_e) + _ev = eval(_e); + + if (exception) { + if (typeof _e == "undefined" || exception == _ev) + testPassed(_a + " threw exception " + exception + "."); + else + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Threw exception " + exception + "."); + } else if (typeof _av == "undefined") + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was undefined."); + else + testFailed(_a + " should throw " + (typeof _e == "undefined" ? "an exception" : _ev) + ". Was " + _av + "."); +} + +var cookies = new Array(); + +// This method sets the cookies using XMLHttpRequest. +// We do not set the cookie right away as it is forbidden by the XHR spec. +// FIXME: Add the possibility to set multiple cookies in a row. +function setCookies(cookie) +{ + try { + var xhr = new XMLHttpRequest(); + xhr.open("GET", "resources/setCookies.cgi", false); + xhr.setRequestHeader("SET_COOKIE", cookie); + xhr.send(null); + if (xhr.status == 200) { + // This is to clear them later. + cookies.push(cookie); + return true; + } else + return false; + } catch (e) { + return false; + } +} + +// Normalize a cookie string +function normalizeCookie(cookie) +{ + // Split the cookie string, sort it and then put it back together. + return cookie.split('; ').sort().join('; '); +} + +// We get the cookies throught an XMLHttpRequest. +function testCookies(result) +{ + var xhr = new XMLHttpRequest(); + xhr.open("GET", "resources/getCookies.cgi", false); + xhr.send(null); + var cookie = xhr.getResponseHeader("HTTP_COOKIE") == null ? '"null"' : xhr.getResponseHeader("HTTP_COOKIE"); + + // Normalize the cookie strings. + result = normalizeCookie(result); + cookie = normalizeCookie(cookie); + + if (cookie === result) + testPassed("cookie is '" + cookie + "'."); + else + testFailed("cookie was '" + cookie + "'. Expected '" + result + "'."); +} + +function clearAllCookies() +{ + var cookieString; + while (cookieString = document.cookie) { + var cookieName = cookieString.substr(0, cookieString.indexOf("=") || cookieString.length()); + cookies.push(cookieName); + clearCookies(); + } +} + +function clearCookies() +{ + if (!cookies.length) + return; + + try { + var xhr = new XMLHttpRequest(); + var cookie; + // We need to clean one cookie at a time because to be cleared the + // cookie must be exactly the same except for the "Max-Age" + // and "Expires" fields. + while (cookie = cookies.pop()) { + xhr.open("GET", "resources/clearCookies.cgi", false); + xhr.setRequestHeader("CLEAR_COOKIE", cookie); + xhr.send(null); + } + } catch (e) { + debug("Could not clear the cookies expect the following results to fail"); + } +} + +// This method check one cookie at a time. +function cookiesShouldBe(cookiesToSet, result) +{ + if (!setCookies(cookiesToSet)) { + testFailed("could not set cookie(s) " + cookiesToSet); + return; + } + testCookies(result); +} diff --git a/LayoutTests/http/tests/cookies/resources/cookies-test-style.css b/LayoutTests/http/tests/cookies/resources/cookies-test-style.css new file mode 100644 index 0000000..f12147c --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/cookies-test-style.css @@ -0,0 +1,12 @@ +.pass { + font-weight: bold; + color: green; +} +.fail { + font-weight: bold; + color: red; +} +#console { + white-space: pre-wrap; + font-family: monospace; +} diff --git a/LayoutTests/http/tests/cookies/resources/getCookies.cgi b/LayoutTests/http/tests/cookies/resources/getCookies.cgi new file mode 100755 index 0000000..c4377c3 --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/getCookies.cgi @@ -0,0 +1,8 @@ +#!/usr/bin/perl -wT +use strict; + +print "Content-Type: text/plain\n"; +print "Cache-Control: no-store\n"; +print 'Cache-Control: no-cache="set-cookie"' . "\n"; + +print "HTTP_COOKIE: " . ($ENV{HTTP_COOKIE} || "") . "\n\n"; diff --git a/LayoutTests/http/tests/cookies/resources/resetCookies.js b/LayoutTests/http/tests/cookies/resources/resetCookies.js new file mode 100644 index 0000000..577822a --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/resetCookies.js @@ -0,0 +1,18 @@ +function resetCookies() +{ + if (window.layoutTestController) + layoutTestController.setAlwaysAcceptCookies(true); + + // Due to cross-origin restrictions, we can only (simply) reset cookies for our current origin. + var url = "http://" + window.location.hostname +":8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies"; + var req = new XMLHttpRequest(); + try { + req.open('GET', url, false); + req.send(); + } catch (e) { + alert("Attempt to clear " + url + " cookies might have failed. Test results might be off from here on out. (" + e + ")"); + } + + if (window.layoutTestController) + layoutTestController.setAlwaysAcceptCookies(false); +} diff --git a/LayoutTests/http/tests/cookies/resources/setCookies.cgi b/LayoutTests/http/tests/cookies/resources/setCookies.cgi new file mode 100755 index 0000000..b9c4b21 --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/setCookies.cgi @@ -0,0 +1,9 @@ +#!/usr/bin/perl -wT +use strict; + +print "Content-Type: text/plain\n"; +print "Cache-Control: no-store\n"; +print 'Cache-Control: no-cache="set-cookie"' . "\n"; + +# We only map the SET_COOKIE request header to "Set-Cookie" +print "Set-Cookie: " . $ENV{"HTTP_SET_COOKIE"} . "\n\n"; diff --git a/LayoutTests/http/tests/cookies/resources/third-party-cookie-relaxing-iframe.html b/LayoutTests/http/tests/cookies/resources/third-party-cookie-relaxing-iframe.html new file mode 100644 index 0000000..d16964e --- /dev/null +++ b/LayoutTests/http/tests/cookies/resources/third-party-cookie-relaxing-iframe.html @@ -0,0 +1,59 @@ +<html> +<script src="resetCookies.js"></script> +<script> +resetCookies(); + +if (window.layoutTestController) + layoutTestController.dumpAsText(); + +window.onmessage = function(evt) +{ + if (evt.data == "showCookies") { + showCookies(); + return; + } else if (evt.data.split(" ")[0] == "sendXHR") { + sendXHR(evt.data.split(" ")[1]); + return; + } else if (evt.data == "resetCookiesAndNotifyDone") { + resetCookiesAndNotifyDone(); + return; + } else + alert("Unknown message."); +} + +var stage = 1; +function showCookies() +{ + alert("Test stage " + stage++ + " document.cookie is: " + document.cookie); + parent.window.postMessage("done", "*"); +} + +function sendXHR(queryCommand) +{ + var baseurl = "http://localhost:8000/cookies/resources/cookie-utility.php"; + var url = queryCommand ? baseurl + "?queryfunction=" + queryCommand : baseurl; + alert(url); + var req = new XMLHttpRequest(); + req.open('GET', url, false); + req.send(); + + if (req.status == 200) + alert("XHR response - " + req.responseText); + else + alert("xhr error"); + + parent.window.postMessage("done", "*"); +} + +function resetCookiesAndNotifyDone() +{ + resetCookies(); + if (window.layoutTestController) + layoutTestController.notifyDone(); +} + +</script> +<body> +HELLO THERE +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/script-tests/TEMPLATE.html b/LayoutTests/http/tests/cookies/script-tests/TEMPLATE.html new file mode 100644 index 0000000..6483f50 --- /dev/null +++ b/LayoutTests/http/tests/cookies/script-tests/TEMPLATE.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="resources/cookies-test-style.css"> +<script src="resources/cookies-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="YOUR_JS_FILE_HERE"></script> +<script src="resources/cookies-test-post.js"></script> +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/script-tests/double-quoted-value-with-semi-colon.js b/LayoutTests/http/tests/cookies/script-tests/double-quoted-value-with-semi-colon.js new file mode 100644 index 0000000..9029875 --- /dev/null +++ b/LayoutTests/http/tests/cookies/script-tests/double-quoted-value-with-semi-colon.js @@ -0,0 +1,11 @@ +description( +'Test for <<a href="rdar://problem/5666078">rdar://problem/5666078</a>> Cookie parsing terminates at the first semicolon, ignoring quotes (<a href="https://bugs.webkit.org/show_bug.cgi?id=16699">16699</a>)' +); + +clearAllCookies(); + +debug("Check that setting a cookie with a semi-colon in a duoble-quoted value works"); +cookiesShouldBe('disorder="477beccb;richard";Version=1;Path=/', 'disorder="477beccb;richard"'); +clearCookies(); + +successfullyParsed = true; diff --git a/LayoutTests/http/tests/cookies/script-tests/multiple-cookies.js b/LayoutTests/http/tests/cookies/script-tests/multiple-cookies.js new file mode 100644 index 0000000..618e224 --- /dev/null +++ b/LayoutTests/http/tests/cookies/script-tests/multiple-cookies.js @@ -0,0 +1,13 @@ +description( +"This test checks that mulitple cookies are correctly set." +); + +clearAllCookies(); + +debug("Check setting several cookies without clearing."); +cookiesShouldBe("test=foobar;", "test=foobar"); +cookiesShouldBe("test2=foobar;", "test=foobar; test2=foobar"); +cookiesShouldBe("test3=foobar;", "test=foobar; test2=foobar; test3=foobar"); +clearCookies(); + +successfullyParsed = true; diff --git a/LayoutTests/http/tests/cookies/script-tests/simple-cookies-expired.js b/LayoutTests/http/tests/cookies/script-tests/simple-cookies-expired.js new file mode 100644 index 0000000..61b70fb --- /dev/null +++ b/LayoutTests/http/tests/cookies/script-tests/simple-cookies-expired.js @@ -0,0 +1,18 @@ +description( +"This test checks that cookies are correctly set using Expires." +); + +clearAllCookies(); + +debug("Check that setting a simple cookie works."); +var date = new Date(); +date.setTime(date.getTime() + 60 * 1000); +cookiesShouldBe("test=foobar; Expires=" + date.toGMTString(), "test=foobar"); +clearCookies(); + +debug("Check setting a cookie that timed out."); +date.setTime(date.getTime() - 2 * 60 * 1000); +cookiesShouldBe("test2=foobar; Expires=" + date.toGMTString(), ""); +clearCookies(); + +successfullyParsed = true; diff --git a/LayoutTests/http/tests/cookies/script-tests/simple-cookies-max-age.js b/LayoutTests/http/tests/cookies/script-tests/simple-cookies-max-age.js new file mode 100644 index 0000000..1d87b13 --- /dev/null +++ b/LayoutTests/http/tests/cookies/script-tests/simple-cookies-max-age.js @@ -0,0 +1,15 @@ +description( +"This test checks that cookies are correctly set using Max-Age." +); + +clearAllCookies(); + +debug("Check that setting a simple cookie works."); +cookiesShouldBe("test=foobar; Max-Age=90000000", "test=foobar"); +clearCookies(); + +debug("Check setting a cookie that timed out."); +cookiesShouldBe("test2=foobar; Max-Age=0", ""); +clearCookies(); + +successfullyParsed = true; diff --git a/LayoutTests/http/tests/cookies/simple-cookies-expired-expected.txt b/LayoutTests/http/tests/cookies/simple-cookies-expired-expected.txt new file mode 100644 index 0000000..d0a5c55 --- /dev/null +++ b/LayoutTests/http/tests/cookies/simple-cookies-expired-expected.txt @@ -0,0 +1,13 @@ +This test checks that cookies are correctly set using Expires. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +Check that setting a simple cookie works. +PASS cookie is 'test=foobar'. +Check setting a cookie that timed out. +PASS cookie is ''. +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/http/tests/cookies/simple-cookies-expired.html b/LayoutTests/http/tests/cookies/simple-cookies-expired.html new file mode 100644 index 0000000..6015d0d --- /dev/null +++ b/LayoutTests/http/tests/cookies/simple-cookies-expired.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="resources/cookies-test-style.css"> +<script src="resources/cookies-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="script-tests/simple-cookies-expired.js"></script> +<script src="resources/cookies-test-post.js"></script> +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/simple-cookies-max-age-expected.txt b/LayoutTests/http/tests/cookies/simple-cookies-max-age-expected.txt new file mode 100644 index 0000000..859c392 --- /dev/null +++ b/LayoutTests/http/tests/cookies/simple-cookies-max-age-expected.txt @@ -0,0 +1,13 @@ +This test checks that cookies are correctly set using Max-Age. + +On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". + + +Check that setting a simple cookie works. +PASS cookie is 'test=foobar'. +Check setting a cookie that timed out. +PASS cookie is ''. +PASS successfullyParsed is true + +TEST COMPLETE + diff --git a/LayoutTests/http/tests/cookies/simple-cookies-max-age.html b/LayoutTests/http/tests/cookies/simple-cookies-max-age.html new file mode 100644 index 0000000..23eee96 --- /dev/null +++ b/LayoutTests/http/tests/cookies/simple-cookies-max-age.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> +<head> +<link rel="stylesheet" href="resources/cookies-test-style.css"> +<script src="resources/cookies-test-pre.js"></script> +</head> +<body> +<p id="description"></p> +<div id="console"></div> +<script src="script-tests/simple-cookies-max-age.js"></script> +<script src="resources/cookies-test-post.js"></script> +</body> +</html> diff --git a/LayoutTests/http/tests/cookies/third-party-cookie-relaxing-expected.txt b/LayoutTests/http/tests/cookies/third-party-cookie-relaxing-expected.txt new file mode 100644 index 0000000..184a97a --- /dev/null +++ b/LayoutTests/http/tests/cookies/third-party-cookie-relaxing-expected.txt @@ -0,0 +1,47 @@ +ALERT: + +ALERT: Allowing all cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies +ALERT: XHR response - Deleted all cookies +ALERT: Test stage 1 document.cookie is: +ALERT: Restricting to first party only cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie +ALERT: XHR response - Set the foo cookie +ALERT: Test stage 2 document.cookie is: +ALERT: + +ALERT: Allowing all cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies +ALERT: XHR response - Deleted all cookies +ALERT: Test stage 3 document.cookie is: +ALERT: Restricting to first party only cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooAndBarCookie +ALERT: XHR response - Set the foo and bar cookies +ALERT: Test stage 4 document.cookie is: +ALERT: + +ALERT: Allowing all cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies +ALERT: XHR response - Deleted all cookies +ALERT: Test stage 5 document.cookie is: +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie +ALERT: XHR response - Set the foo cookie +ALERT: Test stage 6 document.cookie is: foo=awesomevalue +ALERT: Restricting to first party only cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies +ALERT: XHR response - Deleted all cookies +ALERT: Test stage 7 document.cookie is: +ALERT: + +ALERT: Allowing all cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=deleteCookies +ALERT: XHR response - Deleted all cookies +ALERT: Test stage 8 document.cookie is: +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooCookie +ALERT: XHR response - Set the foo cookie +ALERT: Test stage 9 document.cookie is: foo=awesomevalue +ALERT: Restricting to first party only cookies +ALERT: http://localhost:8000/cookies/resources/cookie-utility.php?queryfunction=setFooAndBarCookie +ALERT: XHR response - Set the foo and bar cookies +ALERT: Test stage 10 document.cookie is: bar=anotherawesomevalue; foo=awesomevalue + diff --git a/LayoutTests/http/tests/cookies/third-party-cookie-relaxing.html b/LayoutTests/http/tests/cookies/third-party-cookie-relaxing.html new file mode 100644 index 0000000..df9c4b9 --- /dev/null +++ b/LayoutTests/http/tests/cookies/third-party-cookie-relaxing.html @@ -0,0 +1,96 @@ +<html> +<head> +<script src="resources/resetCookies.js"></script> +<script> +resetCookies(); + +if (window.layoutTestController) { + layoutTestController.dumpAsText(); + layoutTestController.waitUntilDone(); +} + +window.onmessage = function(evt) +{ + if (evt.data != "done") { + alert("Unexpected message: " + evt.data); + return; + } + + runNextTestOrFinish(); +} + +function allowAllCookies() +{ + alert("Allowing all cookies"); + if (window.layoutTestController) + layoutTestController.setAlwaysAcceptCookies(true); + runNextTestOrFinish(); +} + +function restrictCookies() +{ + alert("Restricting to first party only cookies"); + if (window.layoutTestController) + layoutTestController.setAlwaysAcceptCookies(false); + runNextTestOrFinish(); +} + +function deleteAllCookies() +{ + sendXHR("deleteCookies"); +} + +function echoCookies() +{ + window.frames[0].postMessage("showCookies", "*"); +} + +function sendXHR(command) +{ + window.frames[0].postMessage("sendXHR " + command, "*"); +} + +function setFooCookie() +{ + sendXHR("setFooCookie"); +} + +function setFooAndBarCookies() +{ + sendXHR("setFooAndBarCookie"); +} + +function startNewTest() +{ + alert("\n"); + runNextTestOrFinish(); +} + +var currentFunction = 0; +var functions = new Array( + startNewTest, allowAllCookies, deleteAllCookies, echoCookies, restrictCookies, setFooCookie, echoCookies, + startNewTest, allowAllCookies, deleteAllCookies, echoCookies, restrictCookies, setFooAndBarCookies, echoCookies, + startNewTest, allowAllCookies, deleteAllCookies, echoCookies, setFooCookie, echoCookies, restrictCookies, deleteAllCookies, echoCookies, + startNewTest, allowAllCookies, deleteAllCookies, echoCookies, setFooCookie, echoCookies, restrictCookies, setFooAndBarCookies, echoCookies +); + +function runNextTestOrFinish() +{ + if (currentFunction >= functions.length) { + if (window.layoutTestController) { + resetCookies(); + window.frames[0].postMessage("resetCookiesAndNotifyDone", "*"); + } + return; + } + + var functionToRun = currentFunction++; + functions[functionToRun](); +} + +</script> +</head> +<body onload="runNextTestOrFinish();"> +<iframe id='testFrame' src="http://localhost:8000/cookies/resources/third-party-cookie-relaxing-iframe.html"></iframe> +</body> +</html> diff --git a/LayoutTests/platform/android/layout_test_directories.txt b/LayoutTests/platform/android/layout_test_directories.txt index da81ffd..703c4da 100755 --- a/LayoutTests/platform/android/layout_test_directories.txt +++ b/LayoutTests/platform/android/layout_test_directories.txt @@ -36,6 +36,7 @@ fast/url fast/xpath http/conf http/tests/appcache +http/tests/cookies http/tests/resources http/tests/ssl platform/android diff --git a/LayoutTests/platform/android/test_expectations.txt b/LayoutTests/platform/android/test_expectations.txt index 375a595..d0a039c 100644 --- a/LayoutTests/platform/android/test_expectations.txt +++ b/LayoutTests/platform/android/test_expectations.txt @@ -66,6 +66,7 @@ fast/events/touch/basic-multi-touch-events.html FAIL // Requires multi-touch ges fast/events/touch/touch-coords-in-zoom-and-scroll.html FAIL // Requires eventSender.zoomPageIn(),zoomPageOut() fast/events/touch/touch-target.html FAIL // Requires multi-touch gestures not supported by Android system fast/workers FAIL // workers not supported +http/tests/cookies/third-party-cookie-relaxing.html FAIL // We don't support conditional acceptance of third-party cookies http/tests/eventsource/workers FAIL // workers not supported http/tests/workers FAIL // workers not supported http/tests/xmlhttprequest/workers FAIL // workers not supported diff --git a/WebCore/bridge/jni/v8/JavaClassV8.cpp b/WebCore/bridge/jni/v8/JavaClassV8.cpp index 04f8822..1d381af 100644 --- a/WebCore/bridge/jni/v8/JavaClassV8.cpp +++ b/WebCore/bridge/jni/v8/JavaClassV8.cpp @@ -26,7 +26,6 @@ #include "config.h" #include "JavaClassV8.h" - using namespace JSC::Bindings; JavaClass::JavaClass(jobject anInstance) @@ -38,11 +37,6 @@ JavaClass::JavaClass(jobject anInstance) return; } - jstring className = static_cast<jstring>(callJNIMethod<jobject>(aClass, "getName", "()Ljava/lang/String;")); - const char* classNameC = getCharactersFromJString(className); - m_name = strdup(classNameC); - releaseCharactersForJString(className, classNameC); - int i; JNIEnv* env = getJNIEnv(); @@ -82,8 +76,6 @@ JavaClass::JavaClass(jobject anInstance) JavaClass::~JavaClass() { - free(const_cast<char*>(m_name)); - deleteAllValues(m_fields); m_fields.clear(); diff --git a/WebCore/bridge/jni/v8/JavaClassV8.h b/WebCore/bridge/jni/v8/JavaClassV8.h index 0c1d627..99137f1 100644 --- a/WebCore/bridge/jni/v8/JavaClassV8.h +++ b/WebCore/bridge/jni/v8/JavaClassV8.h @@ -49,7 +49,6 @@ public: JavaField* fieldNamed(const char* name) const; private: - const char* m_name; MethodListMap m_methods; FieldMap m_fields; }; diff --git a/WebCore/dom/Element.cpp b/WebCore/dom/Element.cpp index be74487..9afde07 100644 --- a/WebCore/dom/Element.cpp +++ b/WebCore/dom/Element.cpp @@ -973,7 +973,7 @@ void Element::recalcStyle(StyleChange change) if ((change > NoChange || needsStyleRecalc())) { #ifdef ANDROID_STYLE_VERSION - RefPtr<RenderStyle> newStyle = document()->styleSelector()->styleForElement(this); + RefPtr<RenderStyle> newStyle = document()->styleForElementIgnoringPendingStylesheets(this); if (displayDiff(currentStyle.get(), newStyle.get())) document()->incStyleVersion(); #endif diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 2805b21..1148aa6 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -268,10 +268,6 @@ bool BitmapImage::shouldAnimate() void BitmapImage::startAnimation(bool catchUpIfNecessary) { -#ifdef ANDROID_ANIMATED_GIF - // We can't ever seem to keep up, so always let us just show the next frame - catchUpIfNecessary = false; -#endif if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) return; diff --git a/WebKit/Android.mk b/WebKit/Android.mk index 844d8a5..e039421 100644 --- a/WebKit/Android.mk +++ b/WebKit/Android.mk @@ -28,6 +28,7 @@ LOCAL_SRC_FILES := \ android/WebCoreSupport/GeolocationPermissions.cpp \ android/WebCoreSupport/MediaPlayerPrivateAndroid.cpp \ android/WebCoreSupport/PlatformBridge.cpp \ + android/WebCoreSupport/ResourceLoaderAndroid.cpp \ android/WebCoreSupport/UrlInterceptResponse.cpp \ android/WebCoreSupport/V8Counters.cpp @@ -35,6 +36,7 @@ ifeq ($(HTTP_STACK),chrome) LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ android/WebCoreSupport/ChromiumLogging.cpp \ android/WebCoreSupport/WebCache.cpp \ + android/WebCoreSupport/WebCookieJar.cpp \ android/WebCoreSupport/WebUrlLoader.cpp \ android/WebCoreSupport/WebUrlLoaderClient.cpp \ android/WebCoreSupport/WebRequest.cpp \ @@ -42,9 +44,6 @@ LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ android/WebCoreSupport/WebResourceRequest.cpp \ android/WebCoreSupport/WebResponse.cpp \ android/WebCoreSupport/WebViewClientError.cpp -else -LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ - android/WebCoreSupport/ResourceLoaderAndroid.cpp endif # HTTP_STACK == chrome LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \ diff --git a/WebKit/android/WebCoreSupport/PlatformBridge.cpp b/WebKit/android/WebCoreSupport/PlatformBridge.cpp index 1b249a4..cd5088b 100644 --- a/WebKit/android/WebCoreSupport/PlatformBridge.cpp +++ b/WebKit/android/WebCoreSupport/PlatformBridge.cpp @@ -34,6 +34,7 @@ #include "KeyGeneratorClient.h" #include "PluginView.h" #include "Settings.h" +#include "WebCookieJar.h" #include "WebRequestContext.h" #include "WebViewCore.h" #include "npruntime.h" @@ -72,7 +73,7 @@ void PlatformBridge::setCookies(const Document* document, const KURL& url, const std::string cookieValue(value.utf8().data()); GURL cookieGurl(url.string().utf8().data()); bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); - WebRequestContext::get(isPrivateBrowsing)->cookie_store()->SetCookie(cookieGurl, cookieValue); + WebCookieJar::get(isPrivateBrowsing)->cookieStore()->SetCookie(cookieGurl, cookieValue); #else CookieClient* client = JavaSharedClient::GetCookieClient(); if (!client) @@ -87,7 +88,7 @@ String PlatformBridge::cookies(const Document* document, const KURL& url) #if USE(CHROME_NETWORK_STACK) GURL cookieGurl(url.string().utf8().data()); bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); - std::string cookies = WebRequestContext::get(isPrivateBrowsing)->cookie_store()->GetCookies(cookieGurl); + std::string cookies = WebCookieJar::get(isPrivateBrowsing)->cookieStore()->GetCookies(cookieGurl); String cookieString(cookies.c_str()); return cookieString; #else @@ -103,7 +104,7 @@ bool PlatformBridge::cookiesEnabled(const Document* document) { #if USE(CHROME_NETWORK_STACK) bool isPrivateBrowsing = document->settings() && document->settings()->privateBrowsingEnabled(); - return WebRequestContext::get(isPrivateBrowsing)->allowCookies(); + return WebCookieJar::get(isPrivateBrowsing)->allowCookies(); #else CookieClient* client = JavaSharedClient::GetCookieClient(); if (!client) @@ -160,11 +161,11 @@ FloatRect PlatformBridge::screenRect() String PlatformBridge::computeDefaultLanguage() { #if USE(CHROME_NETWORK_STACK) - std::string acceptLanguages = WebRequestContext::get(false)->GetAcceptLanguage(); + String acceptLanguages = WebRequestContext::acceptLanguage(); size_t length = acceptLanguages.find(','); if (length == std::string::npos) length = acceptLanguages.length(); - return String::fromUTF8(acceptLanguages.c_str(), length); + return acceptLanguages.substring(0, length); #else return "en"; #endif diff --git a/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp b/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp index 8872a52..2b4a6fc 100644 --- a/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp +++ b/WebKit/android/WebCoreSupport/ResourceLoaderAndroid.cpp @@ -24,27 +24,45 @@ */ #include <config.h> - #include <ResourceLoaderAndroid.h> #include "FrameLoaderClientAndroid.h" #include "WebCoreFrameBridge.h" #include "WebCoreResourceLoader.h" +#include "WebUrlLoader.h" using namespace android; namespace WebCore { PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start( - ResourceHandle* handle, const ResourceRequest& request, FrameLoaderClient* client, bool isMainResource, bool isSync, bool /*isPrivateBrowsing*/) + ResourceHandle* handle, const ResourceRequest& request, FrameLoaderClient* client, bool isMainResource, bool isSync, bool isPrivateBrowsing) { + // Called on main thread +#if USE(CHROME_NETWORK_STACK) + // TODO: Implement sync requests + return WebUrlLoader::start(client, handle, request, isSync, isPrivateBrowsing); +#else FrameLoaderClientAndroid* clientAndroid = static_cast<FrameLoaderClientAndroid*> (client); return clientAndroid->webFrame()->startLoadingResource(handle, request, isMainResource, isSync); +#endif } bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL& url, int64_t identifier) { +#if USE(CHROME_NETWORK_STACK) + // This method is used to determine if a POST request can be repeated from + // cache, but you cannot really know until you actually try to read from the + // cache. Even if we checked now, something else could come along and wipe + // out the cache entry by the time we fetch it. + // + // So, we always say yes here, to prevent the FrameLoader from initiating a + // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we + // fix-up the cache policy of the request to force a load from the cache. + return true; +#else return WebCoreResourceLoader::willLoadFromCache(url, identifier); +#endif } } diff --git a/WebKit/android/WebCoreSupport/WebCache.cpp b/WebKit/android/WebCoreSupport/WebCache.cpp index b0ae915..9bc148c 100644 --- a/WebKit/android/WebCoreSupport/WebCache.cpp +++ b/WebKit/android/WebCoreSupport/WebCache.cpp @@ -23,37 +23,95 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include "WebCache.h" + +#include "JNIUtility.h" +#include "WebCoreJni.h" #include "WebRequestContext.h" #include "WebUrlLoaderClient.h" +using namespace WTF; using namespace net; namespace android { -WebCache* WebCache::s_instance = 0; +static const std::string& rootDirectory() +{ + // This method may be called on any thread, as the Java method is + // synchronized. + static WTF::Mutex mutex; + MutexLocker lock(mutex); + static std::string cacheDirectory; + if (cacheDirectory.empty()) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); + jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;"); + cacheDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); + env->DeleteLocalRef(bridgeClass); + } + return cacheDirectory; +} -void WebCache::clear() +WebCache* WebCache::get(bool isPrivateBrowsing) { - base::Thread* thread = WebUrlLoaderClient::ioThread(); - if (thread) - thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(getInstance(), &WebCache::doClear)); + static const char* const kDirectory = "/webviewCacheChromium"; + static const char* const kDirectoryPrivate = "/webviewCacheChromiumPrivate"; + + static WebCache* regularCache = 0; + static WebCache* privateCache = 0; + + if (isPrivateBrowsing) { + if (!privateCache) { + std::string storageDirectory = rootDirectory(); + storageDirectory.append(kDirectoryPrivate); + privateCache = new WebCache(storageDirectory); + } + return privateCache; + } + + if (!regularCache) { + std::string storageDirectory = rootDirectory(); + storageDirectory.append(kDirectory); + regularCache = new WebCache(storageDirectory); + } + return regularCache; } -WebCache::WebCache() - : m_doomAllEntriesCallback(this, &WebCache::doomAllEntries) - , m_doneCallback(this, &WebCache::done) +WebCache::WebCache(const std::string& storageDirectory) + : m_storageDirectory(storageDirectory) + , m_doomAllEntriesCallback(this, &WebCache::doomAllEntries) + , m_doneCallback(this, &WebCache::onClearDone) , m_isClearInProgress(false) { + base::Thread* ioThread = WebUrlLoaderClient::ioThread(); + scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy(); + + static const int kMaximumCacheSizeBytes = 20 * 1024 * 1024; + FilePath directoryPath(m_storageDirectory.c_str()); + net::HttpCache::DefaultBackend* backendFactory = new net::HttpCache::DefaultBackend(net::DISK_CACHE, directoryPath, kMaximumCacheSizeBytes, cacheMessageLoopProxy); + + m_hostResolver = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0); + m_cache = new net::HttpCache(m_hostResolver.get(), + 0, // dnsrr_resolver + net::ProxyService::CreateDirect(), + net::SSLConfigService::CreateSystemSSLConfigService(), + net::HttpAuthHandlerFactory::CreateDefault(m_hostResolver.get()), + 0, // network_delegate + 0, // net_log + backendFactory); } -WebCache* WebCache::getInstance() +void WebCache::clear() { - if (!s_instance) { - s_instance = new WebCache(); - s_instance->AddRef(); - } - return s_instance; + base::Thread* thread = WebUrlLoaderClient::ioThread(); + if (thread) + thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &WebCache::doClear)); +} + +void WebCache::cleanupFiles() +{ + WebRequestContext::removeFileOrDirectory(m_storageDirectory.c_str()); } void WebCache::doClear() @@ -61,9 +119,8 @@ void WebCache::doClear() if (m_isClearInProgress) return; m_isClearInProgress = true; - URLRequestContext* context = WebRequestContext::get(false /* isPrivateBrowsing */); - HttpTransactionFactory* factory = context->http_transaction_factory(); - int code = factory->GetCache()->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback); + + int code = m_cache->GetBackend(&m_cacheBackend, &m_doomAllEntriesCallback); // Code ERR_IO_PENDING indicates that the operation is still in progress and // the supplied callback will be invoked when it completes. if (code == ERR_IO_PENDING) @@ -91,10 +148,10 @@ void WebCache::doomAllEntries(int) m_isClearInProgress = false; return; } - done(0 /*unused*/); + onClearDone(0 /*unused*/); } -void WebCache::done(int) +void WebCache::onClearDone(int) { m_isClearInProgress = false; } diff --git a/WebKit/android/WebCoreSupport/WebCache.h b/WebKit/android/WebCoreSupport/WebCache.h index 9fd980f..4aa67c0 100644 --- a/WebKit/android/WebCoreSupport/WebCache.h +++ b/WebKit/android/WebCoreSupport/WebCache.h @@ -28,21 +28,35 @@ #include "ChromiumIncludes.h" +#include <OwnPtr.h> +#include <wtf/ThreadingPrimitives.h> + namespace android { +// This class is not generally threadsafe. get() is not threadsafe - instances +// are created on the WebCore thread only. class WebCache : public base::RefCountedThreadSafe<WebCache> { public: - static void clear(); + static WebCache* get(bool isPrivateBrowsing); + + void clear(); + void cleanupFiles(); + net::HostResolver* hostResolver() { return m_hostResolver.get(); } + net::HttpCache* cache() { return m_cache.get(); } private: - WebCache(); - static WebCache* getInstance(); + WebCache(const std::string& storageDirectory); + // For clear() void doClear(); void doomAllEntries(int); - void done(int); + void onClearDone(int); + + std::string m_storageDirectory; + OwnPtr<net::HostResolver> m_hostResolver; + OwnPtr<net::HttpCache> m_cache; - static WebCache* s_instance; + // For clear() net::CompletionCallbackImpl<WebCache> m_doomAllEntriesCallback; net::CompletionCallbackImpl<WebCache> m_doneCallback; disk_cache::Backend* m_cacheBackend; diff --git a/WebKit/android/WebCoreSupport/WebCookieJar.cpp b/WebKit/android/WebCoreSupport/WebCookieJar.cpp new file mode 100644 index 0000000..91a565c --- /dev/null +++ b/WebKit/android/WebCoreSupport/WebCookieJar.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "WebCookieJar.h" + +#include "JNIUtility.h" +#include "WebCoreJni.h" +#include "WebRequestContext.h" + +namespace android { + +const std::string& databaseDirectory() +{ + // This method may be called on any thread, as the Java method is + // synchronized. + static WTF::Mutex databaseDirectoryMutex; + MutexLocker lock(databaseDirectoryMutex); + static std::string databaseDirectory; + if (databaseDirectory.empty()) { + JNIEnv* env = JSC::Bindings::getJNIEnv(); + jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); + jmethodID method = env->GetStaticMethodID(bridgeClass, "getDatabaseDirectory", "()Ljava/lang/String;"); + databaseDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); + env->DeleteLocalRef(bridgeClass); + } + return databaseDirectory; +} + + +WebCookieJar* WebCookieJar::get(bool isPrivateBrowsing) +{ + static const char* const kDatabaseFilename = "/webviewCookiesChromium.db"; + static const char* const kDatabaseFilenamePrivateBrowsing = "/webviewCookiesChromiumPrivate.db"; + + static WebCookieJar* regularCookieManager = 0; + static WebCookieJar* privateCookieManager = 0; + + WTF::Mutex instanceMutex; + MutexLocker lock(instanceMutex); + + if (isPrivateBrowsing) { + if (!privateCookieManager) { + std::string databaseFilePath = databaseDirectory(); + databaseFilePath.append(kDatabaseFilenamePrivateBrowsing); + privateCookieManager = new WebCookieJar(databaseFilePath); + } + return privateCookieManager; + } + + if (!regularCookieManager) { + std::string databaseFilePath = databaseDirectory(); + databaseFilePath.append(kDatabaseFilename); + regularCookieManager = new WebCookieJar(databaseFilePath); + } + return regularCookieManager; +} + +WebCookieJar::WebCookieJar(const std::string& databaseFilePath) + : m_allowCookies(true) + , m_databaseFilePath(databaseFilePath) +{ + // This is needed for the page cycler. See http://b/2944150 + net::CookieMonster::EnableFileScheme(); + + FilePath cookiePath(m_databaseFilePath.c_str()); + scoped_refptr<SQLitePersistentCookieStore> cookieDb = new SQLitePersistentCookieStore(cookiePath); + m_cookieStore = new net::CookieMonster(cookieDb.get(), 0); +} + +bool WebCookieJar::allowCookies() +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies; +} + +void WebCookieJar::setAllowCookies(bool allow) +{ + MutexLocker lock(m_allowCookiesMutex); + m_allowCookies = allow; +} + +void WebCookieJar::cleanupFiles() +{ + WebRequestContext::removeFileOrDirectory(m_databaseFilePath.c_str()); +} + +// From CookiePolicy in chromium +int WebCookieJar::CanGetCookies(const GURL&, const GURL&, net::CompletionCallback*) +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; +} + +// From CookiePolicy in chromium +int WebCookieJar::CanSetCookie(const GURL&, const GURL&, const std::string&, net::CompletionCallback*) +{ + MutexLocker lock(m_allowCookiesMutex); + return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; +} + +} diff --git a/WebKit/android/WebCoreSupport/WebCookieJar.h b/WebKit/android/WebCoreSupport/WebCookieJar.h new file mode 100644 index 0000000..e3bfe02 --- /dev/null +++ b/WebKit/android/WebCoreSupport/WebCookieJar.h @@ -0,0 +1,65 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 WebCookieJar_h +#define WebCookieJar_h + +#include "ChromiumIncludes.h" + +#include <wtf/ThreadingPrimitives.h> + +namespace android { + +// This class is threadsafe. It is used from the IO, WebCore and Chromium IO +// threads. +class WebCookieJar : public net::CookiePolicy { +public: + static WebCookieJar* get(bool isPrivateBrowsing); + + // CookiePolicy implementation from external/chromium + virtual int CanGetCookies(const GURL& url, const GURL& first_party_for_cookies, net::CompletionCallback*); + virtual int CanSetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie_line, net::CompletionCallback*); + + bool allowCookies(); + void setAllowCookies(bool allow); + void cleanupFiles(); + + // Instead of this it would probably be better to add the cookie methods + // here so the rest of WebKit doesn't have to know about Chromium classes + net::CookieStore* cookieStore() { return m_cookieStore.get(); } + net::CookiePolicy* cookiePolicy() { return this; } + +private: + WebCookieJar(const std::string& databaseFilePath); + + scoped_refptr<net::CookieStore> m_cookieStore; + bool m_allowCookies; + WTF::Mutex m_allowCookiesMutex; + std::string m_databaseFilePath; +}; + +} + +#endif diff --git a/WebKit/android/WebCoreSupport/WebRequest.cpp b/WebKit/android/WebCoreSupport/WebRequest.cpp index 67144cb..366c3c9 100644 --- a/WebKit/android/WebCoreSupport/WebRequest.cpp +++ b/WebKit/android/WebCoreSupport/WebRequest.cpp @@ -51,7 +51,6 @@ extern android::AssetManager* globalAssetManager(); #define ASSERT(assertion, ...) do \ if (!(assertion)) { \ android_printLog(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \ - CRASH(); \ } \ while (0) @@ -67,6 +66,7 @@ WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& web , m_url(webResourceRequest.url()) , m_userAgent(webResourceRequest.userAgent()) , m_loadState(Created) + , m_authRequestCount(0) { GURL gurl(m_url); @@ -87,6 +87,7 @@ WebRequest::WebRequest(WebUrlLoaderClient* loader, const WebResourceRequest& web , m_url(webResourceRequest.url()) , m_userAgent(webResourceRequest.userAgent()) , m_loadState(Created) + , m_authRequestCount(0) { } @@ -311,8 +312,11 @@ void WebRequest::OnAuthRequired(URLRequest* request, net::AuthChallengeInfo* aut ASSERT(m_loadState == Started, "OnAuthRequired called on a WebRequest not in STARTED state (state=%d)", m_loadState); scoped_refptr<net::AuthChallengeInfo> authInfoPtr(authInfo); + bool firstTime = (m_authRequestCount == 0); + ++m_authRequestCount; + m_urlLoader->maybeCallOnMainThread(NewRunnableMethod( - m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr)); + m_urlLoader.get(), &WebUrlLoaderClient::authRequired, authInfoPtr, firstTime)); } // After calling Start(), the delegate will receive an OnResponseStarted diff --git a/WebKit/android/WebCoreSupport/WebRequest.h b/WebKit/android/WebCoreSupport/WebRequest.h index 2bcbb92..98e2921 100644 --- a/WebKit/android/WebCoreSupport/WebRequest.h +++ b/WebKit/android/WebCoreSupport/WebRequest.h @@ -101,6 +101,7 @@ private: std::string m_url; std::string m_userAgent; LoadState m_loadState; + int m_authRequestCount; }; } // namespace android diff --git a/WebKit/android/WebCoreSupport/WebRequestContext.cpp b/WebKit/android/WebCoreSupport/WebRequestContext.cpp index 1a82449..eed8863 100644 --- a/WebKit/android/WebCoreSupport/WebRequestContext.cpp +++ b/WebKit/android/WebCoreSupport/WebRequestContext.cpp @@ -28,44 +28,31 @@ #include "ChromiumIncludes.h" #include "ChromiumLogging.h" -#include "JNIUtility.h" -#include "WebCoreJni.h" -#include "WebUrlLoaderClient.h" -#include "jni.h" +#include "WebCache.h" +#include "WebCookieJar.h" #include <dirent.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> #include <wtf/text/CString.h> namespace { // TODO: The userAgent should not be a static, as it can be set per WebView. // http://b/3113804 std::string userAgent(""); -std::string acceptLanguage(""); - Lock userAgentLock; -Lock acceptLanguageLock; -WTF::Mutex databaseDirectoryMutex; -WTF::Mutex cacheDirectoryMutex; +std::string acceptLanguageStdString(""); +WTF::String acceptLanguageWtfString(""); +WTF::Mutex acceptLanguageMutex; } using namespace WTF; namespace android { -static const char* const kCookiesDatabaseFilename = "/webviewCookiesChromium.db"; -static const char* const kCacheDirectory = "/webviewCacheChromium"; -static const char* const kCookiesDatabaseFilenamePrivate = "/webviewCookiesChromiumPrivate.db"; -static const char* const kCacheDirectoryPrivate = "/webviewCacheChromiumPrivate"; - static scoped_refptr<WebRequestContext> privateBrowsingContext(0); static WTF::Mutex privateBrowsingContextMutex; WebRequestContext::WebRequestContext() - : m_allowCookies(true) { // Also hardcoded in FrameLoader.java accept_charset_ = "utf-8, iso-8859-1, utf-16, *;q=0.7"; @@ -92,91 +79,45 @@ const std::string& WebRequestContext::GetUserAgent(const GURL& url) const return userAgent; } -void WebRequestContext::setAcceptLanguage(String string) +void WebRequestContext::setAcceptLanguage(const String& string) { - // The accept language is set on the WebCore thread and read on the network - // stack's IO thread. - AutoLock aLock(acceptLanguageLock); - acceptLanguage = string.utf8().data(); + MutexLocker lock(acceptLanguageMutex); + acceptLanguageStdString = string.utf8().data(); + acceptLanguageWtfString = string; } const std::string& WebRequestContext::GetAcceptLanguage() const { - // The accept language is set on the WebCore thread and read on the network - // stack's IO thread. - AutoLock aLock(acceptLanguageLock); - return acceptLanguage; + MutexLocker lock(acceptLanguageMutex); + return acceptLanguageStdString; } -static const std::string& getDatabaseDirectory() +const String& WebRequestContext::acceptLanguage() { - // This method may be called on any thread, as the Java method is - // synchronized. - MutexLocker lock(databaseDirectoryMutex); - static std::string databaseDirectory; - if (databaseDirectory.empty()) { - JNIEnv* env = JSC::Bindings::getJNIEnv(); - jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); - jmethodID method = env->GetStaticMethodID(bridgeClass, "getDatabaseDirectory", "()Ljava/lang/String;"); - databaseDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); - env->DeleteLocalRef(bridgeClass); - } - return databaseDirectory; + MutexLocker lock(acceptLanguageMutex); + return acceptLanguageWtfString; } -static const std::string& getCacheDirectory() +WebRequestContext* WebRequestContext::getImpl(bool isPrivateBrowsing) { - // This method may be called on any thread, as the Java method is - // synchronized. - MutexLocker lock(cacheDirectoryMutex); - static std::string cacheDirectory; - if (cacheDirectory.empty()) { - JNIEnv* env = JSC::Bindings::getJNIEnv(); - jclass bridgeClass = env->FindClass("android/webkit/JniUtil"); - jmethodID method = env->GetStaticMethodID(bridgeClass, "getCacheDirectory", "()Ljava/lang/String;"); - cacheDirectory = jstringToStdString(env, static_cast<jstring>(env->CallStaticObjectMethod(bridgeClass, method))); - env->DeleteLocalRef(bridgeClass); - } - return cacheDirectory; -} - -WebRequestContext* WebRequestContext::getContextForPath(const char* cookieFilename, const char* cacheFilename) -{ - std::string cookieString(getDatabaseDirectory()); - cookieString.append(cookieFilename); - FilePath cookiePath(cookieString.c_str()); - std::string cacheString(getCacheDirectory()); - cacheString.append(cacheFilename); - FilePath cachePath(cacheString.c_str()); - WebRequestContext* context = new WebRequestContext(); - context->host_resolver_ = net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism, 0, 0); - base::Thread* ioThread = WebUrlLoaderClient::ioThread(); - scoped_refptr<base::MessageLoopProxy> cacheMessageLoopProxy = ioThread->message_loop_proxy(); - // Todo: check if the context takes ownership of the cache - net::HttpCache::DefaultBackend* defaultBackend = new net::HttpCache::DefaultBackend(net::DISK_CACHE, cachePath, 20 * 1024 * 1024, cacheMessageLoopProxy); - context->http_transaction_factory_ = new net::HttpCache(context->host_resolver(), context->dnsrr_resolver(), net::ProxyService::CreateDirect(), net::SSLConfigService::CreateSystemSSLConfigService(), net::HttpAuthHandlerFactory::CreateDefault(context->host_resolver_), 0, 0, defaultBackend); + WebCache* cache = WebCache::get(isPrivateBrowsing); + context->host_resolver_ = cache->hostResolver(); + context->http_transaction_factory_ = cache->cache(); - scoped_refptr<SQLitePersistentCookieStore> cookieDb = new SQLitePersistentCookieStore(cookiePath); - - // This is needed for the page cycler. See http://b/2944150 - net::CookieMonster::EnableFileScheme(); - - context->cookie_store_ = new net::CookieMonster(cookieDb.get(), 0); - context->cookie_policy_ = context; + WebCookieJar* cookieJar = WebCookieJar::get(isPrivateBrowsing); + context->cookie_store_ = cookieJar->cookieStore(); + context->cookie_policy_ = cookieJar; return context; } WebRequestContext* WebRequestContext::getRegularContext() { - static WTF::Mutex regularContextMutex; static scoped_refptr<WebRequestContext> regularContext(0); - - MutexLocker lock(regularContextMutex); if (!regularContext) - regularContext = getContextForPath(kCookiesDatabaseFilename, kCacheDirectory); + regularContext = getImpl(false); return regularContext; } @@ -184,11 +125,10 @@ WebRequestContext* WebRequestContext::getPrivateBrowsingContext() { MutexLocker lock(privateBrowsingContextMutex); - if (!privateBrowsingContext) { - // TODO: Where is the right place to put the temporary db? Should it be - // kept in memory? - privateBrowsingContext = getContextForPath(kCookiesDatabaseFilenamePrivate, kCacheDirectoryPrivate); - } + // TODO: Where is the right place to put the temporary db? Should it be + // kept in memory? + if (!privateBrowsingContext) + privateBrowsingContext = getImpl(true); return privateBrowsingContext; } @@ -200,7 +140,7 @@ WebRequestContext* WebRequestContext::get(bool isPrivateBrowsing) return isPrivateBrowsing ? getPrivateBrowsingContext() : getRegularContext(); } -static void removeFileOrDirectory(const char* filename) +void WebRequestContext::removeFileOrDirectory(const char* filename) { struct stat filetype; if (stat(filename, &filetype) != 0) @@ -224,7 +164,7 @@ static void removeFileOrDirectory(const char* filename) unlink(filename); } -bool WebRequestContext::cleanupPrivateBrowsingFiles(const std::string& databaseDirectory, const std::string& cacheDirectory) +bool WebRequestContext::cleanupPrivateBrowsingFiles() { // This is called on the UI thread. MutexLocker lock(privateBrowsingContextMutex); @@ -232,38 +172,11 @@ bool WebRequestContext::cleanupPrivateBrowsingFiles(const std::string& databaseD if (!privateBrowsingContext || privateBrowsingContext->HasOneRef()) { privateBrowsingContext = 0; - std::string cookiePath(databaseDirectory); - cookiePath.append(kCookiesDatabaseFilenamePrivate); - removeFileOrDirectory(cookiePath.c_str()); - std::string cachePath(cacheDirectory); - cachePath.append(kCacheDirectoryPrivate); - removeFileOrDirectory(cachePath.c_str()); + WebCookieJar::get(true)->cleanupFiles(); + WebCache::get(true)->cleanupFiles(); return true; } return false; } -int WebRequestContext::CanGetCookies(const GURL&, const GURL&, net::CompletionCallback*) -{ - MutexLocker lock(m_allowCookiesMutex); - return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; -} - -int WebRequestContext::CanSetCookie(const GURL&, const GURL&, const std::string&, net::CompletionCallback*) -{ - MutexLocker lock(m_allowCookiesMutex); - return m_allowCookies ? net::OK : net::ERR_ACCESS_DENIED; -} - -bool WebRequestContext::allowCookies() { - MutexLocker lock(m_allowCookiesMutex); - return m_allowCookies; -} - -void WebRequestContext::setAllowCookies(bool allow) -{ - MutexLocker lock(m_allowCookiesMutex); - m_allowCookies = allow; -} - } // namespace android diff --git a/WebKit/android/WebCoreSupport/WebRequestContext.h b/WebKit/android/WebCoreSupport/WebRequestContext.h index f4a579c..ec1561b 100644 --- a/WebKit/android/WebCoreSupport/WebRequestContext.h +++ b/WebKit/android/WebCoreSupport/WebRequestContext.h @@ -33,39 +33,36 @@ namespace android { -class WebRequestContext : public URLRequestContext, net::CookiePolicy { +// This class is generally not threadsafe. .get() is not threadsafe - instances +// are created on the WebCore thread only. +class WebRequestContext : public URLRequestContext { public: // URLRequestContext overrides. virtual const std::string& GetUserAgent(const GURL&) const; virtual const std::string& GetAcceptLanguage() const; - // CookiePolicy implementation. - virtual int CanGetCookies(const GURL& url, const GURL& first_party_for_cookies, net::CompletionCallback*); - virtual int CanSetCookie(const GURL& url, const GURL& first_party_for_cookies, const std::string& cookie_line, net::CompletionCallback*); - // Lazily create the relevant context. This class holds a reference. - // This may be called on any thread. The context returned, however, is not - // threadsafe, and should only be used on a single thread (the network stack - // IO thread), with the exception of the methods below. static WebRequestContext* get(bool isPrivateBrowsing); // These methods are threadsafe. - static bool cleanupPrivateBrowsingFiles(const std::string& databaseDirectory, const std::string& cacheDirectory); + static bool cleanupPrivateBrowsingFiles(); static void setUserAgent(WTF::String); - static void setAcceptLanguage(WTF::String); - bool allowCookies(); - void setAllowCookies(bool allow); + static void setAcceptLanguage(const WTF::String&); + static const WTF::String& acceptLanguage(); + + // A helper function used by the cache and cookie managers. Should probably + // find a better home, but wait for the refactoring in b/3113804 to be + // completed. + static void removeFileOrDirectory(const char* filename); private: WebRequestContext(); ~WebRequestContext(); - static WebRequestContext* getContextForPath(const char* cookieFilename, const char* cacheFilename); + static WebRequestContext* getImpl(bool isPrivateBrowsing); static WebRequestContext* getRegularContext(); static WebRequestContext* getPrivateBrowsingContext(); - bool m_allowCookies; - WTF::Mutex m_allowCookiesMutex; }; } // namespace android diff --git a/WebKit/android/WebCoreSupport/WebResourceRequest.cpp b/WebKit/android/WebCoreSupport/WebResourceRequest.cpp index c3ec562..9b70fce 100644 --- a/WebKit/android/WebCoreSupport/WebResourceRequest.cpp +++ b/WebKit/android/WebCoreSupport/WebResourceRequest.cpp @@ -36,10 +36,37 @@ namespace android { WebResourceRequest::WebResourceRequest(const WebCore::ResourceRequest& resourceRequest) { + // Set the load flags based on the WebCore request. + m_loadFlags = net::LOAD_NORMAL; + switch (resourceRequest.cachePolicy()) { + case ReloadIgnoringCacheData: + m_loadFlags |= net::LOAD_VALIDATE_CACHE; + break; + case ReturnCacheDataElseLoad: + m_loadFlags |= net::LOAD_PREFERRING_CACHE; + break; + case ReturnCacheDataDontLoad: + m_loadFlags |= net::LOAD_ONLY_FROM_CACHE; + break; + case UseProtocolCachePolicy: + break; + } + + // TODO: We should consider setting these flags and net::LOAD_DO_NOT_SEND_AUTH_DATA + // when FrameLoaderClient::shouldUseCredentialStorage() is false. However, + // the required WebKit logic is not yet in place. See Chromium's + // FrameLoaderClientImpl::shouldUseCredentialStorage(). + if (!resourceRequest.allowCookies()) { + m_loadFlags |= net::LOAD_DO_NOT_SAVE_COOKIES; + m_loadFlags |= net::LOAD_DO_NOT_SEND_COOKIES; + } + + // Set the request headers const HTTPHeaderMap& map = resourceRequest.httpHeaderFields(); for (HTTPHeaderMap::const_iterator it = map.begin(); it != map.end(); ++it) { const std::string& nameUtf8 = it->first.string().utf8().data(); + const std::string& valueUtf8 = it->second.utf8().data(); // Skip over referrer headers found in the header map because we already // pulled it out as a separate parameter. We likewise prune the UA since @@ -47,17 +74,13 @@ WebResourceRequest::WebResourceRequest(const WebCore::ResourceRequest& resourceR if (LowerCaseEqualsASCII(nameUtf8, "referer") || LowerCaseEqualsASCII(nameUtf8, "user-agent")) continue; - // The next comment does not match what is happening in code since the load flags are not implemented - // (http://b/issue?id=2889880) - // TODO: Check this is correct when load flags are implemented and working. - // Skip over "Cache-Control: max-age=0" header if the corresponding // load flag is already specified. FrameLoader sets both the flag and // the extra header -- the extra header is redundant since our network // implementation will add the necessary headers based on load flags. // See http://code.google.com/p/chromium/issues/detail?id=3434. - const std::string& valueUtf8 = it->second.utf8().data(); - if (LowerCaseEqualsASCII(nameUtf8, "cache-control") && LowerCaseEqualsASCII(valueUtf8, "max-age=0")) + if ((m_loadFlags & net::LOAD_VALIDATE_CACHE) && + LowerCaseEqualsASCII(nameUtf8, "cache-control") && LowerCaseEqualsASCII(valueUtf8, "max-age=0")) continue; m_requestHeaders.SetHeader(nameUtf8, valueUtf8); @@ -68,21 +91,6 @@ WebResourceRequest::WebResourceRequest(const WebCore::ResourceRequest& resourceR m_userAgent = resourceRequest.httpUserAgent().utf8().data(); m_url = resourceRequest.url().string().utf8().data(); - - m_loadFlags = net::LOAD_NORMAL; - switch (resourceRequest.cachePolicy()) { - case ReloadIgnoringCacheData: - m_loadFlags |= net::LOAD_VALIDATE_CACHE; - break; - case ReturnCacheDataElseLoad: - m_loadFlags |= net::LOAD_PREFERRING_CACHE; - break; - case ReturnCacheDataDontLoad: - m_loadFlags |= net::LOAD_ONLY_FROM_CACHE; - break; - case UseProtocolCachePolicy: - break; - } } } // namespace android diff --git a/WebKit/android/WebCoreSupport/WebUrlLoader.cpp b/WebKit/android/WebCoreSupport/WebUrlLoader.cpp index 170e78f..8c943a0 100644 --- a/WebKit/android/WebCoreSupport/WebUrlLoader.cpp +++ b/WebKit/android/WebCoreSupport/WebUrlLoader.cpp @@ -80,31 +80,3 @@ void WebUrlLoader::downloadFile() } } // namespace android - - -namespace WebCore { -// on main thread -// static -// TODO: Implement sync requests -PassRefPtr<ResourceLoaderAndroid> ResourceLoaderAndroid::start(WebCore::ResourceHandle* resourceHandle, const WebCore::ResourceRequest& resourceRequest, - FrameLoaderClient* client, bool /*isMainResource*/, bool isSync, bool isPrivateBrowsing) -{ - return android::WebUrlLoader::start(client, resourceHandle, resourceRequest, isSync, isPrivateBrowsing); -} - -// static -bool ResourceLoaderAndroid::willLoadFromCache(const WebCore::KURL&, int64_t identifier) -{ - // This method is used to determine if a POST request can be repeated from - // cache, but you cannot really know until you actually try to read from the - // cache. Even if we checked now, something else could come along and wipe - // out the cache entry by the time we fetch it. - // - // So, we always say yes here, to prevent the FrameLoader from initiating a - // reload. Then in FrameLoaderClientImpl::dispatchWillSendRequest, we - // fix-up the cache policy of the request to force a load from the cache. - // - return true; -} - -} // namespace WebCore diff --git a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp index 7487e48..d7df279 100644 --- a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp +++ b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp @@ -337,7 +337,7 @@ void WebUrlLoaderClient::didFinishLoading() finish(); } -void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo) +void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> authChallengeInfo, bool firstTime) { if (!isActive()) { return; @@ -346,13 +346,7 @@ void WebUrlLoaderClient::authRequired(scoped_refptr<net::AuthChallengeInfo> auth std::string host = base::SysWideToUTF8(authChallengeInfo->host_and_port); std::string realm = base::SysWideToUTF8(authChallengeInfo->realm); - // TODO: Not clear whose responsibility it is to cache credentials. There's nothing - // in AuthChallengeInfo that seems suitable, so for safety we'll tell the UI *not* - // to use cached credentials. We may need to track this ourselves (pass "true" on - // the first call, then "false" for a second call if the credentials are rejected). - bool useCachedCredentials = false; - - m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, useCachedCredentials); + m_webFrame->didReceiveAuthenticationChallenge(this, host, realm, firstTime); } } // namespace android diff --git a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h index 5f2c528..56d4289 100644 --- a/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h +++ b/WebKit/android/WebCoreSupport/WebUrlLoaderClient.h @@ -86,7 +86,7 @@ public: void didFinishLoading(); void didFail(PassOwnPtr<WebResponse>); void willSendRequest(PassOwnPtr<WebResponse>); - void authRequired(scoped_refptr<net::AuthChallengeInfo>); + void authRequired(scoped_refptr<net::AuthChallengeInfo>, bool firstTime); // Handle to the chrome IO thread static base::Thread* ioThread(); diff --git a/WebKit/android/jni/CookieManager.cpp b/WebKit/android/jni/CookieManager.cpp index b38cc3a..821d28d 100644 --- a/WebKit/android/jni/CookieManager.cpp +++ b/WebKit/android/jni/CookieManager.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "ChromiumIncludes.h" +#include "WebCookieJar.h" #include "WebRequestContext.h" #include "WebCoreJni.h" #include <JNIHelp.h> @@ -38,16 +39,7 @@ namespace android { // JNI for android.webkit.CookieManager static const char* javaCookieManagerClass = "android/webkit/CookieManager"; -// Though WebRequestContext::get() is threadsafe, the context itself, in -// general, is not. The context is generally used on the HTTP stack IO -// thread, but calls to the methods of this class are made on the UI thread. -// We ensure thread safety as follows ... -// - The cookie_store() getter just returns a pointer which is only set when the -// context is first constructed. The CookieMonster itself is threadsafe, so -// using it from the UI thread is safe. -// - Calls to the other WebRequestContext methods used here are explicitly -// threadsafe. - +// WebCookieJar is threadsafe, as is CookieMonster. static bool useChromiumHttpStack(JNIEnv*, jobject) { #if USE(CHROME_NETWORK_STACK) @@ -63,8 +55,8 @@ static bool acceptCookie(JNIEnv*, jobject) // This is a static method which gets the cookie policy for all WebViews. We // always apply the same configuration to the contexts for both regular and // private browsing, so expect the same result here. - bool regularAcceptCookies = WebRequestContext::get(false)->allowCookies(); - ASSERT(regularAcceptCookies == WebRequestContext::get(true)->allowCookies()); + bool regularAcceptCookies = WebCookieJar::get(false)->allowCookies(); + ASSERT(regularAcceptCookies == WebCookieJar::get(true)->allowCookies()); return regularAcceptCookies; #else // The Android HTTP stack is implemented Java-side. @@ -79,7 +71,7 @@ static jstring getCookie(JNIEnv* env, jobject, jstring url) GURL gurl(jstringToStdString(env, url)); CookieOptions options; options.set_include_httponly(); - std::string cookies = WebRequestContext::get(false)->cookie_store()->GetCookieMonster()->GetCookiesWithOptions(gurl, options); + std::string cookies = WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options); return cookies.empty() ? 0 : env->NewStringUTF(cookies.c_str()); #else // The Android HTTP stack is implemented Java-side. @@ -91,7 +83,7 @@ static jstring getCookie(JNIEnv* env, jobject, jstring url) static bool hasCookies(JNIEnv*, jobject) { #if USE(CHROME_NETWORK_STACK) - return !WebRequestContext::get(false)->cookie_store()->GetCookieMonster()->GetAllCookies().empty(); + return !WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetAllCookies().empty(); #else // The Android HTTP stack is implemented Java-side. ASSERT_NOT_REACHED(); @@ -102,13 +94,13 @@ static bool hasCookies(JNIEnv*, jobject) static void removeAllCookie(JNIEnv*, jobject) { #if USE(CHROME_NETWORK_STACK) - WebRequestContext::get(false)->cookie_store()->GetCookieMonster()->DeleteAllCreatedAfter(Time(), true); + WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->DeleteAllCreatedAfter(Time(), true); // This will lazily create a new private browsing context. However, if the // context doesn't already exist, there's no need to create it, as cookies // for such contexts are cleared up when we're done with them. // TODO: Consider adding an optimisation to not create the context if it // doesn't already exist. - WebRequestContext::get(true)->cookie_store()->GetCookieMonster()->DeleteAllCreatedAfter(Time(), true); + WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->DeleteAllCreatedAfter(Time(), true); #endif } @@ -116,18 +108,18 @@ static void removeExpiredCookie(JNIEnv*, jobject) { #if USE(CHROME_NETWORK_STACK) // This simply forces a GC. The getters delete expired cookies so won't return expired cookies anyway. - WebRequestContext::get(false)->cookie_store()->GetCookieMonster()->GetAllCookies(); - WebRequestContext::get(true)->cookie_store()->GetCookieMonster()->GetAllCookies(); + WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetAllCookies(); + WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->GetAllCookies(); #endif } -static void removeSessionCookies(WebRequestContext* context) +static void removeSessionCookies(WebCookieJar* cookieJar) { #if USE(CHROME_NETWORK_STACK) - CookieMonster* cookieMonster = context->cookie_store()->GetCookieMonster(); + CookieMonster* cookieMonster = cookieJar->cookieStore()->GetCookieMonster(); CookieMonster::CookieList cookies = cookieMonster->GetAllCookies(); for (CookieMonster::CookieList::const_iterator iter = cookies.begin(); iter != cookies.end(); ++iter) { - if (!iter->IsPersistent()) + if (iter->IsSessionCookie()) cookieMonster->DeleteCanonicalCookie(*iter); } #endif @@ -136,8 +128,8 @@ static void removeSessionCookies(WebRequestContext* context) static void removeSessionCookie(JNIEnv*, jobject) { #if USE(CHROME_NETWORK_STACK) - removeSessionCookies(WebRequestContext::get(false)); - removeSessionCookies(WebRequestContext::get(true)); + removeSessionCookies(WebCookieJar::get(false)); + removeSessionCookies(WebCookieJar::get(true)); #endif } @@ -147,8 +139,8 @@ static void setAcceptCookie(JNIEnv*, jobject, jboolean accept) // This is a static method which configures the cookie policy for all // WebViews, so we configure the contexts for both regular and private // browsing. - WebRequestContext::get(false)->setAllowCookies(accept); - WebRequestContext::get(true)->setAllowCookies(accept); + WebCookieJar::get(false)->setAllowCookies(accept); + WebCookieJar::get(true)->setAllowCookies(accept); #endif } @@ -159,7 +151,7 @@ static void setCookie(JNIEnv* env, jobject, jstring url, jstring value) std::string line(jstringToStdString(env, value)); CookieOptions options; options.set_include_httponly(); - WebRequestContext::get(false)->cookie_store()->GetCookieMonster()->SetCookieWithOptions(gurl, line, options); + WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->SetCookieWithOptions(gurl, line, options); #endif } diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp index 4212aa2..b9b4b75 100644 --- a/WebKit/android/jni/WebCoreFrameBridge.cpp +++ b/WebKit/android/jni/WebCoreFrameBridge.cpp @@ -1666,7 +1666,7 @@ static void ClearWebCoreCache() static void ClearWebViewCache() { #if USE(CHROME_NETWORK_STACK) - WebCache::clear(); + WebCache::get(false /*privateBrowsing*/)->clear(); #else // The Android network stack provides a WebView cache in CacheManager.java. // Clearing this is handled entirely Java-side. diff --git a/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp index adee9d8..737f107 100644 --- a/WebKit/android/jni/WebSettings.cpp +++ b/WebKit/android/jni/WebSettings.cpp @@ -129,7 +129,6 @@ struct FieldIds { mAutoFillEnabled = env->GetFieldID(clazz, "mAutoFillEnabled", "Z"); mAutoFillProfile = env->GetFieldID(clazz, "mAutoFillProfile", "Landroid/webkit/WebSettings$AutoFillProfile;"); jclass autoFillProfileClass = env->FindClass("android/webkit/WebSettings$AutoFillProfile"); - mAutoFillProfileUniqueId = env->GetFieldID(autoFillProfileClass, "mUniqueId", "I"); mAutoFillProfileFullName = env->GetFieldID(autoFillProfileClass, "mFullName", "Ljava/lang/String;"); mAutoFillProfileEmailAddress = env->GetFieldID(autoFillProfileClass, "mEmailAddress", "Ljava/lang/String;"); mAutoFillProfileCompanyName = env->GetFieldID(autoFillProfileClass, "mCompanyName", "Ljava/lang/String;"); @@ -254,7 +253,6 @@ struct FieldIds { #if ENABLE(WEB_AUTOFILL) jfieldID mAutoFillEnabled; jfieldID mAutoFillProfile; - jfieldID mAutoFillProfileUniqueId; jfieldID mAutoFillProfileFullName; jfieldID mAutoFillProfileEmailAddress; jfieldID mAutoFillProfileCompanyName; @@ -291,8 +289,6 @@ inline string16 getStringFieldAsString16(JNIEnv* env, jobject autoFillProfile, j void syncAutoFillProfile(JNIEnv* env, jobject autoFillProfile, WebAutoFill* webAutoFill) { - // FIXME: id is deprecated. - int id = env->GetIntField(autoFillProfile, gFieldIds->mAutoFillProfileUniqueId); string16 fullName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileFullName); string16 emailAddress = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileEmailAddress); string16 companyName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCompanyName); diff --git a/WebKit/android/nav/CacheBuilder.cpp b/WebKit/android/nav/CacheBuilder.cpp index c631cd4..449b9e2 100644 --- a/WebKit/android/nav/CacheBuilder.cpp +++ b/WebKit/android/nav/CacheBuilder.cpp @@ -1275,6 +1275,8 @@ void CacheBuilder::BuildFrame(Frame* root, Frame* frame, isUnclipped = isTransparent; } else if (input->isInputTypeHidden()) continue; + else if (input->isRadioButton() || input->isCheckbox()) + isTransparent = false; } else if (node->hasTagName(HTMLNames::textareaTag)) { cachedInput.init(); type = TEXT_INPUT_CACHEDNODETYPE; diff --git a/WebKit/android/nav/CachedRoot.cpp b/WebKit/android/nav/CachedRoot.cpp index a41cfdc..1a31c15 100644 --- a/WebKit/android/nav/CachedRoot.cpp +++ b/WebKit/android/nav/CachedRoot.cpp @@ -717,9 +717,8 @@ public: layers->getBounds().fLeft, layers->getBounds().fTop, layers->getBounds().fRight, layers->getBounds().fBottom); if (collectGlyphs && (layerType == kDrawGlyph_Type - || ((layerType == kDrawRect_Type - || layerType == kDrawBitmap_Type) - && mTextTest.contains(*layers)))) { + || ((layerType == kDrawRect_Type && mTextTest.contains(*layers)) + || (layerType == kDrawBitmap_Type && mTextSlop.contains(*layers))))) { DBG_NAV_LOGD("RingCheck #%d collectOvers", layers - mLayers.begin()); collectOvers = true; clipped->op(*layers, SkRegion::kUnion_Op); @@ -964,9 +963,9 @@ private: mTextSlop.contains(*layers) ? "true" : "false", gb.fLeft, gb.fTop, gb.fRight, gb.fBottom); #endif - if ((layerType == kDrawGlyph_Type && mTextSlop.contains(*layers)) - || ((layerType == kDrawRect_Type - || layerType == kDrawBitmap_Type) + if (((layerType == kDrawGlyph_Type || layerType == kDrawBitmap_Type) + && mTextSlop.contains(*layers)) + || (layerType == kDrawRect_Type && mTextTest.contains(*layers))) { if (!testLayer) testLayer = layers; @@ -1854,7 +1853,8 @@ void CachedRoot::Debug::print() const b->mHistory->mDebug.print(b); DUMP_NAV_LOGD("// int mMaxXScroll=%d, mMaxYScroll=%d;\n", b->mMaxXScroll, b->mMaxYScroll); - CachedLayer::Debug::printRootLayerAndroid(b->mRootLayer); + if (b->mRootLayer) + CachedLayer::Debug::printRootLayerAndroid(b->mRootLayer); #ifdef DUMP_NAV_CACHE_USING_PRINTF if (gNavCacheLogFile) fclose(gNavCacheLogFile); diff --git a/WebKit/android/nav/WebView.cpp b/WebKit/android/nav/WebView.cpp index 03052ca..6370021 100644 --- a/WebKit/android/nav/WebView.cpp +++ b/WebKit/android/nav/WebView.cpp @@ -1793,6 +1793,25 @@ static void nativeSelectBestAt(JNIEnv *env, jobject obj, jobject jrect) view->selectBestAt(rect); } +static jobject nativeLayerBounds(JNIEnv* env, jobject obj, jint jlayer) +{ + SkRect r; +#if USE(ACCELERATED_COMPOSITING) + LayerAndroid* layer = (LayerAndroid*) jlayer; + r = layer->bounds(); +#else + r.setEmpty(); +#endif + SkIRect irect; + r.round(&irect); + jclass rectClass = env->FindClass("android/graphics/Rect"); + jmethodID init = env->GetMethodID(rectClass, "<init>", "(IIII)V"); + jobject rect = env->NewObject(rectClass, init, irect.fLeft, irect.fTop, + irect.fRight, irect.fBottom); + env->DeleteLocalRef(rectClass); + return rect; +} + static jobject nativeSubtractLayers(JNIEnv* env, jobject obj, jobject jrect) { SkIRect irect = jrect_to_webrect(env, jrect); @@ -2048,19 +2067,10 @@ static void nativeMoveSelection(JNIEnv *env, jobject obj, int x, int y) GET_NATIVE_VIEW(env, obj)->moveSelection(x, y); } -static jboolean nativeCleanupPrivateBrowsingFiles( - JNIEnv *env, jobject obj, jstring databaseDirectoryJString, jstring cacheDirectoryJString) { +static jboolean nativeCleanupPrivateBrowsingFiles(JNIEnv*, jobject) +{ #if USE(CHROME_NETWORK_STACK) - jboolean isCopy; - const char* cString = env->GetStringUTFChars(databaseDirectoryJString, &isCopy); - std::string databaseDirectory(cString); - if (isCopy == JNI_TRUE) - env->ReleaseStringUTFChars(databaseDirectoryJString, cString); - cString = env->GetStringUTFChars(cacheDirectoryJString, &isCopy); - std::string cacheDirectory(cString); - if (isCopy == JNI_TRUE) - env->ReleaseStringUTFChars(cacheDirectoryJString, cString); - return WebRequestContext::cleanupPrivateBrowsingFiles(databaseDirectory, cacheDirectory); + return WebRequestContext::cleanupPrivateBrowsingFiles(); #else return JNI_FALSE; #endif @@ -2312,6 +2322,8 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeImageURI }, { "nativeInstrumentReport", "()V", (void*) nativeInstrumentReport }, + { "nativeLayerBounds", "(I)Landroid/graphics/Rect;", + (void*) nativeLayerBounds }, { "nativeMotionUp", "(III)Z", (void*) nativeMotionUp }, { "nativeMoveCursor", "(IIZ)Z", @@ -2322,7 +2334,7 @@ static JNINativeMethod gJavaWebViewMethods[] = { (void*) nativeMoveGeneration }, { "nativeMoveSelection", "(II)V", (void*) nativeMoveSelection }, - { "nativeCleanupPrivateBrowsingFiles", "(Ljava/lang/String;Ljava/lang/String;)Z", + { "nativeCleanupPrivateBrowsingFiles", "()Z", (void*) nativeCleanupPrivateBrowsingFiles }, { "nativePointInNavCache", "(III)Z", (void*) nativePointInNavCache }, |