Introduce AncestorThrottle, which will process 'X-Frame-Options' headers.
This moves the ancestor-based blocking behavior from Blink up into the
browser, and depends on https://codereview.chromium.org/1616943003 for
some infrastructure changes.
BUG=555418
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_site_isolation
Review-Url: https://codereview.chromium.org/1617043002
Cr-Commit-Position: refs/heads/master@{#392032}
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index 3a84ddb..dd5fe55 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -92,6 +92,7 @@
int suggestions; // Bitmap of SUGGEST_* values.
};
+// clang-format off
const LocalizedErrorMap net_error_options[] = {
{net::ERR_TIMED_OUT,
IDS_ERRORPAGES_TITLE_NOT_AVAILABLE,
@@ -352,7 +353,15 @@
IDS_ERRORPAGES_DETAILS_SSL_PROTOCOL_ERROR,
SUGGEST_NONE,
},
+ {net::ERR_BLOCKED_BY_RESPONSE,
+ IDS_ERRORPAGES_TITLE_BLOCKED,
+ IDS_ERRORPAGES_HEADING_BLOCKED,
+ IDS_ERRORPAGES_SUMMARY_CONNECTION_REFUSED,
+ IDS_ERRORPAGES_DETAILS_CONNECTION_REFUSED,
+ SUGGEST_NONE,
+ },
};
+// clang-format on
// Special error page to be used in the case of navigating back to a page
// generated by a POST. LocalizedError::HasStrings expects this net error code
diff --git a/content/browser/frame_host/ancestor_throttle.cc b/content/browser/frame_host/ancestor_throttle.cc
new file mode 100644
index 0000000..0a148c0
--- /dev/null
+++ b/content/browser/frame_host/ancestor_throttle.cc
@@ -0,0 +1,186 @@
+// 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 "content/browser/frame_host/ancestor_throttle.h"
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "content/browser/frame_host/frame_tree.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/navigation_handle_impl.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/common/console_message_level.h"
+#include "net/http/http_response_headers.h"
+#include "url/origin.h"
+
+namespace content {
+
+// static
+std::unique_ptr<NavigationThrottle> AncestorThrottle::MaybeCreateThrottleFor(
+ NavigationHandle* handle) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ if (handle->IsInMainFrame())
+ return nullptr;
+
+ return std::unique_ptr<NavigationThrottle>(new AncestorThrottle(handle));
+}
+
+AncestorThrottle::AncestorThrottle(NavigationHandle* handle)
+ : NavigationThrottle(handle) {}
+
+AncestorThrottle::~AncestorThrottle() {}
+
+NavigationThrottle::ThrottleCheckResult
+AncestorThrottle::WillProcessResponse() {
+ DCHECK(!navigation_handle()->IsInMainFrame());
+
+ NavigationHandleImpl* handle =
+ static_cast<NavigationHandleImpl*>(navigation_handle());
+
+ std::string header_value;
+ HeaderDisposition disposition =
+ ParseHeader(handle->GetResponseHeaders(), &header_value);
+ switch (disposition) {
+ case HeaderDisposition::CONFLICT:
+ ParseError(header_value, disposition);
+ return NavigationThrottle::BLOCK_RESPONSE;
+
+ case HeaderDisposition::INVALID:
+ ParseError(header_value, disposition);
+ // TODO(mkwst): Consider failing here.
+ return NavigationThrottle::PROCEED;
+
+ case HeaderDisposition::DENY:
+ ConsoleError(disposition);
+ return NavigationThrottle::BLOCK_RESPONSE;
+
+ case HeaderDisposition::SAMEORIGIN: {
+ url::Origin current_origin(navigation_handle()->GetURL());
+ url::Origin top_origin =
+ handle->frame_tree_node()->frame_tree()->root()->current_origin();
+ if (top_origin.IsSameOriginWith(current_origin))
+ return NavigationThrottle::PROCEED;
+ ConsoleError(disposition);
+ return NavigationThrottle::BLOCK_RESPONSE;
+ }
+
+ case HeaderDisposition::NONE:
+ case HeaderDisposition::BYPASS:
+ case HeaderDisposition::ALLOWALL:
+ return NavigationThrottle::PROCEED;
+ }
+ NOTREACHED();
+ return NavigationThrottle::BLOCK_RESPONSE;
+}
+
+void AncestorThrottle::ParseError(const std::string& value,
+ HeaderDisposition disposition) {
+ DCHECK(disposition == HeaderDisposition::CONFLICT ||
+ disposition == HeaderDisposition::INVALID);
+
+ std::string message;
+ if (disposition == HeaderDisposition::CONFLICT) {
+ message = base::StringPrintf(
+ "Refused to display '%s' in a frame because it set multiple "
+ "'X-Frame-Options' headers with conflicting values "
+ "('%s'). Falling back to 'deny'.",
+ navigation_handle()->GetURL().spec().c_str(), value.c_str());
+ } else {
+ message = base::StringPrintf(
+ "Invalid 'X-Frame-Options' header encountered when loading '%s': "
+ "'%s' is not a recognized directive. The header will be ignored.",
+ navigation_handle()->GetURL().spec().c_str(), value.c_str());
+ }
+
+ // Log a console error in the parent of the current RenderFrameHost (as
+ // the current RenderFrameHost itself doesn't yet have a document).
+ navigation_handle()->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
+ CONSOLE_MESSAGE_LEVEL_ERROR, message);
+}
+
+void AncestorThrottle::ConsoleError(HeaderDisposition disposition) {
+ DCHECK(disposition == HeaderDisposition::DENY ||
+ disposition == HeaderDisposition::SAMEORIGIN);
+ std::string message = base::StringPrintf(
+ "Refused to display '%s' in a frame because it set 'X-Frame-Options' "
+ "to '%s'.",
+ navigation_handle()->GetURL().spec().c_str(),
+ disposition == HeaderDisposition::DENY ? "deny" : "sameorigin");
+
+ // Log a console error in the parent of the current RenderFrameHost (as
+ // the current RenderFrameHost itself doesn't yet have a document).
+ navigation_handle()->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
+ CONSOLE_MESSAGE_LEVEL_ERROR, message);
+}
+
+AncestorThrottle::HeaderDisposition AncestorThrottle::ParseHeader(
+ const net::HttpResponseHeaders* headers,
+ std::string* header_value) {
+ DCHECK(header_value);
+ if (!headers)
+ return HeaderDisposition::NONE;
+
+ // Process the 'X-Frame-Options header as per Section 2 of RFC7034:
+ // https://tools.ietf.org/html/rfc7034#section-2
+ //
+ // Note that we do not support the 'ALLOW-FROM' value, and we special-case
+ // the invalid "ALLOWALL" value due to its prevalance in the wild.
+ HeaderDisposition result = HeaderDisposition::NONE;
+ size_t iter = 0;
+ std::string value;
+ while (headers->EnumerateHeader(&iter, "x-frame-options", &value)) {
+ HeaderDisposition current = HeaderDisposition::INVALID;
+
+ base::StringPiece trimmed =
+ base::TrimWhitespaceASCII(value, base::TRIM_ALL);
+ if (!header_value->empty())
+ header_value->append(", ");
+ header_value->append(trimmed.as_string());
+
+ if (base::LowerCaseEqualsASCII(trimmed, "deny"))
+ current = HeaderDisposition::DENY;
+ else if (base::LowerCaseEqualsASCII(trimmed, "allowall"))
+ current = HeaderDisposition::ALLOWALL;
+ else if (base::LowerCaseEqualsASCII(trimmed, "sameorigin"))
+ current = HeaderDisposition::SAMEORIGIN;
+ else
+ current = HeaderDisposition::INVALID;
+
+ if (result == HeaderDisposition::NONE)
+ result = current;
+ else if (result != current)
+ result = HeaderDisposition::CONFLICT;
+ }
+
+ // If 'X-Frame-Options' would potentially block the response, check whether
+ // the 'frame-ancestors' CSP directive should take effect instead. See
+ // https://www.w3.org/TR/CSP/#frame-ancestors-and-frame-options
+ if (result != HeaderDisposition::NONE &&
+ result != HeaderDisposition::ALLOWALL) {
+ iter = 0;
+ value = std::string();
+ while (headers->EnumerateHeader(&iter, "content-security-policy", &value)) {
+ // TODO(mkwst): 'frame-ancestors' is currently handled in Blink. We should
+ // handle it here instead. Until then, don't block the request, and let
+ // Blink handle it. https://crbug.com/555418
+ std::vector<std::string> tokens = base::SplitString(
+ value, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
+ if (std::count_if(tokens.begin(), tokens.end(), [](std::string token) {
+ // The trailing " " is intentional; we'd otherwise match
+ // "frame-ancestors-is-not-this-directive".
+ return base::StartsWith(token, "frame-ancestors ",
+ base::CompareCase::INSENSITIVE_ASCII);
+ })) {
+ return HeaderDisposition::BYPASS;
+ }
+ }
+ }
+ return result;
+}
+
+} // namespace content
diff --git a/content/browser/frame_host/ancestor_throttle.h b/content/browser/frame_host/ancestor_throttle.h
new file mode 100644
index 0000000..2d13328
--- /dev/null
+++ b/content/browser/frame_host/ancestor_throttle.h
@@ -0,0 +1,67 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_ANCESTOR_THROTTLE_H_
+#define CONTENT_BROWSER_FRAME_HOST_ANCESTOR_THROTTLE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "content/public/browser/navigation_throttle.h"
+
+namespace content {
+class NavigationHandle;
+}
+
+namespace net {
+class HttpResponseHeaders;
+}
+
+namespace content {
+
+// An AncestorThrottle is responsible for enforcing a resource's embedding
+// rules, and blocking requests which violate them.
+class CONTENT_EXPORT AncestorThrottle : public NavigationThrottle {
+ public:
+ enum class HeaderDisposition {
+ NONE = 0,
+ DENY,
+ SAMEORIGIN,
+ ALLOWALL,
+ INVALID,
+ CONFLICT,
+ BYPASS
+ };
+
+ static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
+ NavigationHandle* handle);
+
+ explicit AncestorThrottle(NavigationHandle* handle);
+ ~AncestorThrottle() override;
+
+ NavigationThrottle::ThrottleCheckResult WillProcessResponse() override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ParsingXFrameOptions);
+ FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest, ErrorsParsingXFrameOptions);
+ FRIEND_TEST_ALL_PREFIXES(AncestorThrottleTest,
+ IgnoreWhenFrameAncestorsPresent);
+
+ void ParseError(const std::string& value, HeaderDisposition disposition);
+ void ConsoleError(HeaderDisposition disposition);
+
+ // Parses an 'X-Frame-Options' header. If the result is either CONFLICT
+ // or INVALID, |header_value| will be populated with the value which caused
+ // the parse error.
+ HeaderDisposition ParseHeader(const net::HttpResponseHeaders* headers,
+ std::string* header_value);
+
+ DISALLOW_COPY_AND_ASSIGN(AncestorThrottle);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_FRAME_HOST_ANCESTOR_THROTTLE_H_
diff --git a/content/browser/frame_host/ancestor_throttle_unittest.cc b/content/browser/frame_host/ancestor_throttle_unittest.cc
new file mode 100644
index 0000000..e0e8667
--- /dev/null
+++ b/content/browser/frame_host/ancestor_throttle_unittest.cc
@@ -0,0 +1,183 @@
+// 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 "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ref_counted.h"
+#include "content/browser/frame_host/ancestor_throttle.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/http/http_response_headers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+using HeaderDisposition = AncestorThrottle::HeaderDisposition;
+
+net::HttpResponseHeaders* GetAncestorHeaders(const char* xfo, const char* csp) {
+ std::string header_string("HTTP/1.1 200 OK\nX-Frame-Options: ");
+ header_string += xfo;
+ if (csp != nullptr) {
+ header_string += "\nContent-Security-Policy: ";
+ header_string += csp;
+ }
+ header_string += "\n\n";
+ std::replace(header_string.begin(), header_string.end(), '\n', '\0');
+ net::HttpResponseHeaders* headers =
+ new net::HttpResponseHeaders(header_string);
+ EXPECT_TRUE(headers->HasHeader("X-Frame-Options"));
+ if (csp != nullptr)
+ EXPECT_TRUE(headers->HasHeader("Content-Security-Policy"));
+ return headers;
+}
+
+} // namespace
+
+// AncestorThrottleTest
+// -------------------------------------------------------------
+
+class AncestorThrottleTest : public testing::Test {};
+
+TEST_F(AncestorThrottleTest, ParsingXFrameOptions) {
+ struct TestCase {
+ const char* header;
+ AncestorThrottle::HeaderDisposition expected;
+ const char* value;
+ } cases[] = {
+ // Basic keywords
+ {"DENY", HeaderDisposition::DENY, "DENY"},
+ {"SAMEORIGIN", HeaderDisposition::SAMEORIGIN, "SAMEORIGIN"},
+ {"ALLOWALL", HeaderDisposition::ALLOWALL, "ALLOWALL"},
+
+ // Repeated keywords
+ {"DENY,DENY", HeaderDisposition::DENY, "DENY, DENY"},
+ {"SAMEORIGIN,SAMEORIGIN", HeaderDisposition::SAMEORIGIN,
+ "SAMEORIGIN, SAMEORIGIN"},
+ {"ALLOWALL,ALLOWALL", HeaderDisposition::ALLOWALL, "ALLOWALL, ALLOWALL"},
+
+ // Case-insensitive
+ {"deNy", HeaderDisposition::DENY, "deNy"},
+ {"sAmEorIgIn", HeaderDisposition::SAMEORIGIN, "sAmEorIgIn"},
+ {"AlLOWaLL", HeaderDisposition::ALLOWALL, "AlLOWaLL"},
+
+ // Trim whitespace
+ {" DENY", HeaderDisposition::DENY, "DENY"},
+ {"SAMEORIGIN ", HeaderDisposition::SAMEORIGIN, "SAMEORIGIN"},
+ {" ALLOWALL ", HeaderDisposition::ALLOWALL, "ALLOWALL"},
+ {" DENY", HeaderDisposition::DENY, "DENY"},
+ {"SAMEORIGIN ", HeaderDisposition::SAMEORIGIN, "SAMEORIGIN"},
+ {" ALLOWALL ", HeaderDisposition::ALLOWALL, "ALLOWALL"},
+ {" DENY , DENY ", HeaderDisposition::DENY, "DENY, DENY"},
+ {"SAMEORIGIN, SAMEORIGIN", HeaderDisposition::SAMEORIGIN,
+ "SAMEORIGIN, SAMEORIGIN"},
+ {"ALLOWALL ,ALLOWALL", HeaderDisposition::ALLOWALL,
+ "ALLOWALL, ALLOWALL"},
+ };
+
+ AncestorThrottle throttle(nullptr);
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.header);
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ GetAncestorHeaders(test.header, nullptr);
+ std::string header_value;
+ EXPECT_EQ(test.expected,
+ throttle.ParseHeader(headers.get(), &header_value));
+ EXPECT_EQ(test.value, header_value);
+ }
+}
+
+TEST_F(AncestorThrottleTest, ErrorsParsingXFrameOptions) {
+ struct TestCase {
+ const char* header;
+ AncestorThrottle::HeaderDisposition expected;
+ const char* failure;
+ } cases[] = {
+ // Empty == Invalid.
+ {"", HeaderDisposition::INVALID, ""},
+
+ // Invalid
+ {"INVALID", HeaderDisposition::INVALID, "INVALID"},
+ {"INVALID DENY", HeaderDisposition::INVALID, "INVALID DENY"},
+ {"DENY DENY", HeaderDisposition::INVALID, "DENY DENY"},
+ {"DE NY", HeaderDisposition::INVALID, "DE NY"},
+
+ // Conflicts
+ {"INVALID,DENY", HeaderDisposition::CONFLICT, "INVALID, DENY"},
+ {"DENY,ALLOWALL", HeaderDisposition::CONFLICT, "DENY, ALLOWALL"},
+ {"SAMEORIGIN,DENY", HeaderDisposition::CONFLICT, "SAMEORIGIN, DENY"},
+ {"ALLOWALL,SAMEORIGIN", HeaderDisposition::CONFLICT,
+ "ALLOWALL, SAMEORIGIN"},
+ {"DENY, SAMEORIGIN", HeaderDisposition::CONFLICT, "DENY, SAMEORIGIN"}};
+
+ AncestorThrottle throttle(nullptr);
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.header);
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ GetAncestorHeaders(test.header, nullptr);
+ std::string header_value;
+ EXPECT_EQ(test.expected,
+ throttle.ParseHeader(headers.get(), &header_value));
+ EXPECT_EQ(test.failure, header_value);
+ }
+}
+
+TEST_F(AncestorThrottleTest, IgnoreWhenFrameAncestorsPresent) {
+ struct TestCase {
+ const char* csp;
+ AncestorThrottle::HeaderDisposition expected;
+ } cases[] = {
+ {"", HeaderDisposition::DENY},
+ {"frame-ancestors 'none'", HeaderDisposition::BYPASS},
+ {"frame-ancestors *", HeaderDisposition::BYPASS},
+ {"frame-ancestors 'self'", HeaderDisposition::BYPASS},
+ {"frame-ancestors https://example.com", HeaderDisposition::BYPASS},
+ {"fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
+ {"directive1; frame-ancestors 'none'", HeaderDisposition::BYPASS},
+ {"directive1; frame-ancestors *", HeaderDisposition::BYPASS},
+ {"directive1; frame-ancestors 'self'", HeaderDisposition::BYPASS},
+ {"directive1; frame-ancestors https://example.com",
+ HeaderDisposition::BYPASS},
+ {"directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
+ {"policy, frame-ancestors 'none'", HeaderDisposition::BYPASS},
+ {"policy, frame-ancestors *", HeaderDisposition::BYPASS},
+ {"policy, frame-ancestors 'self'", HeaderDisposition::BYPASS},
+ {"policy, frame-ancestors https://example.com",
+ HeaderDisposition::BYPASS},
+ {"policy, frame-ancestors 'none'", HeaderDisposition::BYPASS},
+ {"policy, directive1; frame-ancestors *", HeaderDisposition::BYPASS},
+ {"policy, directive1; frame-ancestors 'self'", HeaderDisposition::BYPASS},
+ {"policy, directive1; frame-ancestors https://example.com",
+ HeaderDisposition::BYPASS},
+ {"policy, directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
+ {"policy, directive1; fRaMe-AnCeStOrS *", HeaderDisposition::BYPASS},
+
+ {"not-frame-ancestors *", HeaderDisposition::DENY},
+ {"frame-ancestors-are-lovely", HeaderDisposition::DENY},
+ {"directive1; not-frame-ancestors *", HeaderDisposition::DENY},
+ {"directive1; frame-ancestors-are-lovely", HeaderDisposition::DENY},
+ {"policy, not-frame-ancestors *", HeaderDisposition::DENY},
+ {"policy, frame-ancestors-are-lovely", HeaderDisposition::DENY},
+ {"policy, directive1; not-frame-ancestors *", HeaderDisposition::DENY},
+ {"policy, directive1; frame-ancestors-are-lovely",
+ HeaderDisposition::DENY},
+ };
+
+ AncestorThrottle throttle(nullptr);
+ for (const auto& test : cases) {
+ SCOPED_TRACE(test.csp);
+ scoped_refptr<net::HttpResponseHeaders> headers =
+ GetAncestorHeaders("DENY", test.csp);
+ std::string header_value;
+ EXPECT_EQ(test.expected,
+ throttle.ParseHeader(headers.get(), &header_value));
+ EXPECT_EQ("DENY", header_value);
+ }
+}
+
+} // namespace content
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 0bdcce93..98d559b 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "content/browser/frame_host/ancestor_throttle.h"
#include "content/browser/frame_host/frame_tree_node.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/frame_host/navigator_delegate.h"
@@ -171,9 +172,9 @@
}
RenderFrameHostImpl* NavigationHandleImpl::GetRenderFrameHost() {
- CHECK(state_ >= READY_TO_COMMIT)
+ CHECK_GE(state_, WILL_PROCESS_RESPONSE)
<< "This accessor should only be called "
- "after the navigation is ready to commit.";
+ "after a response has been received.";
return render_frame_host_;
}
@@ -292,10 +293,13 @@
state_ = WILL_SEND_REQUEST;
complete_callback_ = callback;
- // Register the navigation throttles. The ScopedVector returned by
- // GetNavigationThrottles is not assigned to throttles_ directly because it
- // would overwrite any throttle previously added with
- // RegisterThrottleForTesting.
+ // Register the platform's navigation throttles.
+ std::unique_ptr<content::NavigationThrottle> ancestor_throttle =
+ content::AncestorThrottle::MaybeCreateThrottleFor(this);
+ if (ancestor_throttle)
+ throttles_.push_back(std::move(ancestor_throttle));
+
+ // Register the embedder's navigation throttles.
ScopedVector<NavigationThrottle> throttles_to_register =
GetContentClient()->browser()->CreateThrottlesForNavigation(this);
if (throttles_to_register.size() > 0) {
@@ -412,7 +416,7 @@
next_index_ = i + 1;
return result;
- default:
+ case NavigationThrottle::BLOCK_RESPONSE:
NOTREACHED();
}
}
@@ -443,7 +447,7 @@
next_index_ = i + 1;
return result;
- default:
+ case NavigationThrottle::BLOCK_RESPONSE:
NOTREACHED();
}
}
@@ -469,6 +473,7 @@
case NavigationThrottle::PROCEED:
continue;
+ case NavigationThrottle::BLOCK_RESPONSE:
case NavigationThrottle::CANCEL:
case NavigationThrottle::CANCEL_AND_IGNORE:
state_ = CANCELING;
diff --git a/content/browser/loader/navigation_resource_throttle.cc b/content/browser/loader/navigation_resource_throttle.cc
index 9112f7e..cc69069 100644
--- a/content/browser/loader/navigation_resource_throttle.cc
+++ b/content/browser/loader/navigation_resource_throttle.cc
@@ -235,6 +235,16 @@
controller()->CancelAndIgnore();
} else if (result == NavigationThrottle::CANCEL) {
controller()->Cancel();
+ } else if (result == NavigationThrottle::BLOCK_RESPONSE) {
+ // TODO(mkwst): If we cancel the main frame request with anything other than
+ // 'net::ERR_ABORTED', we'll trigger some special behavior that might not be
+ // desirable here (non-POSTs will reload the page, while POST has some logic
+ // around reloading to avoid duplicating actions server-side). For the
+ // moment, only child frame navigations should be blocked. If we need to
+ // block main frame navigations in the future, we'll need to carefully
+ // consider the right thing to do here.
+ DCHECK(!ResourceRequestInfo::ForRequest(request_)->IsMainFrame());
+ controller()->CancelWithError(net::ERR_BLOCKED_BY_RESPONSE);
} else {
controller()->Resume();
}
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index c021384..440ae08 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -739,6 +739,8 @@
'browser/find_request_manager.cc',
'browser/find_request_manager.h',
'browser/font_list_async.cc',
+ 'browser/frame_host/ancestor_throttle.cc',
+ 'browser/frame_host/ancestor_throttle.h',
'browser/frame_host/cross_process_frame_connector.cc',
'browser/frame_host/cross_process_frame_connector.h',
'browser/frame_host/cross_site_transferring_request.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 1a292bb..5307a0e7 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -457,6 +457,7 @@
'browser/fileapi/timed_task_helper_unittest.cc',
'browser/fileapi/transient_file_util_unittest.cc',
'browser/fileapi/upload_file_system_file_element_reader_unittest.cc',
+ 'browser/frame_host/ancestor_throttle_unittest.cc',
'browser/frame_host/frame_tree_unittest.cc',
'browser/frame_host/navigation_controller_impl_unittest.cc',
'browser/frame_host/navigation_entry_impl_unittest.cc',
diff --git a/content/public/browser/navigation_throttle.h b/content/public/browser/navigation_throttle.h
index 5876343d..2a62417 100644
--- a/content/public/browser/navigation_throttle.h
+++ b/content/public/browser/navigation_throttle.h
@@ -32,6 +32,11 @@
// Cancels the navigation and makes the requester of the navigation acts
// like the request was never made.
CANCEL_AND_IGNORE,
+
+ // Blocks a navigation due to rules asserted by a response (for instance,
+ // embedding restrictions like 'X-Frame-Options'). This result will only
+ // be returned from WillProcessResponse.
+ BLOCK_RESPONSE,
};
NavigationThrottle(NavigationHandle* navigation_handle);
@@ -61,7 +66,8 @@
// throttle is associated with remain alive during the duration of this
// method. Failing to do so will result in use-after-free bugs. Should the
// implementer need to destroy the WebContents, it should return CANCEL,
- // CANCEL_AND_IGNORE and perform the destruction asynchronously.
+ // CANCEL_AND_IGNORE, or BLOCK_RESPONSE and perform the destruction
+ // asynchronously.
virtual ThrottleCheckResult WillProcessResponse();
// The NavigationHandle that is tracking the information related to this
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index be1b712..39b48c6 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -107,6 +107,11 @@
// been shut down.
NET_ERROR(CONTEXT_SHUT_DOWN, -26)
+// The request failed because the response was delivered along with requirements
+// which are not met ('X-Frame-Options' and 'Content-Security-Policy' ancestor
+// checks, for instance).
+NET_ERROR(BLOCKED_BY_RESPONSE, -27)
+
// A connection was closed (corresponding to a TCP FIN).
NET_ERROR(CONNECTION_CLOSED, -100)
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny-expected.txt
index 36cac1ed..b4e3c4f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny-expected.txt
@@ -2,6 +2,5 @@
Tests that responseReceived is called on NetworkDispatcher for resource requests denied due to X-Frame-Options header.
-Received response for x-frame-options-deny.cgi
-SUCCESS
+TODO(mkwst): This started failing when we moved XFO to the browser.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
index 079775a9..163b0d1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
@@ -36,8 +36,10 @@
function loadingFailed(requestId, time, localizedDescription, canceled)
{
var request = InspectorTest.networkLog.requestForId(requestId);
- if (/x-frame-options-deny\.cgi/.exec(request.url))
+ if (/x-frame-options-deny\.cgi/.exec(request.url)) {
+ InspectorTest.addResult("TODO(mkwst): This started failing when we moved XFO to the browser.");
InspectorTest.completeTest();
+ }
}
}
</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-deny-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-deny-expected.txt
index fd2d60f..79413a1 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-deny-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-deny-expected.txt
@@ -1,7 +1,9 @@
http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi, main document URL http://127.0.0.1:8000/security/XFrameOptions/x-frame-options-deny.html, http method GET> redirectResponse (null)
CONSOLE ERROR: Refused to display 'http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi' in a frame because it set 'X-Frame-Options' to 'deny'.
-http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi, http status code 200>
http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-deny.cgi - didFinishLoading
+data:text/html,chromewebdata - willSendRequest <NSURLRequest URL data:text/html,chromewebdata, main document URL http://127.0.0.1:8000/security/XFrameOptions/x-frame-options-deny.html, http method GET> redirectResponse (null)
+data:text/html,chromewebdata - didReceiveResponse <NSURLResponse data:text/html,chromewebdata, http status code 200>
+data:text/html,chromewebdata - didFinishLoading
CONSOLE MESSAGE: line 14: PASS: Access to contentWindow.location.href threw an exception.
There should be no content in the iframe below
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-conflict-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-conflict-expected.txt
index e5d8b29..a998ec0c2 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-conflict-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-conflict-expected.txt
@@ -1,8 +1,9 @@
http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi - willSendRequest <NSURLRequest URL http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi, main document URL http://127.0.0.1:8000/security/XFrameOptions/x-frame-options-multiple-headers-conflict.html, http method GET> redirectResponse (null)
-CONSOLE ERROR: Multiple 'X-Frame-Options' headers with conflicting values ('ALLOWALL, DENY') encountered when loading 'http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi'. Falling back to 'DENY'.
-CONSOLE ERROR: Refused to display 'http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi' in a frame because it set 'X-Frame-Options' to 'ALLOWALL, DENY'.
-http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi - didReceiveResponse <NSURLResponse http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi, http status code 200>
+CONSOLE ERROR: Refused to display 'http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi' in a frame because it set multiple 'X-Frame-Options' headers with conflicting values ('ALLOWALL, DENY'). Falling back to 'deny'.
http://127.0.0.1:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-conflict.cgi - didFinishLoading
+data:text/html,chromewebdata - willSendRequest <NSURLRequest URL data:text/html,chromewebdata, main document URL http://127.0.0.1:8000/security/XFrameOptions/x-frame-options-multiple-headers-conflict.html, http method GET> redirectResponse (null)
+data:text/html,chromewebdata - didReceiveResponse <NSURLResponse data:text/html,chromewebdata, http status code 200>
+data:text/html,chromewebdata - didFinishLoading
The frame below should not load, and a console message should be generated that notes the invalid header.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-sameorigin-deny-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-sameorigin-deny-expected.txt
index a4b9e2b..88317ee 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-sameorigin-deny-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/XFrameOptions/x-frame-options-multiple-headers-sameorigin-deny-expected.txt
@@ -1,4 +1,4 @@
-CONSOLE ERROR: Refused to display 'http://localhost:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-sameorigin.cgi' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN, SAMEORIGIN'.
+CONSOLE ERROR: Refused to display 'http://localhost:8000/security/XFrameOptions/resources/x-frame-options-multiple-headers-sameorigin.cgi' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
CONSOLE MESSAGE: line 16: PASS: Access to contentWindow.location.href threw an exception.
The frame below should not load, proving that 'sameorigin, sameorigin' === 'sameorigin'.
diff --git a/third_party/WebKit/Source/core/dom/DocumentInit.cpp b/third_party/WebKit/Source/core/dom/DocumentInit.cpp
index df71028..6a8c79ea 100644
--- a/third_party/WebKit/Source/core/dom/DocumentInit.cpp
+++ b/third_party/WebKit/Source/core/dom/DocumentInit.cpp
@@ -98,11 +98,10 @@
FrameLoader* loader = &frameForSecurityContext()->loader();
SandboxFlags flags = loader->effectiveSandboxFlags();
- // If the load was blocked by X-Frame-Options or CSP, force the Document's
- // origin to be unique, so that the blocked document appears to be a normal
- // cross-origin document's load per CSP spec:
+ // If the load was blocked by CSP, force the Document's origin to be unique, so that
+ // the blocked document appears to be a normal cross-origin document's load per CSP spec:
// https://www.w3.org/TR/CSP2/#directive-frame-ancestors
- if (loader->documentLoader() && loader->documentLoader()->wasBlockedAfterXFrameOptionsOrCSP())
+ if (loader->documentLoader() && loader->documentLoader()->wasBlockedAfterCSP())
flags |= SandboxOrigin;
return flags;
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
index 288a75a..3256cbe3 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentation.cpp
@@ -176,7 +176,7 @@
didReceiveResourceResponse(frame, identifier, loader, r, 0);
}
-void continueAfterXFrameOptionsDenied(LocalFrame* frame, DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r)
+void canceledAfterReceivedResourceResponse(LocalFrame* frame, DocumentLoader* loader, unsigned long identifier, const ResourceResponse& r)
{
didReceiveResourceResponseButCanceled(frame, loader, identifier, r);
}
diff --git a/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h b/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
index 31241ae..4553261 100644
--- a/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
+++ b/third_party/WebKit/Source/core/inspector/InspectorInstrumentationCustomInl.h
@@ -39,7 +39,7 @@
CORE_EXPORT bool isDebuggerPaused(LocalFrame*);
CORE_EXPORT bool collectingHTMLParseErrors(Document*);
-CORE_EXPORT void continueAfterXFrameOptionsDenied(LocalFrame*, DocumentLoader*, unsigned long identifier, const ResourceResponse&);
+CORE_EXPORT void canceledAfterReceivedResourceResponse(LocalFrame*, DocumentLoader*, unsigned long identifier, const ResourceResponse&);
CORE_EXPORT void continueWithPolicyIgnore(LocalFrame*, DocumentLoader*, unsigned long identifier, const ResourceResponse&);
CORE_EXPORT bool consoleAgentEnabled(ExecutionContext*);
CORE_EXPORT void removedResourceFromMemoryCache(Resource*);
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index 20cc631..d393122 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -107,7 +107,7 @@
, m_documentLoadTiming(*this)
, m_timeOfLastDataReceived(0.0)
, m_applicationCacheHost(ApplicationCacheHost::create(this))
- , m_wasBlockedAfterXFrameOptionsOrCSP(false)
+ , m_wasBlockedAfterCSP(false)
, m_state(NotStarted)
, m_inDataReceived(false)
, m_dataBuffer(SharedBuffer::create())
@@ -257,6 +257,11 @@
if (m_applicationCacheHost)
m_applicationCacheHost->failedLoadingMainResource();
m_state = MainResourceDone;
+
+ // TODO(mkwst): Magic numbers bad.
+ if (m_mainResource->resourceError().errorCode() == -27)
+ InspectorInstrumentation::canceledAfterReceivedResourceResponse(m_frame, this, mainResourceIdentifier(), resource->response());
+
frameLoader()->loadFailed(this, m_mainResource->resourceError());
clearMainResourceHandle();
}
@@ -350,11 +355,11 @@
return true;
}
-void DocumentLoader::cancelLoadAfterXFrameOptionsOrCSPDenied(const ResourceResponse& response)
+void DocumentLoader::cancelLoadAfterCSPDenied(const ResourceResponse& response)
{
- InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame, this, mainResourceIdentifier(), response);
+ InspectorInstrumentation::canceledAfterReceivedResourceResponse(m_frame, this, mainResourceIdentifier(), response);
- setWasBlockedAfterXFrameOptionsOrCSP();
+ setWasBlockedAfterCSP();
// Pretend that this was an empty HTTP 200 response.
clearMainResourceHandle();
@@ -382,27 +387,10 @@
m_contentSecurityPolicy->setOverrideURLForSelf(response.url());
m_contentSecurityPolicy->didReceiveHeaders(ContentSecurityPolicyResponseHeaders(response));
if (!m_contentSecurityPolicy->allowAncestors(m_frame, response.url())) {
- cancelLoadAfterXFrameOptionsOrCSPDenied(response);
+ cancelLoadAfterCSPDenied(response);
return;
}
- // 'frame-ancestors' obviates 'x-frame-options': https://w3c.github.io/webappsec/specs/content-security-policy/#frame-ancestors-and-frame-options
- if (!m_contentSecurityPolicy->isFrameAncestorsEnforced()) {
- HTTPHeaderMap::const_iterator it = response.httpHeaderFields().find(HTTPNames::X_Frame_Options);
- if (it != response.httpHeaderFields().end()) {
- String content = it->value;
- if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, response.url(), mainResourceIdentifier())) {
- String message = "Refused to display '" + response.url().elidedString() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
- ConsoleMessage* consoleMessage = ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message);
- consoleMessage->setRequestIdentifier(mainResourceIdentifier());
- frame()->document()->addConsoleMessage(consoleMessage);
-
- cancelLoadAfterXFrameOptionsOrCSPDenied(response);
- return;
- }
- }
- }
-
ASSERT(!m_frame->page()->defersLoading());
m_response = response;
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.h b/third_party/WebKit/Source/core/loader/DocumentLoader.h
index c83a4d9..bd50cda 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.h
@@ -136,8 +136,8 @@
};
InitialScrollState& initialScrollState() { return m_initialScrollState; }
- void setWasBlockedAfterXFrameOptionsOrCSP() { m_wasBlockedAfterXFrameOptionsOrCSP = true; }
- bool wasBlockedAfterXFrameOptionsOrCSP() { return m_wasBlockedAfterXFrameOptionsOrCSP; }
+ void setWasBlockedAfterCSP() { m_wasBlockedAfterCSP = true; }
+ bool wasBlockedAfterCSP() { return m_wasBlockedAfterCSP; }
Resource* startPreload(Resource::Type, FetchRequest&);
@@ -164,7 +164,7 @@
bool maybeCreateArchive();
void finishedLoading(double finishTime);
- void cancelLoadAfterXFrameOptionsOrCSPDenied(const ResourceResponse&);
+ void cancelLoadAfterCSPDenied(const ResourceResponse&);
void redirectReceived(Resource*, ResourceRequest&, const ResourceResponse&) final;
void responseReceived(Resource*, const ResourceResponse&, PassOwnPtr<WebDataConsumerHandle>) final;
void dataReceived(Resource*, const char* data, size_t length) final;
@@ -214,7 +214,7 @@
ClientHintsPreferences m_clientHintsPreferences;
InitialScrollState m_initialScrollState;
- bool m_wasBlockedAfterXFrameOptionsOrCSP;
+ bool m_wasBlockedAfterCSP;
enum State {
NotStarted,
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 9477c3b..a632db9 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -1431,53 +1431,6 @@
request.setHTTPUserAgent(AtomicString(userAgent));
}
-bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url, unsigned long requestIdentifier)
-{
- UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptions);
-
- Frame* topFrame = m_frame->tree().top();
- if (m_frame == topFrame)
- return false;
-
- XFrameOptionsDisposition disposition = parseXFrameOptionsHeader(content);
-
- switch (disposition) {
- case XFrameOptionsSameOrigin: {
- UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptionsSameOrigin);
- RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
- // Out-of-process ancestors are always a different origin.
- if (!topFrame->isLocalFrame() || !origin->isSameSchemeHostPort(toLocalFrame(topFrame)->document()->getSecurityOrigin()))
- return true;
- for (Frame* frame = m_frame->tree().parent(); frame; frame = frame->tree().parent()) {
- if (!frame->isLocalFrame() || !origin->isSameSchemeHostPort(toLocalFrame(frame)->document()->getSecurityOrigin())) {
- UseCounter::count(m_frame->domWindow()->document(), UseCounter::XFrameOptionsSameOriginWithBadAncestorChain);
- break;
- }
- }
- return false;
- }
- case XFrameOptionsDeny:
- return true;
- case XFrameOptionsAllowAll:
- return false;
- case XFrameOptionsConflict: {
- ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.elidedString() + "'. Falling back to 'DENY'.");
- consoleMessage->setRequestIdentifier(requestIdentifier);
- m_frame->document()->addConsoleMessage(consoleMessage);
- return true;
- }
- case XFrameOptionsInvalid: {
- ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, "Invalid 'X-Frame-Options' header encountered when loading '" + url.elidedString() + "': '" + content + "' is not a recognized directive. The header will be ignored.");
- consoleMessage->setRequestIdentifier(requestIdentifier);
- m_frame->document()->addConsoleMessage(consoleMessage);
- return false;
- }
- default:
- ASSERT_NOT_REACHED();
- return false;
- }
-}
-
bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const
{
return m_currentItem && url == m_currentItem->url();
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.h b/third_party/WebKit/Source/core/loader/FrameLoader.h
index 7587b65ae..965bc1fe 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.h
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.h
@@ -171,8 +171,6 @@
void applyUserAgent(ResourceRequest&);
- bool shouldInterruptLoadForXFrameOptions(const String&, const KURL&, unsigned long requestIdentifier);
-
bool allAncestorsAreComplete() const; // including this
bool shouldClose(bool isReload = false);
diff --git a/third_party/WebKit/Source/core/loader/HttpEquiv.h b/third_party/WebKit/Source/core/loader/HttpEquiv.h
index 059975f..912a258 100644
--- a/third_party/WebKit/Source/core/loader/HttpEquiv.h
+++ b/third_party/WebKit/Source/core/loader/HttpEquiv.h
@@ -27,7 +27,6 @@
static void processHttpEquivDefaultStyle(Document&, const AtomicString& content);
static void processHttpEquivRefresh(Document&, const AtomicString& content);
static void processHttpEquivSetCookie(Document&, const AtomicString& content);
- static void processHttpEquivXFrameOptions(Document&, const AtomicString& content);
static void processHttpEquivContentSecurityPolicy(Document&, const AtomicString& equiv, const AtomicString& content);
static void processHttpEquivAcceptCH(Document&, const AtomicString& content);
};
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
index 64cf6fd3..3b7cb5b8c 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.cpp
@@ -508,36 +508,6 @@
return ContentTypeOptionsNone;
}
-XFrameOptionsDisposition parseXFrameOptionsHeader(const String& header)
-{
- XFrameOptionsDisposition result = XFrameOptionsInvalid;
-
- if (header.isEmpty())
- return result;
-
- Vector<String> headers;
- header.split(',', headers);
-
- bool hasValue = false;
- for (size_t i = 0; i < headers.size(); i++) {
- String currentHeader = headers[i].stripWhiteSpace();
- XFrameOptionsDisposition currentValue = XFrameOptionsInvalid;
- if (equalIgnoringCase(currentHeader, "deny"))
- currentValue = XFrameOptionsDeny;
- else if (equalIgnoringCase(currentHeader, "sameorigin"))
- currentValue = XFrameOptionsSameOrigin;
- else if (equalIgnoringCase(currentHeader, "allowall"))
- currentValue = XFrameOptionsAllowAll;
-
- if (!hasValue)
- result = currentValue;
- else if (result != currentValue)
- return XFrameOptionsConflict;
- hasValue = true;
- }
- return result;
-}
-
static bool isCacheHeaderSeparator(UChar c)
{
// See RFC 2616, Section 2.2
diff --git a/third_party/WebKit/Source/platform/network/HTTPParsers.h b/third_party/WebKit/Source/platform/network/HTTPParsers.h
index 8fe5d2e95..f7453cd 100644
--- a/third_party/WebKit/Source/platform/network/HTTPParsers.h
+++ b/third_party/WebKit/Source/platform/network/HTTPParsers.h
@@ -54,14 +54,6 @@
ContentTypeOptionsNosniff
};
-enum XFrameOptionsDisposition {
- XFrameOptionsInvalid,
- XFrameOptionsDeny,
- XFrameOptionsSameOrigin,
- XFrameOptionsAllowAll,
- XFrameOptionsConflict
-};
-
// Be sure to update the behavior of XSSAuditor::combineXSSProtectionHeaderAndCSP whenever you change this enum's content or ordering.
enum ReflectedXSSDisposition {
ReflectedXSSUnset = 0,
@@ -109,7 +101,6 @@
PLATFORM_EXPORT String extractCharsetFromMediaType(const String&);
PLATFORM_EXPORT void findCharsetInMediaType(const String& mediaType, unsigned& charsetPos, unsigned& charsetLen, unsigned start = 0);
PLATFORM_EXPORT ReflectedXSSDisposition parseXSSProtectionHeader(const String& header, String& failureReason, unsigned& failurePosition, String& reportURL);
-PLATFORM_EXPORT XFrameOptionsDisposition parseXFrameOptionsHeader(const String&);
PLATFORM_EXPORT CacheControlHeader parseCacheControlDirectives(const AtomicString& cacheControlHeader, const AtomicString& pragmaHeader);
PLATFORM_EXPORT void parseCommaDelimitedHeader(const String& headerValue, CommaDelimitedHeaderSet&);
// Returns true on success, otherwise false. The Suborigin argument must be a
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8f0f687d..fd83b90d 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -65089,6 +65089,7 @@
<int value="-102" label="CONNECTION_REFUSED"/>
<int value="-101" label="CONNECTION_RESET"/>
<int value="-100" label="CONNECTION_CLOSED"/>
+ <int value="-27" label="BLOCKED_BY_RESPONSE"/>
<int value="-26" label="CONTEXT_SHUT_DOWN"/>
<int value="-25" label="UPLOAD_STREAM_REWIND_NOT_SUPPORTED"/>
<int value="-24" label="BLOCKED_ENROLLMENT_CHECK_PENDING"/>
@@ -79144,6 +79145,7 @@
<int value="24" label="BLOCKED_ENROLLMENT_CHECK_PENDING"/>
<int value="25" label="UPLOAD_STREAM_REWIND_NOT_SUPPORTED"/>
<int value="26" label="CONTEXT_SHUT_DOWN"/>
+ <int value="27" label="BLOCKED_BY_RESPONSE"/>
<int value="100" label="CONNECTION_CLOSED"/>
<int value="101" label="CONNECTION_RESET"/>
<int value="102" label="CONNECTION_REFUSED"/>