Disallow setting base URL to a data or JavaScript URL
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Feb 2020 18:16:49 +0000 (18:16 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Feb 2020 18:16:49 +0000 (18:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=207136

Source/WebCore:

Reviewed by Brent Fulgham.

Inspired by <https://bugs.chromium.org/p/chromium/issues/detail?id=679318>.

Block setting the base URL to a data URL or JavaScript URL as such usage is questionable.
This makes WebKit match the behavior of Chrome and Firefox and is in the spirit of the
discussion in <https://github.com/whatwg/html/issues/2249>.

On Mac and iOS, this restriction is applied only to apps linked against a future SDK to
avoid breaking shipped apps.

For all other ports, this restriction is enabled by default.

Tests: fast/url/relative2.html
       fast/url/segments-from-data-url2.html
       http/tests/security/allowed-base-url-data-url-via-setting.html
       http/tests/security/denied-base-url-data-url.html
       http/tests/security/denied-base-url-javascript-url.html

* dom/Document.cpp:
(WebCore::Document::processBaseElement): Condition updating the parsed
base URL on whether is has an allowed scheme, if restrictions are enabled. Otherwise,
do what we do now. If the scheme is disallowed then log a message to the console to
explain this to web developers.
* html/parser/HTMLPreloadScanner.cpp:
(WebCore::TokenPreloadScanner::scan): Pass whether to apply restrictons to the base URL
to updatePredictedBaseURL(). This depends on whether the setting is enabled or not.
(WebCore::TokenPreloadScanner::updatePredictedBaseURL): Modifed to take a boolean as to
whether to apply restrictions. If restrictions are not to be applied do what we do now.
Otherwise, only do what we do now if the scheme for the predicated base URL is allowed.
* html/parser/HTMLPreloadScanner.h:
* page/SecurityPolicy.cpp:
(WebCore::SecurityPolicy::isBaseURLSchemeAllowed): Added.
* page/SecurityPolicy.h:
* page/Settings.yaml: Add a setting to toggle restrictions on the base URL scheme.

Source/WebKit:

Reviewed by Brent Fulgham.

Apply base URL restrictions to apps linked to a future WebKit to avoid breaking existing apps.

* Shared/WebPreferences.yaml:
* UIProcess/API/Cocoa/WKWebView.mm:
(shouldRestrictBaseURLSchemes): Added.
(-[WKWebView _setupPageConfiguration:]): Update settings.
* UIProcess/Cocoa/VersionChecks.h:

Source/WebKitLegacy/mac:

Reviewed by Brent Fulgham.

Apply base URL restrictions to apps linked to a future WebKit to avoid breaking existing apps.

* Misc/WebKitVersionChecks.h:
* WebView/WebView.mm:
(shouldRestrictBaseURLSchemes): Added.
(-[WebView _commonInitializationWithFrameName:groupName:]): Update settings.

Source/WTF:

Reviewed by Brent Fulgham.

Add some more macro definitions.

* wtf/spi/darwin/dyldSPI.h:

LayoutTests:

RReviewed by Brent Fulgham.

Add some tests. Update others to toggle the setting to apply or unapply the new behavior.

The test denied-base-url-javascript-url.html is derived from the test base-url-javascript.html,
included in <https://chromium.googlesource.com/chromium/src.git/+/c133efa0b915430701930b76a7cfe35608b9a403>.

* fast/url/relative-expected.txt:
* fast/url/relative.html:
* fast/url/relative2-expected.txt: Copied from LayoutTests/fast/url/relative-expected.txt.
* fast/url/relative2.html: Copied from LayoutTests/fast/url/relative.html.
* fast/url/resources/utilities.js:
(setShouldEllipsizeFileURLPaths): Added. Toggles ellipsizing the path portion of a file URL to simplify matching.
Otherwise, file URLs could be machine-specific.
(canonicalizedPathname): Added.
(segments): Modified to optionally call canonicalizedPathname.
(canonicalize): Ditto.
* fast/url/segments-from-data-url-expected.txt:
* fast/url/segments-from-data-url.html:
* fast/url/segments-from-data-url2-expected.txt: Copied from LayoutTests/fast/url/segments-from-data-url-expected.txt.
* fast/url/segments-from-data-url2.html: Copied from LayoutTests/fast/url/segments-from-data-url.html.
* fetch/fetch-url-serialization-expected.txt:
* http/tests/plugins/navigation-during-load-embed.html:
* http/tests/plugins/navigation-during-load.html:
* http/tests/security/allowed-base-url-data-url-via-setting-expected.txt: Added.
* http/tests/security/allowed-base-url-data-url-via-setting.html: Added.
* http/tests/security/denied-base-url-data-url-expected.txt: Added.
* http/tests/security/denied-base-url-data-url.html: Added.
* http/tests/security/denied-base-url-javascript-url-expected.txt: Added.
* http/tests/security/denied-base-url-javascript-url.html: Added.

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

39 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/url/relative-expected.txt
LayoutTests/fast/url/relative.html
LayoutTests/fast/url/relative2-expected.txt [new file with mode: 0644]
LayoutTests/fast/url/relative2.html [new file with mode: 0644]
LayoutTests/fast/url/resources/utilities.js
LayoutTests/fast/url/segments-from-data-url-expected.txt
LayoutTests/fast/url/segments-from-data-url.html
LayoutTests/fast/url/segments-from-data-url2-expected.txt [new file with mode: 0644]
LayoutTests/fast/url/segments-from-data-url2.html [new file with mode: 0644]
LayoutTests/fetch/fetch-url-serialization-expected.txt
LayoutTests/http/tests/plugins/navigation-during-load-embed.html
LayoutTests/http/tests/plugins/navigation-during-load.html
LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting.html [new file with mode: 0644]
LayoutTests/http/tests/security/denied-base-url-data-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/denied-base-url-data-url.html [new file with mode: 0644]
LayoutTests/http/tests/security/denied-base-url-javascript-url-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/denied-base-url-javascript-url.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/url/a-element-expected.txt
LayoutTests/imported/w3c/web-platform-tests/url/a-element-origin-expected.txt
LayoutTests/imported/w3c/web-platform-tests/url/a-element-origin-xhtml-expected.txt
LayoutTests/imported/w3c/web-platform-tests/url/a-element-xhtml-expected.txt
Source/WTF/ChangeLog
Source/WTF/wtf/spi/darwin/dyldSPI.h
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/html/parser/HTMLPreloadScanner.cpp
Source/WebCore/html/parser/HTMLPreloadScanner.h
Source/WebCore/page/SecurityPolicy.cpp
Source/WebCore/page/SecurityPolicy.h
Source/WebCore/page/Settings.yaml
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPreferences.yaml
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/Cocoa/VersionChecks.h
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/Misc/WebKitVersionChecks.h
Source/WebKitLegacy/mac/WebView/WebView.mm

index a62402f..d3dfbb2 100644 (file)
@@ -1,3 +1,39 @@
+2020-02-10  Daniel Bates  <dabates@apple.com>
+
+        Disallow setting base URL to a data or JavaScript URL
+        https://bugs.webkit.org/show_bug.cgi?id=207136
+
+        RReviewed by Brent Fulgham.
+
+        Add some tests. Update others to toggle the setting to apply or unapply the new behavior.
+
+        The test denied-base-url-javascript-url.html is derived from the test base-url-javascript.html,
+        included in <https://chromium.googlesource.com/chromium/src.git/+/c133efa0b915430701930b76a7cfe35608b9a403>.
+
+        * fast/url/relative-expected.txt:
+        * fast/url/relative.html:
+        * fast/url/relative2-expected.txt: Copied from LayoutTests/fast/url/relative-expected.txt.
+        * fast/url/relative2.html: Copied from LayoutTests/fast/url/relative.html.
+        * fast/url/resources/utilities.js:
+        (setShouldEllipsizeFileURLPaths): Added. Toggles ellipsizing the path portion of a file URL to simplify matching.
+        Otherwise, file URLs could be machine-specific.
+        (canonicalizedPathname): Added.
+        (segments): Modified to optionally call canonicalizedPathname.
+        (canonicalize): Ditto.
+        * fast/url/segments-from-data-url-expected.txt:
+        * fast/url/segments-from-data-url.html:
+        * fast/url/segments-from-data-url2-expected.txt: Copied from LayoutTests/fast/url/segments-from-data-url-expected.txt.
+        * fast/url/segments-from-data-url2.html: Copied from LayoutTests/fast/url/segments-from-data-url.html.
+        * fetch/fetch-url-serialization-expected.txt:
+        * http/tests/plugins/navigation-during-load-embed.html:
+        * http/tests/plugins/navigation-during-load.html:
+        * http/tests/security/allowed-base-url-data-url-via-setting-expected.txt: Added.
+        * http/tests/security/allowed-base-url-data-url-via-setting.html: Added.
+        * http/tests/security/denied-base-url-data-url-expected.txt: Added.
+        * http/tests/security/denied-base-url-data-url.html: Added.
+        * http/tests/security/denied-base-url-javascript-url-expected.txt: Added.
+        * http/tests/security/denied-base-url-javascript-url.html: Added.
+
 2020-02-10  Jacob Uphoff  <jacob_uphoff@apple.com>
 
         [ macOS wk2 ] webgpu/whlsl/store-to-property-updates-properly.html is flaky failing
index 171550a..c1fdbc6 100644 (file)
@@ -1,3 +1,9 @@
+CONSOLE MESSAGE: line 50: Blocked setting data:foobar as the base URL because it does not have an allowed scheme.
+CONSOLE MESSAGE: line 50: Blocked setting data:foobar as the base URL because it does not have an allowed scheme.
+CONSOLE MESSAGE: line 50: Blocked setting data:foobar as the base URL because it does not have an allowed scheme.
+CONSOLE MESSAGE: line 50: Blocked setting data:foobar as the base URL because it does not have an allowed scheme.
+CONSOLE MESSAGE: line 50: Blocked setting data:foobar as the base URL because it does not have an allowed scheme.
+CONSOLE MESSAGE: line 50: Blocked setting data:asdf as the base URL because it does not have an allowed scheme.
 Test resolution of relative URLs.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -38,7 +44,7 @@ PASS canonicalize('?foo=bar#com') is 'http://host/a?foo=bar#com'
 PASS canonicalize('#ref') is 'http://host/a#ref'
 PASS canonicalize('#') is 'http://host/a#'
 PASS canonicalize('#bye') is 'http://host/a?foo=bar#bye'
-FAIL canonicalize('baz.html') should be . Was baz.html.
+PASS canonicalize('baz.html') is 'file:///.../baz.html'
 PASS canonicalize('data:baz') is 'data:baz'
 PASS canonicalize('data:/base') is 'data:/base'
 PASS canonicalize('http://host/') is 'http://host/'
@@ -46,7 +52,7 @@ PASS canonicalize('http:host') is 'http://host/'
 PASS canonicalize('./asd:fgh') is 'http://foo/asd:fgh'
 PASS canonicalize(':foo') is 'http://foo/:foo'
 PASS canonicalize(' hello world') is 'http://foo/hello%20world'
-FAIL canonicalize(':foo') should be . Was :foo.
+PASS canonicalize(':foo') is 'file:///.../:foo'
 PASS canonicalize(';foo') is 'http://host/;foo'
 PASS canonicalize(';foo') is 'http://host/;foo'
 PASS canonicalize(';/../bar') is 'http://host/bar'
@@ -58,6 +64,7 @@ FAIL canonicalize('//') should be http:. Was //.
 PASS canonicalize('\\/another/path') is 'http://another/path'
 PASS canonicalize('/\\Another\\path') is 'http://another/path'
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 4e26f7d..ac121cc 100644 (file)
@@ -1,11 +1,14 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../resources/js-test-pre.js"></script>
+<script src="../../resources/js-test.js"></script>
 <script src="resources/utilities.js"></script>
 </head>
 <body>
 <script>
+if (window.internals && internals.settings)
+  internals.settings.setShouldRestrictBaseURLSchemes(true);
+
 description("Test resolution of relative URLs.");
 
 cases = [ 
@@ -57,7 +60,7 @@ cases = [
   ["http://host/a?foo=bar#hello", "#bye", "http://host/a?foo=bar#bye"],
   // Non-hierarchical base: no relative handling. Relative input should
   // error, and if a scheme is present, it should be treated as absolute.
-  ["data:foobar", "baz.html", ""],
+  ["data:foobar", "baz.html", "file:///.../baz.html"],
   ["data:foobar", "data:baz", "data:baz"],
   ["data:foobar", "data:/base", "data:/base"],
   // Non-hierarchical base: absolute input should succeed.
@@ -67,7 +70,7 @@ cases = [
   ["http://foo/bar", "./asd:fgh", "http://foo/asd:fgh"],
   ["http://foo/bar", ":foo", "http://foo/:foo"],
   ["http://foo/bar", " hello world", "http://foo/hello%20world"],
-  ["data:asdf", ":foo", ""],
+  ["data:asdf", ":foo", "file:///.../:foo"],
   // We should treat semicolons like any other character in URL resolving
   ["http://host/a", ";foo", "http://host/;foo"],
   ["http://host/a;", ";foo", "http://host/;foo"],
@@ -87,6 +90,7 @@ cases = [
 ];
 
 var originalBaseURL = canonicalize(".");
+setShouldEllipsizeFileURLPaths(true);
 
 for (var i = 0; i < cases.length; ++i) {
   baseURL = cases[i][0];
@@ -99,6 +103,5 @@ for (var i = 0; i < cases.length; ++i) {
 
 setBaseURL(originalBaseURL);
 </script>
-<script src="../../resources/js-test-post.js"></script>
 </body>
 </html>
diff --git a/LayoutTests/fast/url/relative2-expected.txt b/LayoutTests/fast/url/relative2-expected.txt
new file mode 100644 (file)
index 0000000..171550a
--- /dev/null
@@ -0,0 +1,63 @@
+Test resolution of relative URLs.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS canonicalize('http://another/') is 'http://another/'
+PASS canonicalize('http:////another/') is 'http://another/'
+PASS canonicalize('') is 'http://foo/bar'
+PASS canonicalize('') is 'http://foo/bar'
+PASS canonicalize('') is 'http://foo/bar'
+PASS canonicalize('  another  ') is 'http://foo/another'
+PASS canonicalize('  .  ') is 'http://foo/'
+PASS canonicalize('     ') is 'http://foo/bar'
+PASS canonicalize('http:path') is 'http://host/path'
+PASS canonicalize('http:path') is 'http://host/a/path'
+PASS canonicalize('http:/path') is 'http://host/path'
+PASS canonicalize('HTTP:/path') is 'http://host/path'
+PASS canonicalize('https:host2') is 'https://host2/'
+PASS canonicalize('htto:/host2') is 'htto:/host2'
+PASS canonicalize('/b/c/d') is 'http://host/b/c/d'
+PASS canonicalize('\\b\\c\\d') is 'http://host/b/c/d'
+PASS canonicalize('/b/../c') is 'http://host/c'
+PASS canonicalize('/b/../c') is 'http://host/c'
+PASS canonicalize('\\b/../c?x#y') is 'http://host/c?x#y'
+PASS canonicalize('/b/../c?x#y') is 'http://host/c?x#y'
+PASS canonicalize('b') is 'http://host/b'
+PASS canonicalize('bc/de') is 'http://host/bc/de'
+PASS canonicalize('bc/de?query#ref') is 'http://host/a/bc/de?query#ref'
+PASS canonicalize('.') is 'http://host/a/'
+PASS canonicalize('..') is 'http://host/'
+PASS canonicalize('./..') is 'http://host/'
+PASS canonicalize('../.') is 'http://host/'
+PASS canonicalize('././.') is 'http://host/a/'
+PASS canonicalize('../../../foo') is 'http://host/foo'
+PASS canonicalize('?foo=bar') is 'http://host/a?foo=bar'
+PASS canonicalize('?') is 'http://host/a?'
+PASS canonicalize('?foo=bar#com') is 'http://host/a?foo=bar#com'
+PASS canonicalize('#ref') is 'http://host/a#ref'
+PASS canonicalize('#') is 'http://host/a#'
+PASS canonicalize('#bye') is 'http://host/a?foo=bar#bye'
+FAIL canonicalize('baz.html') should be . Was baz.html.
+PASS canonicalize('data:baz') is 'data:baz'
+PASS canonicalize('data:/base') is 'data:/base'
+PASS canonicalize('http://host/') is 'http://host/'
+PASS canonicalize('http:host') is 'http://host/'
+PASS canonicalize('./asd:fgh') is 'http://foo/asd:fgh'
+PASS canonicalize(':foo') is 'http://foo/:foo'
+PASS canonicalize(' hello world') is 'http://foo/hello%20world'
+FAIL canonicalize(':foo') should be . Was :foo.
+PASS canonicalize(';foo') is 'http://host/;foo'
+PASS canonicalize(';foo') is 'http://host/;foo'
+PASS canonicalize(';/../bar') is 'http://host/bar'
+PASS canonicalize('//another') is 'http://another/'
+PASS canonicalize('//another/path?query#ref') is 'http://another/path?query#ref'
+PASS canonicalize('///another/path') is 'http://another/path'
+PASS canonicalize('//Another\\path') is 'http://another/path'
+FAIL canonicalize('//') should be http:. Was //.
+PASS canonicalize('\\/another/path') is 'http://another/path'
+PASS canonicalize('/\\Another\\path') is 'http://another/path'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/url/relative2.html b/LayoutTests/fast/url/relative2.html
new file mode 100644 (file)
index 0000000..8c3cdfd
--- /dev/null
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="resources/utilities.js"></script>
+</head>
+<body>
+<script>
+if (window.internals && internals.settings)
+  internals.settings.setShouldRestrictBaseURLSchemes(false);
+
+description("Test resolution of relative URLs.");
+
+cases = [ 
+  // Format: [baseURL, relativeURL, expectedURL],
+  // Basic absolute input.
+  ["http://host/a", "http://another/", "http://another/"],
+  ["http://host/a", "http:////another/", "http://another/"],
+  // Empty relative URLs should only remove the ref part of the URL,
+  // leaving the rest unchanged.
+  ["http://foo/bar", "", "http://foo/bar"],
+  ["http://foo/bar#ref", "", "http://foo/bar"],
+  ["http://foo/bar#", "", "http://foo/bar"],
+  // Spaces at the ends of the relative path should be ignored.
+  ["http://foo/bar", "  another  ", "http://foo/another"],
+  ["http://foo/bar", "  .  ", "http://foo/"],
+  ["http://foo/bar", " \t ", "http://foo/bar"],
+  // Matching schemes without two slashes are treated as relative.
+  ["http://host/a", "http:path", "http://host/path"],
+  ["http://host/a/", "http:path", "http://host/a/path"],
+  ["http://host/a", "http:/path", "http://host/path"],
+  ["http://host/a", "HTTP:/path", "http://host/path"],
+  // Nonmatching schemes are absolute.
+  ["http://host/a", "https:host2", "https://host2/"],
+  ["http://host/a", "htto:/host2", "htto:/host2"],
+  // Absolute path input
+  ["http://host/a", "/b/c/d", "http://host/b/c/d"],
+  ["http://host/a", "\\\\b\\\\c\\\\d", "http://host/b/c/d"],
+  ["http://host/a", "/b/../c", "http://host/c"],
+  ["http://host/a?b#c", "/b/../c", "http://host/c"],
+  ["http://host/a", "\\\\b/../c?x#y", "http://host/c?x#y"],
+  ["http://host/a?b#c", "/b/../c?x#y", "http://host/c?x#y"],
+  // Relative path input
+  ["http://host/a", "b", "http://host/b"],
+  ["http://host/a", "bc/de", "http://host/bc/de"],
+  ["http://host/a/", "bc/de?query#ref", "http://host/a/bc/de?query#ref"],
+  ["http://host/a/", ".", "http://host/a/"],
+  ["http://host/a/", "..", "http://host/"],
+  ["http://host/a/", "./..", "http://host/"],
+  ["http://host/a/", "../.", "http://host/"],
+  ["http://host/a/", "././.", "http://host/a/"],
+  ["http://host/a?query#ref", "../../../foo", "http://host/foo"],
+  // Query input
+  ["http://host/a", "?foo=bar", "http://host/a?foo=bar"],
+  ["http://host/a?x=y#z", "?", "http://host/a?"],
+  ["http://host/a?x=y#z", "?foo=bar#com", "http://host/a?foo=bar#com"],
+  // Ref input
+  ["http://host/a", "#ref", "http://host/a#ref"],
+  ["http://host/a#b", "#", "http://host/a#"],
+  ["http://host/a?foo=bar#hello", "#bye", "http://host/a?foo=bar#bye"],
+  // Non-hierarchical base: no relative handling. Relative input should
+  // error, and if a scheme is present, it should be treated as absolute.
+  ["data:foobar", "baz.html", ""],
+  ["data:foobar", "data:baz", "data:baz"],
+  ["data:foobar", "data:/base", "data:/base"],
+  // Non-hierarchical base: absolute input should succeed.
+  ["data:foobar", "http://host/", "http://host/"],
+  ["data:foobar", "http:host", "http://host/"],
+  // Invalid schemes should be treated as relative.
+  ["http://foo/bar", "./asd:fgh", "http://foo/asd:fgh"],
+  ["http://foo/bar", ":foo", "http://foo/:foo"],
+  ["http://foo/bar", " hello world", "http://foo/hello%20world"],
+  ["data:asdf", ":foo", ""],
+  // We should treat semicolons like any other character in URL resolving
+  ["http://host/a", ";foo", "http://host/;foo"],
+  ["http://host/a;", ";foo", "http://host/;foo"],
+  ["http://host/a", ";/../bar", "http://host/bar"],
+  // Relative URLs can also be written as "//foo/bar" which is relative to
+  // the scheme. In this case, it would take the old scheme, so for http
+  // the example would resolve to "http://foo/bar".
+  ["http://host/a", "//another", "http://another/"],
+  ["http://host/a", "//another/path?query#ref", "http://another/path?query#ref"],
+  ["http://host/a", "///another/path", "http://another/path"],
+  ["http://host/a", "//Another\\\\path", "http://another/path"],
+  ["http://host/a", "//", "http:"],
+  // IE will also allow one or the other to be a backslash to get the same
+  // behavior.
+  ["http://host/a", "\\\\/another/path", "http://another/path"],
+  ["http://host/a", "/\\\\Another\\\\path", "http://another/path"],
+];
+
+var originalBaseURL = canonicalize(".");
+
+for (var i = 0; i < cases.length; ++i) {
+  baseURL = cases[i][0];
+  relativeURL = cases[i][1];
+  expectedURL = cases[i][2];
+  setBaseURL(baseURL);
+  shouldBe("canonicalize('" + relativeURL + "')",
+           "'" + expectedURL + "'");
+}
+
+setBaseURL(originalBaseURL);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 631bdec..7c62ff2 100644 (file)
@@ -1,13 +1,37 @@
 // Start the bidding at 42 for no particular reason.
+const FILE_PROTOCOL = "file:";
+
 var lastID = 42;
 
+var g_shouldEllipsizeFileURLPaths = false;
+
+function setShouldEllipsizeFileURLPaths(shouldEllipsizeFileURLPaths)
+{
+  g_shouldEllipsizeFileURLPaths = true;
+}
+
+// Simplifies file URL comparisons by ellipsizing everything except the basename of the file,
+// and normalizing to a Unix-style path. Note that this algorithm does not preserve path
+// hierarchy (e.g. file:///a => file:///.../a), but it is good enough for our purposes.
+function canonicalizedPathname(pathname)
+{
+  const NOT_FOUND = 0;
+  let positionAfterLastSlash = pathname.lastIndexOf("\\") + 1; // Windows path separator
+  if (positionAfterLastSlash === NOT_FOUND)
+    positionAfterLastSlash = pathname.lastIndexOf("/") + 1;
+  return "/.../" + (positionAfterLastSlash === NOT_FOUND ? pathname : pathname.substr(positionAfterLastSlash));
+}
+
 function canonicalize(url)
 {
   // It would be more elegant to use the DOM here, but we use document.write()
   // so the tests run correctly in Firefox.
   var id = ++lastID;
   document.write("<a id='" + id + "' href='" + url + "'></a>");
-  return document.getElementById(id).href;
+  let result = document.getElementById(id).href;
+  if (url !== "." && g_shouldEllipsizeFileURLPaths && result.startsWith(FILE_PROTOCOL))
+    return FILE_PROTOCOL + "//" + canonicalizedPathname(result.substr(FILE_PROTOCOL.length));
+  return result;
 }
 
 function setBaseURL(url)
@@ -32,13 +56,7 @@ function segments(url)
   // so the tests run correctly in Firefox.
   var id = ++lastID;
   document.write("<a id='" + id + "' href='" + url + "'></a>");
-  var elmt = document.getElementById(id);
-  return JSON.stringify([
-    elmt.protocol,
-    elmt.hostname,
-    elmt.port,
-    elmt.pathname,
-    elmt.search,
-    elmt.hash
-  ]);
+  let link = document.getElementById(id);
+  let pathname = g_shouldEllipsizeFileURLPaths && link.protocol === FILE_PROTOCOL ? canonicalizedPathname(link.pathname) : link.pathname;
+  return JSON.stringify([link.protocol, link.hostname, link.port, pathname, link.search, link.hash]);
 }
index 1c4ca32..ad2fb48 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 50: Blocked setting data:text/plain,baseURL as the base URL because it does not have an allowed scheme.
 Test URL segmentation
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -5,8 +6,8 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS segments('http://user:pass@foo:21/bar;par?b#c') is '["http:","foo","21","/bar;par","?b","#c"]'
 PASS segments('http:foo.com') is '["http:","foo.com","","/","",""]'
-PASS segments('\t   :foo.com   \n') is '[":","","","","",""]'
-PASS segments(' foo.com  ') is '[":","","","","",""]'
+PASS segments('\t   :foo.com   \n') is '["file:","","","/.../:foo.com","",""]'
+PASS segments(' foo.com  ') is '["file:","","","/.../foo.com","",""]'
 PASS segments('a:\t foo.com') is '["a:","",""," foo.com","",""]'
 PASS segments('http://f:21/ b ? d # e ') is '["http:","f","21","/%20b%20","?%20d%20","# e"]'
 PASS segments('http://f:/c') is '["http:","f","","/c","",""]'
@@ -19,26 +20,26 @@ PASS segments('http://f:\n/c') is '["http:","f","","/c","",""]'
 PASS segments('http://f:fifty-two/c') is '[":","","","","",""]'
 FAIL segments('http://f:999999/c') should be [":","","0","","",""]. Was [":","","","","",""].
 PASS segments('http://f: 21 / b ? d # e ') is '[":","","","","",""]'
-FAIL segments('') should be ["data:","","","text/plain,baseURL","",""]. Was [":","","","","",""].
-FAIL segments('  \t') should be ["data:","","","text/plain,baseURL","",""]. Was [":","","","","",""].
-PASS segments(':foo.com/') is '[":","","","","",""]'
-PASS segments(':foo.com\\') is '[":","","","","",""]'
-PASS segments(':') is '[":","","","","",""]'
-PASS segments(':a') is '[":","","","","",""]'
-PASS segments(':/') is '[":","","","","",""]'
-PASS segments(':\\') is '[":","","","","",""]'
-PASS segments(':#') is '[":","","","","",""]'
-FAIL segments('#') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","",""].
-FAIL segments('#/') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#/"].
-FAIL segments('#\\') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#\\"].
-FAIL segments('#;?') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#;?"].
-PASS segments('?') is '[":","","","","",""]'
-PASS segments('/') is '[":","","","","",""]'
-PASS segments(':23') is '[":","","","","",""]'
-FAIL segments('/:23') should be ["data:","","","/:23","",""]. Was [":","","","","",""].
-PASS segments('//') is '[":","","","","",""]'
-PASS segments('::') is '[":","","","","",""]'
-PASS segments('::23') is '[":","","","","",""]'
+PASS segments('') is '["file:","","","/.../segments-from-data-url.html","",""]'
+PASS segments('  \t') is '["file:","","","/.../segments-from-data-url.html","",""]'
+PASS segments(':foo.com/') is '["file:","","","/.../","",""]'
+PASS segments(':foo.com\\') is '["file:","","","/.../","",""]'
+PASS segments(':') is '["file:","","","/.../:","",""]'
+PASS segments(':a') is '["file:","","","/.../:a","",""]'
+PASS segments(':/') is '["file:","","","/.../","",""]'
+PASS segments(':\\') is '["file:","","","/.../","",""]'
+PASS segments(':#') is '["file:","","","/.../:","",""]'
+PASS segments('#') is '["file:","","","/.../segments-from-data-url.html","",""]'
+PASS segments('#/') is '["file:","","","/.../segments-from-data-url.html","","#/"]'
+PASS segments('#\\') is '["file:","","","/.../segments-from-data-url.html","","#\\\\"]'
+PASS segments('#;?') is '["file:","","","/.../segments-from-data-url.html","","#;?"]'
+PASS segments('?') is '["file:","","","/.../segments-from-data-url.html","",""]'
+PASS segments('/') is '["file:","","","/.../","",""]'
+PASS segments(':23') is '["file:","","","/.../:23","",""]'
+PASS segments('/:23') is '["file:","","","/.../:23","",""]'
+PASS segments('//') is '["file:","","","/.../","",""]'
+PASS segments('::') is '["file:","","","/.../::","",""]'
+PASS segments('::23') is '["file:","","","/.../::23","",""]'
 FAIL segments('foo://') should be ["foo:","","","//","",""]. Was ["foo:","","","","",""].
 PASS segments('http://a:b@c:29/d') is '["http:","c","29","/d","",""]'
 PASS segments('http::@c:29') is '["http:","c","29","/","",""]'
@@ -54,11 +55,11 @@ FAIL segments('foo://///////') should be ["foo:","","","/////////","",""]. Was [
 FAIL segments('foo://///////bar.com/') should be ["foo:","","","/////////bar.com/","",""]. Was ["foo:","","","///////bar.com/","",""].
 FAIL segments('foo:////://///') should be ["foo:","","","////://///","",""]. Was ["foo:","","","//://///","",""].
 PASS segments('c:/foo') is '["c:","","","/foo","",""]'
-PASS segments('//foo/bar') is '[":","","","","",""]'
+PASS segments('//foo/bar') is '["file:","foo","","/.../bar","",""]'
 PASS segments('http://foo/path;a??e#f#g') is '["http:","foo","","/path;a","??e","#f#g"]'
 PASS segments('http://foo/abcd?efgh?ijkl') is '["http:","foo","","/abcd","?efgh?ijkl",""]'
 PASS segments('http://foo/abcd#foo?bar') is '["http:","foo","","/abcd","","#foo?bar"]'
-FAIL segments('[61:24:74]:98') should be ["data:","","","text/[61:24:74]:98","",""]. Was [":","","","","",""].
+PASS segments('[61:24:74]:98') is '["file:","","","/.../[61:24:74]:98","",""]'
 FAIL segments('http://[61:27]:98') should be [":","","0","","",""]. Was [":","","","","",""].
 PASS segments('http:[61:27]/:foo') is '[":","","","","",""]'
 PASS segments('http://[1::2]:3:4') is '[":","","","","",""]'
@@ -70,6 +71,7 @@ PASS segments('http://[2001::1]') is '["http:","[2001::1]","","/","",""]'
 PASS segments('http://[2001::1]:80') is '["http:","[2001::1]","","/","",""]'
 PASS segments('http://[[::]]') is '[":","","","","",""]'
 PASS successfullyParsed is true
+Some tests failed.
 
 TEST COMPLETE
 
index 99103eb..0a28397 100644 (file)
@@ -1,19 +1,22 @@
 <!DOCTYPE html>
 <html>
 <head>
-<script src="../../resources/js-test-pre.js"></script>
+<script src="../../resources/js-test.js"></script>
 <script src="resources/utilities.js"></script>
 </head>
 <body>
 <script>
+if (window.internals && internals.settings)
+  internals.settings.setShouldRestrictBaseURLSchemes(true);
+
 description("Test URL segmentation");
 
 cases = [ 
   // [URL, [SCHEME, HOST, PORT, PATH, QUERY, REF]]
   ["http://user:pass@foo:21/bar;par?b#c",    ["http:","foo","21","/bar;par","?b","#c"]],
   ["http:foo.com",                           ["http:","foo.com","","/","",""]],
-  ["\\t   :foo.com   \\n",                   [":","","","","",""]],
-  [" foo.com  ",                             [":","","","","",""]],
+  ["\\t   :foo.com   \\n",                   ["file:","","","/.../:foo.com","",""]],
+  [" foo.com  ",                             ["file:","","","/.../foo.com","",""]],
   ["a:\\t foo.com",                          ["a:","",""," foo.com","",""]],
   ["http://f:21/ b ? d # e ",                ["http:","f","21","/%20b%20","?%20d%20","# e"]],
   ["http://f:/c",                            ["http:","f","","/c","",""]],
@@ -26,26 +29,26 @@ cases = [
   ["http://f:fifty-two/c",                   [":","","","","",""]],
   ["http://f:999999/c",                      [":","","0","","",""]],
   ["http://f: 21 / b ? d # e ",              [":","","","","",""]],
-  ["",                                       ["data:","","","text/plain,baseURL","",""]],
-  ["  \\t",                                  ["data:","","","text/plain,baseURL","",""]],
-  [":foo.com/",                              [":","","","","",""]],
-  [":foo.com\\\\",                           [":","","","","",""]],
-  [":",                                      [":","","","","",""]],
-  [":a",                                     [":","","","","",""]],
-  [":/",                                     [":","","","","",""]],
-  [":\\\\",                                  [":","","","","",""]],
-  [":#",                                     [":","","","","",""]],
-  ["#",                                      [":","","","","",""]],
-  ["#/",                                     [":","","","","",""]],
-  ["#\\\\",                                  [":","","","","",""]],
-  ["#;?",                                    [":","","","","",""]],
-  ["?",                                      [":","","","","",""]],
-  ["/",                                      [":","","","","",""]],
-  [":23",                                    [":","","","","",""]],
-  ["/:23",                                   ["data:","","","/:23","",""]],
-  ["//",                                     [":","","","","",""]],
-  ["::",                                     [":","","","","",""]],
-  ["::23",                                   [":","","","","",""]],
+  ["",                                       ["file:","","","/.../segments-from-data-url.html","",""]],
+  ["  \\t",                                  ["file:","","","/.../segments-from-data-url.html","",""]],
+  [":foo.com/",                              ["file:","","","/.../","",""]],
+  [":foo.com\\\\",                           ["file:","","","/.../","",""]],
+  [":",                                      ["file:","","","/.../:","",""]],
+  [":a",                                     ["file:","","","/.../:a","",""]],
+  [":/",                                     ["file:","","","/.../","",""]],
+  [":\\\\",                                  ["file:","","","/.../","",""]],
+  [":#",                                     ["file:","","","/.../:","",""]],
+  ["#",                                      ["file:","","","/.../segments-from-data-url.html","",""]],
+  ["#/",                                     ["file:","","","/.../segments-from-data-url.html","","#/"]],
+  ["#\\\\",                                  ["file:","","","/.../segments-from-data-url.html","","#\\\\"]],
+  ["#;?",                                    ["file:","","","/.../segments-from-data-url.html","","#;?"]],
+  ["?",                                      ["file:","","","/.../segments-from-data-url.html","",""]],
+  ["/",                                      ["file:","","","/.../","",""]],
+  [":23",                                    ["file:","","","/.../:23","",""]],
+  ["/:23",                                   ["file:","","","/.../:23","",""]],
+  ["//",                                     ["file:","","","/.../","",""]],
+  ["::",                                     ["file:","","","/.../::","",""]],
+  ["::23",                                   ["file:","","","/.../::23","",""]],
   ["foo://",                                 ["foo:","","","//","",""]],
   ["http://a:b@c:29/d",                      ["http:","c","29","/d","",""]],
   ["http::@c:29",                            ["http:","c","29","/","",""]],
@@ -61,11 +64,11 @@ cases = [
   ["foo://///////bar.com/",                  ["foo:","","","/////////bar.com/","",""]],
   ["foo:////://///",                         ["foo:","","","////://///","",""]],
   ["c:/foo",                                 ["c:","","","/foo","",""]],
-  ["//foo/bar",                              [":","","","","",""]],
+  ["//foo/bar",                              ["file:","foo","","/.../bar","",""]],
   ["http://foo/path;a??e#f#g",               ["http:","foo","","/path;a","??e","#f#g"]],
   ["http://foo/abcd?efgh?ijkl",              ["http:","foo","","/abcd","?efgh?ijkl",""]],
   ["http://foo/abcd#foo?bar",                ["http:","foo","","/abcd","","#foo?bar"]],
-  ["[61:24:74]:98",                          ["data:","","","text/[61:24:74]:98","",""]],
+  ["[61:24:74]:98",                          ["file:","","","/.../[61:24:74]:98","",""]],
   ["http://[61:27]:98",                      [":","","0","","",""]],
   ["http:[61:27]/:foo",                      [":","","","","",""]],
   ["http://[1::2]:3:4",                      [":","","","","",""]],
@@ -80,6 +83,7 @@ cases = [
 
 var originalBaseURL = canonicalize(".");
 setBaseURL("data:text/plain,baseURL");
+setShouldEllipsizeFileURLPaths(true);
 
 for (var i = 0; i < cases.length; ++i) {
   shouldBe("segments('" + cases[i][0] + "')",
@@ -88,6 +92,5 @@ for (var i = 0; i < cases.length; ++i) {
 
 setBaseURL(originalBaseURL);
 </script>
-<script src="../../resources/js-test-post.js"></script>
 </body>
 </html>
diff --git a/LayoutTests/fast/url/segments-from-data-url2-expected.txt b/LayoutTests/fast/url/segments-from-data-url2-expected.txt
new file mode 100644 (file)
index 0000000..1c4ca32
--- /dev/null
@@ -0,0 +1,75 @@
+Test URL segmentation
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS segments('http://user:pass@foo:21/bar;par?b#c') is '["http:","foo","21","/bar;par","?b","#c"]'
+PASS segments('http:foo.com') is '["http:","foo.com","","/","",""]'
+PASS segments('\t   :foo.com   \n') is '[":","","","","",""]'
+PASS segments(' foo.com  ') is '[":","","","","",""]'
+PASS segments('a:\t foo.com') is '["a:","",""," foo.com","",""]'
+PASS segments('http://f:21/ b ? d # e ') is '["http:","f","21","/%20b%20","?%20d%20","# e"]'
+PASS segments('http://f:/c') is '["http:","f","","/c","",""]'
+PASS segments('http://f:0/c') is '["http:","f","0","/c","",""]'
+PASS segments('http://f:00000000000000/c') is '["http:","f","0","/c","",""]'
+FAIL segments('http://f:00000000000000000000080/c') should be ["http:","f","0","/c","",""]. Was ["http:","f","","/c","",""].
+PASS segments('http://f:b/c') is '[":","","","","",""]'
+PASS segments('http://f: /c') is '[":","","","","",""]'
+PASS segments('http://f:\n/c') is '["http:","f","","/c","",""]'
+PASS segments('http://f:fifty-two/c') is '[":","","","","",""]'
+FAIL segments('http://f:999999/c') should be [":","","0","","",""]. Was [":","","","","",""].
+PASS segments('http://f: 21 / b ? d # e ') is '[":","","","","",""]'
+FAIL segments('') should be ["data:","","","text/plain,baseURL","",""]. Was [":","","","","",""].
+FAIL segments('  \t') should be ["data:","","","text/plain,baseURL","",""]. Was [":","","","","",""].
+PASS segments(':foo.com/') is '[":","","","","",""]'
+PASS segments(':foo.com\\') is '[":","","","","",""]'
+PASS segments(':') is '[":","","","","",""]'
+PASS segments(':a') is '[":","","","","",""]'
+PASS segments(':/') is '[":","","","","",""]'
+PASS segments(':\\') is '[":","","","","",""]'
+PASS segments(':#') is '[":","","","","",""]'
+FAIL segments('#') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","",""].
+FAIL segments('#/') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#/"].
+FAIL segments('#\\') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#\\"].
+FAIL segments('#;?') should be [":","","","","",""]. Was ["data:","","","text/plain,baseURL","","#;?"].
+PASS segments('?') is '[":","","","","",""]'
+PASS segments('/') is '[":","","","","",""]'
+PASS segments(':23') is '[":","","","","",""]'
+FAIL segments('/:23') should be ["data:","","","/:23","",""]. Was [":","","","","",""].
+PASS segments('//') is '[":","","","","",""]'
+PASS segments('::') is '[":","","","","",""]'
+PASS segments('::23') is '[":","","","","",""]'
+FAIL segments('foo://') should be ["foo:","","","//","",""]. Was ["foo:","","","","",""].
+PASS segments('http://a:b@c:29/d') is '["http:","c","29","/d","",""]'
+PASS segments('http::@c:29') is '["http:","c","29","/","",""]'
+PASS segments('http://&a:foo(b]c@d:2/') is '["http:","d","2","/","",""]'
+PASS segments('http://::@c@d:2') is '["http:","d","2","/","",""]'
+PASS segments('http://foo.com:b@d/') is '["http:","d","","/","",""]'
+PASS segments('http://foo.com/\\@') is '["http:","foo.com","","//@","",""]'
+PASS segments('http:\\\\foo.com\\') is '["http:","foo.com","","/","",""]'
+PASS segments('http:\\\\a\\b:c\\d@foo.com\\') is '["http:","a","","/b:c/d@foo.com/","",""]'
+PASS segments('foo:/') is '["foo:","","","/","",""]'
+PASS segments('foo:/bar.com/') is '["foo:","","","/bar.com/","",""]'
+FAIL segments('foo://///////') should be ["foo:","","","/////////","",""]. Was ["foo:","","","///////","",""].
+FAIL segments('foo://///////bar.com/') should be ["foo:","","","/////////bar.com/","",""]. Was ["foo:","","","///////bar.com/","",""].
+FAIL segments('foo:////://///') should be ["foo:","","","////://///","",""]. Was ["foo:","","","//://///","",""].
+PASS segments('c:/foo') is '["c:","","","/foo","",""]'
+PASS segments('//foo/bar') is '[":","","","","",""]'
+PASS segments('http://foo/path;a??e#f#g') is '["http:","foo","","/path;a","??e","#f#g"]'
+PASS segments('http://foo/abcd?efgh?ijkl') is '["http:","foo","","/abcd","?efgh?ijkl",""]'
+PASS segments('http://foo/abcd#foo?bar') is '["http:","foo","","/abcd","","#foo?bar"]'
+FAIL segments('[61:24:74]:98') should be ["data:","","","text/[61:24:74]:98","",""]. Was [":","","","","",""].
+FAIL segments('http://[61:27]:98') should be [":","","0","","",""]. Was [":","","","","",""].
+PASS segments('http:[61:27]/:foo') is '[":","","","","",""]'
+PASS segments('http://[1::2]:3:4') is '[":","","","","",""]'
+PASS segments('http://2001::1') is '[":","","","","",""]'
+PASS segments('http://[2001::1') is '[":","","","","",""]'
+PASS segments('http://2001::1]') is '[":","","","","",""]'
+PASS segments('http://2001::1]:80') is '[":","","","","",""]'
+PASS segments('http://[2001::1]') is '["http:","[2001::1]","","/","",""]'
+PASS segments('http://[2001::1]:80') is '["http:","[2001::1]","","/","",""]'
+PASS segments('http://[[::]]') is '[":","","","","",""]'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/url/segments-from-data-url2.html b/LayoutTests/fast/url/segments-from-data-url2.html
new file mode 100644 (file)
index 0000000..4d94eec
--- /dev/null
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+<script src="resources/utilities.js"></script>
+</head>
+<body>
+<script>
+if (window.internals && internals.settings)
+  internals.settings.setShouldRestrictBaseURLSchemes(false);
+
+description("Test URL segmentation");
+
+cases = [ 
+  // [URL, [SCHEME, HOST, PORT, PATH, QUERY, REF]]
+  ["http://user:pass@foo:21/bar;par?b#c",    ["http:","foo","21","/bar;par","?b","#c"]],
+  ["http:foo.com",                           ["http:","foo.com","","/","",""]],
+  ["\\t   :foo.com   \\n",                   [":","","","","",""]],
+  [" foo.com  ",                             [":","","","","",""]],
+  ["a:\\t foo.com",                          ["a:","",""," foo.com","",""]],
+  ["http://f:21/ b ? d # e ",                ["http:","f","21","/%20b%20","?%20d%20","# e"]],
+  ["http://f:/c",                            ["http:","f","","/c","",""]],
+  ["http://f:0/c",                           ["http:","f","0","/c","",""]],
+  ["http://f:00000000000000/c",              ["http:","f","0","/c","",""]],
+  ["http://f:00000000000000000000080/c",     ["http:","f","0","/c","",""]],
+  ["http://f:b/c",                           [":","","","","",""]],
+  ["http://f: /c",                           [":","","","","",""]],
+  ["http://f:\\n/c",                         ["http:","f","","/c","",""]],
+  ["http://f:fifty-two/c",                   [":","","","","",""]],
+  ["http://f:999999/c",                      [":","","0","","",""]],
+  ["http://f: 21 / b ? d # e ",              [":","","","","",""]],
+  ["",                                       ["data:","","","text/plain,baseURL","",""]],
+  ["  \\t",                                  ["data:","","","text/plain,baseURL","",""]],
+  [":foo.com/",                              [":","","","","",""]],
+  [":foo.com\\\\",                           [":","","","","",""]],
+  [":",                                      [":","","","","",""]],
+  [":a",                                     [":","","","","",""]],
+  [":/",                                     [":","","","","",""]],
+  [":\\\\",                                  [":","","","","",""]],
+  [":#",                                     [":","","","","",""]],
+  ["#",                                      [":","","","","",""]],
+  ["#/",                                     [":","","","","",""]],
+  ["#\\\\",                                  [":","","","","",""]],
+  ["#;?",                                    [":","","","","",""]],
+  ["?",                                      [":","","","","",""]],
+  ["/",                                      [":","","","","",""]],
+  [":23",                                    [":","","","","",""]],
+  ["/:23",                                   ["data:","","","/:23","",""]],
+  ["//",                                     [":","","","","",""]],
+  ["::",                                     [":","","","","",""]],
+  ["::23",                                   [":","","","","",""]],
+  ["foo://",                                 ["foo:","","","//","",""]],
+  ["http://a:b@c:29/d",                      ["http:","c","29","/d","",""]],
+  ["http::@c:29",                            ["http:","c","29","/","",""]],
+  ["http://&a:foo(b]c@d:2/",                 ["http:","d","2","/","",""]],
+  ["http://::@c@d:2",                        ["http:","d","2","/","",""]],
+  ["http://foo.com:b@d/",                    ["http:","d","","/","",""]],
+  ["http://foo.com/\\\\@",                   ["http:","foo.com","","//@","",""]],
+  ["http:\\\\\\\\foo.com\\\\",               ["http:","foo.com","","/","",""]],
+  ["http:\\\\\\\\a\\\\b:c\\\\d@foo.com\\\\", ["http:","a","","/b:c/d@foo.com/","",""]],
+  ["foo:/",                                  ["foo:","","","/","",""]],
+  ["foo:/bar.com/",                          ["foo:","","","/bar.com/","",""]],
+  ["foo://///////",                          ["foo:","","","/////////","",""]],
+  ["foo://///////bar.com/",                  ["foo:","","","/////////bar.com/","",""]],
+  ["foo:////://///",                         ["foo:","","","////://///","",""]],
+  ["c:/foo",                                 ["c:","","","/foo","",""]],
+  ["//foo/bar",                              [":","","","","",""]],
+  ["http://foo/path;a??e#f#g",               ["http:","foo","","/path;a","??e","#f#g"]],
+  ["http://foo/abcd?efgh?ijkl",              ["http:","foo","","/abcd","?efgh?ijkl",""]],
+  ["http://foo/abcd#foo?bar",                ["http:","foo","","/abcd","","#foo?bar"]],
+  ["[61:24:74]:98",                          ["data:","","","text/[61:24:74]:98","",""]],
+  ["http://[61:27]:98",                      [":","","0","","",""]],
+  ["http:[61:27]/:foo",                      [":","","","","",""]],
+  ["http://[1::2]:3:4",                      [":","","","","",""]],
+  ["http://2001::1",                         [":","","","","",""]],
+  ["http://[2001::1",                        [":","","","","",""]],
+  ["http://2001::1]",                        [":","","","","",""]],
+  ["http://2001::1]:80",                     [":","","","","",""]],
+  ["http://[2001::1]",                       ["http:","[2001::1]","","/","",""]],
+  ["http://[2001::1]:80",                    ["http:","[2001::1]","","/","",""]],
+  ["http://[[::]]",                          [":","","","","",""]],
+];
+
+var originalBaseURL = canonicalize(".");
+setBaseURL("data:text/plain,baseURL");
+
+for (var i = 0; i < cases.length; ++i) {
+  shouldBe("segments('" + cases[i][0] + "')",
+           "'" + JSON.stringify(cases[i][1]) + "'");
+}
+
+setBaseURL(originalBaseURL);
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index e7a5e97..95c9a26 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 18: Blocked setting data:, as the base URL because it does not have an allowed scheme.
 
 PASS Testing Request url 'http://example       .
 org' with base 'http://example.org/foo/bar' 
@@ -272,7 +273,7 @@ PASS Testing Request url 'http://[google.com]' with base 'http://other.com/'
 PASS Testing Request url 'http://foo:💩@example.com/bar' with base 'http://other.com/' 
 PASS Testing Request url '#' with base 'test:test' 
 PASS Testing Request url '#x' with base 'mailto:x@x.com' 
-PASS Testing Request url '#x' with base 'data:,' 
+FAIL Testing Request url '#x' with base 'data:,' assert_equals: expected "data:,#x" but got "mailto:x@x.com#x"
 PASS Testing Request url '#x' with base 'about:blank' 
 PASS Testing Request url '#' with base 'test:test?test' 
 PASS Testing Request url 'https://@test@test@example:800/' with base 'http://doesnotmatter/' 
index 86e403a..578cee0 100644 (file)
@@ -3,6 +3,9 @@
 <head>
 <script src="/js-test-resources/js-test.js"></script>
 <script>
+if (window.internals && internals.settings)
+    internals.settings.setShouldRestrictBaseURLSchemes(false);
+
 var embed;
 
 function embedLoad() {
index aab27ae..63445b4 100644 (file)
@@ -3,6 +3,9 @@
 <head>
 <script src="/js-test-resources/js-test.js"></script>
 <script>
+if (window.internals && internals.settings)
+    internals.settings.setShouldRestrictBaseURLSchemes(false);
+
 var object;
 
 function objectLoad() {
diff --git a/LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting-expected.txt b/LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting-expected.txt
new file mode 100644 (file)
index 0000000..779542d
--- /dev/null
@@ -0,0 +1,6 @@
+CONSOLE MESSAGE: line 1: ReferenceError: Can't find variable: non
+This test overrides the setting shouldRestrictBaseURLSchemes to false and then sets the base URL to a data URL. This is allowed and hence script is executed.
+
+Note that there will be a JavaScript ReferenceError on success.
+
+PASS
diff --git a/LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting.html b/LayoutTests/http/tests/security/allowed-base-url-data-url-via-setting.html
new file mode 100644 (file)
index 0000000..ccf4847
--- /dev/null
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+if (window.internals && internals.settings)
+    internals.settings.setShouldRestrictBaseURLSchemes(false);
+
+function done()
+{
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function testFailed()
+{
+    document.getElementById("result").textContent = "FAIL";
+    done();
+}
+
+function testPassed()
+{
+    document.getElementById("result").textContent = "PASS";
+    done();
+} 
+</script>
+<base href="data:/,testPassed()/">
+</head>
+<body>
+<p>This test overrides the setting <code>shouldRestrictBaseURLSchemes</code> to <code>false</code> and then sets the base URL to a data URL. This is allowed and hence script is executed.</p>
+<p>Note that there will be a JavaScript ReferenceError on success.</p>
+<p id="result"></p>
+<script src="./non-existent-file.js" onerror="testFailed()"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/denied-base-url-data-url-expected.txt b/LayoutTests/http/tests/security/denied-base-url-data-url-expected.txt
new file mode 100644 (file)
index 0000000..112ad9b
--- /dev/null
@@ -0,0 +1,4 @@
+CONSOLE MESSAGE: line 31: Blocked setting data:/,testFailed()/ as the base URL because it does not have an allowed scheme.
+This tests that setting a base URL to a data URL is denied and hence there is no script execution.
+
+PASS
diff --git a/LayoutTests/http/tests/security/denied-base-url-data-url.html b/LayoutTests/http/tests/security/denied-base-url-data-url.html
new file mode 100644 (file)
index 0000000..9cb44cc
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+if (window.testRunner) {
+    testRunner.dumpAsText();
+    testRunner.waitUntilDone();
+}
+
+if (window.internals && internals.settings)
+    internals.settings.setShouldRestrictBaseURLSchemes(true);
+
+function done()
+{
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+
+function testFailed()
+{
+    document.getElementById("result").textContent = "FAIL";
+    done();
+}
+
+function testPassed()
+{
+    document.getElementById("result").textContent = "PASS";
+    done();
+} 
+</script>
+<base href="data:/,testFailed()/">
+</head>
+<body>
+<p>This tests that setting a base URL to a data URL is denied and hence there is no script execution.</p>
+<p id="result"></p>
+<script src="./non-existent-file.js" onerror="testPassed()"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/denied-base-url-javascript-url-expected.txt b/LayoutTests/http/tests/security/denied-base-url-javascript-url-expected.txt
new file mode 100644 (file)
index 0000000..69a0676
--- /dev/null
@@ -0,0 +1,4 @@
+CONSOLE MESSAGE: line 9: Blocked setting javascript:// This is JavaScript as the base URL because it does not have an allowed scheme.
+
+PASS 'javascript:' is an invalid base URL. 
+
diff --git a/LayoutTests/http/tests/security/denied-base-url-javascript-url.html b/LayoutTests/http/tests/security/denied-base-url-javascript-url.html
new file mode 100644 (file)
index 0000000..f4b533c
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script>
+    if (window.internals && internals.settings)
+      internals.settings.setShouldRestrictBaseURLSchemes(true);
+  </script>
+  <base href="javascript:// This is JavaScript">
+</head>
+<body>
+  <script>
+    async_test(t => {
+      var base = document.querySelector('base');
+      var img = new Image();
+      img.onload = t.step_func_done(_ => {
+        assert_equals(img.naturalWidth, 76, "Image loaded correctly.");
+        assert_equals(img.src, "http://127.0.0.1:8000/security/resources/abe.png");
+      });
+      img.onerror = t.unreached_func("Image should have loaded.");
+      img.src = "/security/resources/abe.png";
+    }, "'javascript:' is an invalid base URL.");
+  </script>
+</body>
index 0dbb508..6bbe117 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 19: Blocked setting data:, as the base URL because it does not have an allowed scheme.
 
 PASS Loading data… 
 PASS Parsing: <http://example  .
@@ -301,7 +302,7 @@ PASS Parsing: <http://[::1.]> against <http://other.com/>
 PASS Parsing: <http://foo:💩@example.com/bar> against <http://other.com/> 
 PASS Parsing: <#> against <test:test> 
 PASS Parsing: <#x> against <mailto:x@x.com> 
-PASS Parsing: <#x> against <data:,> 
+FAIL Parsing: <#x> against <data:,> assert_equals: href expected "data:,#x" but got "mailto:x@x.com#x"
 PASS Parsing: <#x> against <about:blank> 
 PASS Parsing: <#> against <test:test?test> 
 PASS Parsing: <https://@test@test@example:800/> against <http://doesnotmatter/> 
index 0623c64..c1eb5ee 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 19: Blocked setting data:, as the base URL because it does not have an allowed scheme.
 
 PASS Loading data… 
 PASS Parsing origin: <http://example   .
@@ -221,7 +222,7 @@ PASS Parsing origin: <http://0..0x300/> against <about:blank>
 PASS Parsing origin: <http://foo:💩@example.com/bar> against <http://other.com/> 
 FAIL Parsing origin: <#> against <test:test> assert_equals: origin expected "null" but got "test://"
 FAIL Parsing origin: <#x> against <mailto:x@x.com> assert_equals: origin expected "null" but got "mailto://"
-PASS Parsing origin: <#x> against <data:,> 
+FAIL Parsing origin: <#x> against <data:,> assert_equals: origin expected "null" but got "mailto://"
 PASS Parsing origin: <#x> against <about:blank> 
 FAIL Parsing origin: <#> against <test:test?test> assert_equals: origin expected "null" but got "test://"
 PASS Parsing origin: <https://@test@test@example:800/> against <http://doesnotmatter/> 
index 0623c64..c1eb5ee 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 19: Blocked setting data:, as the base URL because it does not have an allowed scheme.
 
 PASS Loading data… 
 PASS Parsing origin: <http://example   .
@@ -221,7 +222,7 @@ PASS Parsing origin: <http://0..0x300/> against <about:blank>
 PASS Parsing origin: <http://foo:💩@example.com/bar> against <http://other.com/> 
 FAIL Parsing origin: <#> against <test:test> assert_equals: origin expected "null" but got "test://"
 FAIL Parsing origin: <#x> against <mailto:x@x.com> assert_equals: origin expected "null" but got "mailto://"
-PASS Parsing origin: <#x> against <data:,> 
+FAIL Parsing origin: <#x> against <data:,> assert_equals: origin expected "null" but got "mailto://"
 PASS Parsing origin: <#x> against <about:blank> 
 FAIL Parsing origin: <#> against <test:test?test> assert_equals: origin expected "null" but got "test://"
 PASS Parsing origin: <https://@test@test@example:800/> against <http://doesnotmatter/> 
index 0dbb508..6bbe117 100644 (file)
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: line 19: Blocked setting data:, as the base URL because it does not have an allowed scheme.
 
 PASS Loading data… 
 PASS Parsing: <http://example  .
@@ -301,7 +302,7 @@ PASS Parsing: <http://[::1.]> against <http://other.com/>
 PASS Parsing: <http://foo:💩@example.com/bar> against <http://other.com/> 
 PASS Parsing: <#> against <test:test> 
 PASS Parsing: <#x> against <mailto:x@x.com> 
-PASS Parsing: <#x> against <data:,> 
+FAIL Parsing: <#x> against <data:,> assert_equals: href expected "data:,#x" but got "mailto:x@x.com#x"
 PASS Parsing: <#x> against <about:blank> 
 PASS Parsing: <#> against <test:test?test> 
 PASS Parsing: <https://@test@test@example:800/> against <http://doesnotmatter/> 
index 7f6a358..4832962 100644 (file)
@@ -1,3 +1,14 @@
+2020-02-10  Daniel Bates  <dabates@apple.com>
+
+        Disallow setting base URL to a data or JavaScript URL
+        https://bugs.webkit.org/show_bug.cgi?id=207136
+
+        Reviewed by Brent Fulgham.
+
+        Add some more macro definitions.
+
+        * wtf/spi/darwin/dyldSPI.h:
+
 2020-02-10  Truitt Savell  <tsavell@apple.com>
 
         Unreviewed, rolling out r256091.
index c8e762d..d7dec8b 100644 (file)
 #define DYLD_IOS_VERSION_13_2 0x000D0200
 #endif
 
+#ifndef DYLD_IOS_VERSION_13_4
+#define DYLD_IOS_VERSION_13_4 0x000D0400
+#endif
+
 #ifndef DYLD_MACOSX_VERSION_10_13
 #define DYLD_MACOSX_VERSION_10_13 0x000A0D00
 #endif
 #define DYLD_MACOSX_VERSION_10_15_1 0x000A0F01
 #endif
 
+#ifndef DYLD_MACOSX_VERSION_10_15_4
+#define DYLD_MACOSX_VERSION_10_15_4 0x000A0F04
+#endif
+
 #else
 
 #define DYLD_IOS_VERSION_3_0 0x00030000
@@ -79,6 +87,7 @@
 #define DYLD_IOS_VERSION_12_0 0x000C0000
 #define DYLD_IOS_VERSION_13_0 0x000D0000
 #define DYLD_IOS_VERSION_13_2 0x000D0200
+#define DYLD_IOS_VERSION_13_4 0x000D0400
 
 #define DYLD_MACOSX_VERSION_10_11 0x000A0B00
 #define DYLD_MACOSX_VERSION_10_12 0x000A0C00
@@ -86,6 +95,7 @@
 #define DYLD_MACOSX_VERSION_10_14 0x000A0E00
 #define DYLD_MACOSX_VERSION_10_15 0x000A0F00
 #define DYLD_MACOSX_VERSION_10_15_1 0x000A0F01
+#define DYLD_MACOSX_VERSION_10_15_4 0x000A0F04
 
 #endif
 
index bceb52b..50a52b9 100644 (file)
@@ -1,3 +1,44 @@
+2020-02-10  Daniel Bates  <dabates@apple.com>
+
+        Disallow setting base URL to a data or JavaScript URL
+        https://bugs.webkit.org/show_bug.cgi?id=207136
+
+        Reviewed by Brent Fulgham.
+
+        Inspired by <https://bugs.chromium.org/p/chromium/issues/detail?id=679318>.
+
+        Block setting the base URL to a data URL or JavaScript URL as such usage is questionable.
+        This makes WebKit match the behavior of Chrome and Firefox and is in the spirit of the
+        discussion in <https://github.com/whatwg/html/issues/2249>.
+
+        On Mac and iOS, this restriction is applied only to apps linked against a future SDK to
+        avoid breaking shipped apps.
+
+        For all other ports, this restriction is enabled by default.
+
+        Tests: fast/url/relative2.html
+               fast/url/segments-from-data-url2.html
+               http/tests/security/allowed-base-url-data-url-via-setting.html
+               http/tests/security/denied-base-url-data-url.html
+               http/tests/security/denied-base-url-javascript-url.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::processBaseElement): Condition updating the parsed
+        base URL on whether is has an allowed scheme, if restrictions are enabled. Otherwise,
+        do what we do now. If the scheme is disallowed then log a message to the console to
+        explain this to web developers.
+        * html/parser/HTMLPreloadScanner.cpp:
+        (WebCore::TokenPreloadScanner::scan): Pass whether to apply restrictons to the base URL
+        to updatePredictedBaseURL(). This depends on whether the setting is enabled or not.
+        (WebCore::TokenPreloadScanner::updatePredictedBaseURL): Modifed to take a boolean as to
+        whether to apply restrictions. If restrictions are not to be applied do what we do now.
+        Otherwise, only do what we do now if the scheme for the predicated base URL is allowed.
+        * html/parser/HTMLPreloadScanner.h:
+        * page/SecurityPolicy.cpp:
+        (WebCore::SecurityPolicy::isBaseURLSchemeAllowed): Added.
+        * page/SecurityPolicy.h:
+        * page/Settings.yaml: Add a setting to toggle restrictions on the base URL scheme.
+
 2020-02-10  Truitt Savell  <tsavell@apple.com>
 
         Unreviewed, rolling out r256091.
index 3312577..858e883 100644 (file)
@@ -3337,8 +3337,12 @@ void Document::processBaseElement()
             baseElementURL = URL(url(), strippedHref);
     }
     if (m_baseElementURL != baseElementURL && contentSecurityPolicy()->allowBaseURI(baseElementURL)) {
-        m_baseElementURL = baseElementURL;
-        updateBaseURL();
+        if (settings().shouldRestrictBaseURLSchemes() && !SecurityPolicy::isBaseURLSchemeAllowed(baseElementURL))
+            addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked setting " + baseElementURL.stringCenterEllipsizedToLength() + " as the base URL because it does not have an allowed scheme.");
+        else {
+            m_baseElementURL = baseElementURL;
+            updateBaseURL();
+        }
     }
 
     m_baseTarget = target ? *target : nullAtom();
index 20a9a39..ebc57cf 100644 (file)
@@ -42,6 +42,7 @@
 #include "MediaQueryParser.h"
 #include "RenderView.h"
 #include "RuntimeEnabledFeatures.h"
+#include "SecurityPolicy.h"
 #include "SizesAttributeParser.h"
 #include <wtf/MainThread.h>
 
@@ -433,7 +434,7 @@ void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<Pr
             // The first <base> element is the one that wins.
             if (!m_predictedBaseElementURL.isEmpty())
                 return;
-            updatePredictedBaseURL(token);
+            updatePredictedBaseURL(token, document.settings().shouldRestrictBaseURLSchemes());
             return;
         }
         if (tagId == TagId::Picture) {
@@ -453,11 +454,15 @@ void TokenPreloadScanner::scan(const HTMLToken& token, Vector<std::unique_ptr<Pr
     }
 }
 
-void TokenPreloadScanner::updatePredictedBaseURL(const HTMLToken& token)
+void TokenPreloadScanner::updatePredictedBaseURL(const HTMLToken& token, bool shouldRestrictBaseURLSchemes)
 {
     ASSERT(m_predictedBaseElementURL.isEmpty());
-    if (auto* hrefAttribute = findAttribute(token.attributes(), hrefAttr->localName().string()))
-        m_predictedBaseElementURL = URL(m_documentURL, stripLeadingAndTrailingHTMLSpaces(StringImpl::create8BitIfPossible(hrefAttribute->value))).isolatedCopy();
+    auto* hrefAttribute = findAttribute(token.attributes(), hrefAttr->localName().string());
+    if (!hrefAttribute)
+        return;
+    URL temp { m_documentURL, stripLeadingAndTrailingHTMLSpaces(StringImpl::create8BitIfPossible(hrefAttribute->value)) };
+    if (!shouldRestrictBaseURLSchemes || SecurityPolicy::isBaseURLSchemeAllowed(temp))
+        m_predictedBaseElementURL = temp.isolatedCopy();
 }
 
 HTMLPreloadScanner::HTMLPreloadScanner(const HTMLParserOptions& options, const URL& documentURL, float deviceScaleFactor)
index 8181da7..39c7048 100644 (file)
@@ -67,7 +67,7 @@ private:
 
     static String initiatorFor(TagId);
 
-    void updatePredictedBaseURL(const HTMLToken&);
+    void updatePredictedBaseURL(const HTMLToken&, bool shouldRestrictBaseURLSchemes);
 
     CSSPreloadScanner m_cssScanner;
     const URL m_documentURL;
index 0deb064..cd16751 100644 (file)
@@ -145,6 +145,12 @@ bool SecurityPolicy::shouldInheritSecurityOriginFromOwner(const URL& url)
     return url.isEmpty() || equalIgnoringASCIICase(url.string(), WTF::blankURL()) || equalLettersIgnoringASCIICase(url.string(), "about:srcdoc");
 }
 
+bool SecurityPolicy::isBaseURLSchemeAllowed(const URL& url)
+{
+    // See <https://github.com/whatwg/html/issues/2249>.
+    return !url.protocolIsData() && !WTF::protocolIsJavaScript(url);
+}
+
 void SecurityPolicy::setLocalLoadPolicy(LocalLoadPolicy policy)
 {
     localLoadPolicy = policy;
index 533b1d4..14bed77 100644 (file)
@@ -52,6 +52,8 @@ public:
 
     static bool shouldInheritSecurityOriginFromOwner(const URL&);
 
+    static bool isBaseURLSchemeAllowed(const URL&);
+
     enum LocalLoadPolicy {
         AllowLocalLoadsForAll, // No restriction on local loads.
         AllowLocalLoadsForLocalAndSubstituteData,
index f3f8d0a..5d3a88c 100644 (file)
@@ -126,6 +126,8 @@ clipboardAccessPolicy:
   initial: ClipboardAccessPolicy::RequiresUserGesture
 asyncClipboardAPIEnabled:
   initial: false
+shouldRestrictBaseURLSchemes:
+  initial: true
 
 textAreasAreResizable:
   initial: false
index a061c82..a326622 100644 (file)
@@ -1,3 +1,18 @@
+2020-02-10  Daniel Bates  <dabates@apple.com>
+
+        Disallow setting base URL to a data or JavaScript URL
+        https://bugs.webkit.org/show_bug.cgi?id=207136
+
+        Reviewed by Brent Fulgham.
+
+        Apply base URL restrictions to apps linked to a future WebKit to avoid breaking existing apps.
+
+        * Shared/WebPreferences.yaml:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (shouldRestrictBaseURLSchemes): Added.
+        (-[WKWebView _setupPageConfiguration:]): Update settings.
+        * UIProcess/Cocoa/VersionChecks.h:
+
 2020-02-10  Chris Dumez  <cdumez@apple.com>
 
         Crash under WebProcessProxy::shouldSendPendingMessage()
index 489e44e..b64a1f7 100644 (file)
@@ -223,6 +223,10 @@ AllowTopNavigationToDataURLs:
   type: bool
   defaultValue: false
 
+ShouldRestrictBaseURLSchemes:
+  type: bool
+  defaultValue: false
+
 AVFoundationEnabled:
   type: bool
   defaultValue: DEFAULT_AVFOUNDATION_ENABLED
index aa9002e..44ad7aa 100644 (file)
@@ -228,6 +228,12 @@ static bool shouldRequireUserGestureToLoadVideo()
 #endif
 }
 
+static bool shouldRestrictBaseURLSchemes()
+{
+    static bool shouldRestrictBaseURLSchemes = linkedOnOrAfter(WebKit::SDKVersion::FirstThatRestrictsBaseURLSchemes);
+    return shouldRestrictBaseURLSchemes;
+}
+
 #if PLATFORM(MAC)
 static uint32_t convertUserInterfaceDirectionPolicy(WKUserInterfaceDirectionPolicy policy)
 {
@@ -443,6 +449,7 @@ static void hardwareKeyboardAvailabilityChangedCallback(CFNotificationCenterRef,
     pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incompleteImageBorderEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _incompleteImageBorderEnabled]));
     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldDeferAsynchronousScriptsUntilAfterDocumentLoadKey(), WebKit::WebPreferencesStore::Value(!![_configuration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]));
+    pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldRestrictBaseURLSchemesKey(), WebKit::WebPreferencesStore::Value(shouldRestrictBaseURLSchemes()));
 
 #if PLATFORM(MAC)
     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::showsURLsInToolTipsEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _showsURLsInToolTips]));
index c318e7e..03b31cb 100644 (file)
@@ -81,6 +81,7 @@ enum class SDKVersion : uint32_t {
     FirstWhereWKContentViewDoesNotOverrideKeyCommands = DYLD_IOS_VERSION_13_0,
     FirstThatSupportsOverflowHiddenOnMainFrame = DYLD_IOS_VERSION_13_0,
     FirstWhereSiteSpecificQuirksAreEnabledByDefault = DYLD_IOS_VERSION_13_2,
+    FirstThatRestrictsBaseURLSchemes = DYLD_IOS_VERSION_13_4,
 #elif PLATFORM(MAC)
     FirstWithNetworkCache = DYLD_MACOSX_VERSION_10_11,
     FirstWithExceptionsForDuplicateCompletionHandlerCalls = DYLD_MACOSX_VERSION_10_13,
@@ -91,6 +92,7 @@ enum class SDKVersion : uint32_t {
     FirstWithSnapshotAfterScreenUpdates = DYLD_MACOS_VERSION_FIRST_WITH_SNAPSHOT_AFTER_SCREEN_UPDATES,
     FirstWithExceptionsForRelatedWebViewsUsingDifferentDataStores = DYLD_MACOS_VERSION_FIRST_WITH_EXCEPTIONS_FOR_RELATED_WEBVIEWS_USING_DIFFERENT_DATA_STORES,
     FirstWhereSiteSpecificQuirksAreEnabledByDefault = DYLD_MACOSX_VERSION_10_15_1,
+    FirstThatRestrictsBaseURLSchemes = DYLD_MACOSX_VERSION_10_15_4,
 #endif
 };
 
index 2d7ac44..b26ec41 100644 (file)
@@ -1,3 +1,17 @@
+2020-02-10  Daniel Bates  <dabates@apple.com>
+
+        Disallow setting base URL to a data or JavaScript URL
+        https://bugs.webkit.org/show_bug.cgi?id=207136
+
+        Reviewed by Brent Fulgham.
+
+        Apply base URL restrictions to apps linked to a future WebKit to avoid breaking existing apps.
+
+        * Misc/WebKitVersionChecks.h:
+        * WebView/WebView.mm:
+        (shouldRestrictBaseURLSchemes): Added.
+        (-[WebView _commonInitializationWithFrameName:groupName:]): Update settings.
+
 2020-02-10  Truitt Savell  <tsavell@apple.com>
 
         Unreviewed, rolling out r256091.
index 9582c42..7ada7e9 100644 (file)
 enum class SDKVersion : uint32_t {
 #if PLATFORM(IOS_FAMILY)
     FirstThatDefaultsToPassiveTouchListenersOnDocument = DYLD_IOS_VERSION_11_3,
+    FirstThatRestrictsBaseURLSchemes = DYLD_IOS_VERSION_13_4,
 #else
     FirstWithDropToNavigateDisallowedByDefault = DYLD_MACOSX_VERSION_10_13,
     FirstWithWebIconDatabaseWarning = DYLD_MACOSX_VERSION_10_13,
+    FirstThatRestrictsBaseURLSchemes = DYLD_MACOSX_VERSION_10_15_4,
 #endif
 };
 
index d45ac9a..22a8804 100644 (file)
@@ -1323,6 +1323,12 @@ static bool shouldRequireUserGestureToLoadVideo()
 #endif
 }
 
+static bool shouldRestrictBaseURLSchemes()
+{
+    static bool shouldRestrictBaseURLSchemes = linkedOnOrAfter(SDKVersion::FirstThatRestrictsBaseURLSchemes);
+    return shouldRestrictBaseURLSchemes;
+}
+
 #if ENABLE(GAMEPAD)
 static void WebKitInitializeGamepadProviderIfNecessary()
 {
@@ -1488,6 +1494,7 @@ static void WebKitInitializeGamepadProviderIfNecessary()
     _private->page->setCanStartMedia([self window]);
     _private->page->settings().setLocalStorageDatabasePath([[self preferences] _localStorageDatabasePath]);
     _private->page->settings().setUseLegacyBackgroundSizeShorthandBehavior(shouldUseLegacyBackgroundSizeShorthandBehavior());
+    _private->page->settings().setShouldRestrictBaseURLSchemes(shouldRestrictBaseURLSchemes());
 
 #if !PLATFORM(IOS_FAMILY)
     if (needsOutlookQuirksScript()) {