summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-at-unique-origin-denied.html12
-rw-r--r--third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-whitelisted-at-unique-origin-denied.html11
-rw-r--r--third_party/WebKit/Source/core/core.gypi1
-rw-r--r--third_party/WebKit/Source/core/frame/History.cpp19
-rw-r--r--third_party/WebKit/Source/core/frame/History.h11
-rw-r--r--third_party/WebKit/Source/core/frame/HistoryTest.cpp116
6 files changed, 156 insertions, 14 deletions
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-at-unique-origin-denied.html b/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-at-unique-origin-denied.html
index a3f0189..ee8c324 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-at-unique-origin-denied.html
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-at-unique-origin-denied.html
@@ -4,7 +4,15 @@
<script>
test(function () {
assert_throws('SecurityError', function () {
- history.pushState(null, null, document.URL);
+ history.pushState(null, null, document.URL + "/path");
});
-}, 'pushState at unique origin should fail with SecurityError');
+}, 'pushState to a new path in unique origin should fail with SecurityError');
+test(function () {
+ try {
+ history.pushState(null, null, document.URL + "#hash");
+ done();
+ } catch (e) {
+ assert_unreached("pushState to a new hash should not fail.");
+ }
+}, 'pushState to new hash in unique origin should not fail with SecurityError');
</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-whitelisted-at-unique-origin-denied.html b/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-whitelisted-at-unique-origin-denied.html
index c89da59..7a48831 100644
--- a/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-whitelisted-at-unique-origin-denied.html
+++ b/third_party/WebKit/LayoutTests/http/tests/navigation/pushstate-whitelisted-at-unique-origin-denied.html
@@ -8,7 +8,16 @@ test(function () {
test(function () {
assert_throws('SecurityError', function () {
- history.pushState(null, null, document.URL);
+ history.pushState(null, null, document.URL + "/path");
});
}, 'pushState at unique origin should fail with SecurityError (even with whitelisted origins)');
+
+test(function () {
+ try {
+ history.pushState(null, null, document.URL + "#hash");
+ done();
+ } catch (e) {
+ assert_unreached("pushState to a new hash should not fail.");
+ }
+}, 'pushState to new hash in unique origin should not fail with SecurityError');
</script>
diff --git a/third_party/WebKit/Source/core/core.gypi b/third_party/WebKit/Source/core/core.gypi
index a14d16f..cd89ae0 100644
--- a/third_party/WebKit/Source/core/core.gypi
+++ b/third_party/WebKit/Source/core/core.gypi
@@ -3949,6 +3949,7 @@
'fileapi/FileListTest.cpp',
'fileapi/FileTest.cpp',
'frame/FrameViewTest.cpp',
+ 'frame/HistoryTest.cpp',
'frame/ImageBitmapTest.cpp',
'frame/OriginsUsingFeaturesTest.cpp',
'frame/RootFrameViewportTest.cpp',
diff --git a/third_party/WebKit/Source/core/frame/History.cpp b/third_party/WebKit/Source/core/frame/History.cpp
index cfbaefc..0e6fee8 100644
--- a/third_party/WebKit/Source/core/frame/History.cpp
+++ b/third_party/WebKit/Source/core/frame/History.cpp
@@ -182,24 +182,25 @@ KURL History::urlForState(const String& urlString)
return KURL(document->baseURL(), urlString);
}
-bool History::canChangeToUrl(const KURL& url)
+bool History::canChangeToUrl(const KURL& url, SecurityOrigin* documentOrigin, const KURL& documentURL)
{
if (!url.isValid())
return false;
- Document* document = m_frame->document();
- SecurityOrigin* origin = document->securityOrigin();
- if (origin->isGrantedUniversalAccess())
+ if (documentOrigin->isGrantedUniversalAccess())
return true;
- if (origin->isUnique())
- return false;
+ // We allow sandboxed documents, `data:`/`file:` URLs, etc. to use
+ // 'pushState'/'replaceState' to modify the URL fragment: see
+ // https://crbug.com/528681 for the compatibility concerns.
+ if (documentOrigin->isUnique() || documentOrigin->isLocal())
+ return equalIgnoringFragmentIdentifier(url, documentURL);
- if (!equalIgnoringPathQueryAndFragment(url, document->url()))
+ if (!equalIgnoringPathQueryAndFragment(url, documentURL))
return false;
RefPtr<SecurityOrigin> requestedOrigin = SecurityOrigin::create(url);
- if (requestedOrigin->isUnique() || !requestedOrigin->isSameSchemeHostPort(origin))
+ if (requestedOrigin->isUnique() || !requestedOrigin->isSameSchemeHostPort(documentOrigin))
return false;
return true;
@@ -211,7 +212,7 @@ void History::stateObjectAdded(PassRefPtr<SerializedScriptValue> data, const Str
return;
KURL fullURL = urlForState(urlString);
- if (!canChangeToUrl(fullURL)) {
+ if (!canChangeToUrl(fullURL, m_frame->document()->securityOrigin(), m_frame->document()->url())) {
// We can safely expose the URL to JavaScript, as a) no redirection takes place: JavaScript already had this URL, b) JavaScript can only access a same-origin History object.
exceptionState.throwSecurityError("A history state object with URL '" + fullURL.elidedString() + "' cannot be created in a document with origin '" + m_frame->document()->securityOrigin()->toString() + "' and URL '" + m_frame->document()->url().elidedString() + "'.");
return;
diff --git a/third_party/WebKit/Source/core/frame/History.h b/third_party/WebKit/Source/core/frame/History.h
index 626f64a..4b63ba5 100644
--- a/third_party/WebKit/Source/core/frame/History.h
+++ b/third_party/WebKit/Source/core/frame/History.h
@@ -26,6 +26,7 @@
#ifndef History_h
#define History_h
+#include "base/gtest_prod_util.h"
#include "bindings/core/v8/ScriptWrappable.h"
#include "bindings/core/v8/SerializedScriptValue.h"
#include "core/loader/FrameLoaderTypes.h"
@@ -39,8 +40,9 @@ class LocalFrame;
class KURL;
class ExecutionContext;
class ExceptionState;
+class SecurityOrigin;
-class History final : public GarbageCollectedFinalized<History>, public ScriptWrappable, public DOMWindowProperty {
+class CORE_EXPORT History final : public GarbageCollectedFinalized<History>, public ScriptWrappable, public DOMWindowProperty {
DEFINE_WRAPPERTYPEINFO();
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(History);
public:
@@ -76,10 +78,15 @@ public:
DECLARE_VIRTUAL_TRACE();
private:
+ FRIEND_TEST_ALL_PREFIXES(HistoryTest, CanChangeToURL);
+ FRIEND_TEST_ALL_PREFIXES(HistoryTest, CanChangeToURLInFileOrigin);
+ FRIEND_TEST_ALL_PREFIXES(HistoryTest, CanChangeToURLInUniqueOrigin);
+
explicit History(LocalFrame*);
+ static bool canChangeToUrl(const KURL&, SecurityOrigin*, const KURL& documentURL);
+
KURL urlForState(const String& url);
- bool canChangeToUrl(const KURL& url);
void stateObjectAdded(PassRefPtr<SerializedScriptValue>, const String& title, const String& url, HistoryScrollRestorationType, FrameLoadType, ExceptionState&);
SerializedScriptValue* stateInternal() const;
diff --git a/third_party/WebKit/Source/core/frame/HistoryTest.cpp b/third_party/WebKit/Source/core/frame/HistoryTest.cpp
new file mode 100644
index 0000000..bb5e573
--- /dev/null
+++ b/third_party/WebKit/Source/core/frame/HistoryTest.cpp
@@ -0,0 +1,116 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/frame/History.h"
+
+#include "platform/weborigin/KURL.h"
+#include "platform/weborigin/SecurityOrigin.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+class HistoryTest : public ::testing::Test {
+};
+
+TEST_F(HistoryTest, CanChangeToURL)
+{
+ struct TestCase {
+ const char* url;
+ const char* documentURL;
+ bool expected;
+ } cases[] = {
+ {"http://example.com/", "http://example.com/", true},
+ {"http://example.com/#hash", "http://example.com/", true},
+ {"http://example.com/path", "http://example.com/", true},
+ {"http://example.com/path#hash", "http://example.com/", true},
+ {"http://example.com/path?query", "http://example.com/", true},
+ {"http://example.com/path?query#hash", "http://example.com/", true},
+ {"http://example.com:80/", "http://example.com/", true},
+ {"http://example.com:80/#hash", "http://example.com/", true},
+ {"http://example.com:80/path", "http://example.com/", true},
+ {"http://example.com:80/path#hash", "http://example.com/", true},
+ {"http://example.com:80/path?query", "http://example.com/", true},
+ {"http://example.com:80/path?query#hash", "http://example.com/", true},
+ {"http://not-example.com:80/", "http://example.com/", false},
+ {"http://not-example.com:80/#hash", "http://example.com/", false},
+ {"http://not-example.com:80/path", "http://example.com/", false},
+ {"http://not-example.com:80/path#hash", "http://example.com/", false},
+ {"http://not-example.com:80/path?query", "http://example.com/", false},
+ {"http://not-example.com:80/path?query#hash", "http://example.com/", false},
+ {"http://example.com:81/", "http://example.com/", false},
+ {"http://example.com:81/#hash", "http://example.com/", false},
+ {"http://example.com:81/path", "http://example.com/", false},
+ {"http://example.com:81/path#hash", "http://example.com/", false},
+ {"http://example.com:81/path?query", "http://example.com/", false},
+ {"http://example.com:81/path?query#hash", "http://example.com/", false},
+ };
+
+ for (const auto& test : cases) {
+ KURL url(ParsedURLString, test.url);
+ KURL documentURL(ParsedURLString, test.documentURL);
+ RefPtr<SecurityOrigin> documentOrigin = SecurityOrigin::create(documentURL);
+ EXPECT_EQ(test.expected, History::canChangeToUrl(url, documentOrigin.get(), documentURL));
+ }
+}
+
+TEST_F(HistoryTest, CanChangeToURLInFileOrigin)
+{
+ struct TestCase {
+ const char* url;
+ const char* documentURL;
+ bool expected;
+ } cases[] = {
+ {"file:///path/to/file/", "file:///path/to/file/", true},
+ {"file:///path/to/file/#hash", "file:///path/to/file/", true},
+ {"file:///path/to/file/path", "file:///path/to/file/", false},
+ {"file:///path/to/file/path#hash", "file:///path/to/file/", false},
+ {"file:///path/to/file/path?query", "file:///path/to/file/", false},
+ {"file:///path/to/file/path?query#hash", "file:///path/to/file/", false},
+ };
+
+ for (const auto& test : cases) {
+ KURL url(ParsedURLString, test.url);
+ KURL documentURL(ParsedURLString, test.documentURL);
+ RefPtr<SecurityOrigin> documentOrigin = SecurityOrigin::create(documentURL);
+ EXPECT_EQ(test.expected, History::canChangeToUrl(url, documentOrigin.get(), documentURL));
+ }
+}
+
+TEST_F(HistoryTest, CanChangeToURLInUniqueOrigin)
+{
+ struct TestCase {
+ const char* url;
+ const char* documentURL;
+ bool expected;
+ } cases[] = {
+ {"http://example.com/", "http://example.com/", true},
+ {"http://example.com/#hash", "http://example.com/", true},
+ {"http://example.com/path", "http://example.com/", false},
+ {"http://example.com/path#hash", "http://example.com/", false},
+ {"http://example.com/path?query", "http://example.com/", false},
+ {"http://example.com/path?query#hash", "http://example.com/", false},
+ {"http://example.com:80/", "http://example.com/", true},
+ {"http://example.com:80/#hash", "http://example.com/", true},
+ {"http://example.com:80/path", "http://example.com/", false},
+ {"http://example.com:80/path#hash", "http://example.com/", false},
+ {"http://example.com:80/path?query", "http://example.com/", false},
+ {"http://example.com:80/path?query#hash", "http://example.com/", false},
+ {"http://example.com:81/", "http://example.com/", false},
+ {"http://example.com:81/#hash", "http://example.com/", false},
+ {"http://example.com:81/path", "http://example.com/", false},
+ {"http://example.com:81/path#hash", "http://example.com/", false},
+ {"http://example.com:81/path?query", "http://example.com/", false},
+ {"http://example.com:81/path?query#hash", "http://example.com/", false},
+ };
+
+ for (const auto& test : cases) {
+ KURL url(ParsedURLString, test.url);
+ KURL documentURL(ParsedURLString, test.documentURL);
+ RefPtr<SecurityOrigin> documentOrigin = SecurityOrigin::createUnique();
+ EXPECT_EQ(test.expected, History::canChangeToUrl(url, documentOrigin.get(), documentURL));
+ }
+}
+
+} // namespace blink
+