| Index: content/browser/frame_host/xfo_throttle.cc
|
| diff --git a/content/browser/frame_host/xfo_throttle.cc b/content/browser/frame_host/xfo_throttle.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..08885e2c63925a31ae97ad7182584c2834db3ff3
|
| --- /dev/null
|
| +++ b/content/browser/frame_host/xfo_throttle.cc
|
| @@ -0,0 +1,153 @@
|
| +// Copyright 2015 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/xfo_throttle.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
|
| +scoped_ptr<NavigationThrottle> XFOThrottle::MaybeCreateThrottleFor(
|
| + NavigationHandle* handle) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| +
|
| + if (handle->IsInMainFrame())
|
| + return nullptr;
|
| +
|
| + return scoped_ptr<NavigationThrottle>(new XFOThrottle(handle));
|
| +}
|
| +
|
| +XFOThrottle::XFOThrottle(NavigationHandle* handle)
|
| + : NavigationThrottle(handle) {}
|
| +
|
| +XFOThrottle::~XFOThrottle() {}
|
| +
|
| +NavigationThrottle::ThrottleCheckResult XFOThrottle::WillProcessResponse() {
|
| + DCHECK(!navigation_handle()->IsInMainFrame());
|
| +
|
| + NavigationHandleImpl* handle =
|
| + static_cast<NavigationHandleImpl*>(navigation_handle());
|
| +
|
| + std::string failed_parse;
|
| + HeaderDisposition disposition =
|
| + ParseHeader(handle->GetResponseHeaders(), &failed_parse);
|
| + switch (disposition) {
|
| + case CONFLICT:
|
| + case INVALID:
|
| + ParseError(failed_parse, disposition);
|
| + return NavigationThrottle::BLOCK;
|
| +
|
| + case DENY:
|
| + ConsoleError(disposition);
|
| + return NavigationThrottle::BLOCK;
|
| +
|
| + case 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;
|
| + }
|
| +
|
| + case NOT_PRESENT:
|
| + case ALLOWALL:
|
| + return NavigationThrottle::PROCEED;
|
| + }
|
| + NOTREACHED();
|
| + return NavigationThrottle::PROCEED;
|
| +}
|
| +
|
| +void XFOThrottle::ParseError(const std::string& value,
|
| + HeaderDisposition disposition) {
|
| + DCHECK(disposition == CONFLICT || disposition == INVALID);
|
| +
|
| + std::string message;
|
| + if (disposition == CONFLICT) {
|
| + message = base::StringPrintf(
|
| + "Multiple 'X-Frame-Options' headers with conflicting values "
|
| + "('%s') encountered when loading '%s'. Falling back to 'DENY'.",
|
| + value.c_str(), navigation_handle()->GetURL().spec().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());
|
| + }
|
| +
|
| + NavigationHandleImpl* handle =
|
| + static_cast<NavigationHandleImpl*>(navigation_handle());
|
| + // Log a console error in the parent of the current RenderFrameHost (as
|
| + // the current RenderFrameHost itself doesn't yet have a document).
|
| + handle->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
|
| + CONSOLE_MESSAGE_LEVEL_ERROR, message);
|
| +}
|
| +
|
| +void XFOThrottle::ConsoleError(HeaderDisposition disposition) {
|
| + DCHECK(disposition == DENY || disposition == 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 == DENY ? "DENY" : "SAMEORIGIN");
|
| +
|
| + NavigationHandleImpl* handle =
|
| + static_cast<NavigationHandleImpl*>(navigation_handle());
|
| + // Log a console error in the parent of the current RenderFrameHost (as
|
| + // the current RenderFrameHost itself doesn't yet have a document).
|
| + handle->GetRenderFrameHost()->GetParent()->AddMessageToConsole(
|
| + CONSOLE_MESSAGE_LEVEL_ERROR, message);
|
| +}
|
| +
|
| +// static
|
| +XFOThrottle::HeaderDisposition XFOThrottle::ParseHeader(
|
| + const net::HttpResponseHeaders* headers,
|
| + std::string* failed_parse) {
|
| + if (!headers)
|
| + return NOT_PRESENT;
|
| +
|
| + void* iter = nullptr;
|
| + std::string value;
|
| + HeaderDisposition result = NOT_PRESENT;
|
| + while (headers->EnumerateHeader(&iter, "x-frame-options", &value)) {
|
| + HeaderDisposition current = INVALID;
|
| + base::StringPiece trimmed =
|
| + base::TrimWhitespaceASCII(value, base::TRIM_ALL);
|
| +
|
| + if (base::LowerCaseEqualsASCII(trimmed, "deny")) {
|
| + current = DENY;
|
| + } else if (base::LowerCaseEqualsASCII(trimmed, "allowall")) {
|
| + current = ALLOWALL;
|
| + } else if (base::LowerCaseEqualsASCII(trimmed, "sameorigin")) {
|
| + current = SAMEORIGIN;
|
| + } else {
|
| + if (failed_parse)
|
| + *failed_parse = trimmed.as_string();
|
| + return INVALID;
|
| + }
|
| +
|
| + if (result == NOT_PRESENT) {
|
| + result = current;
|
| + } else if (result != current) {
|
| + if (failed_parse)
|
| + *failed_parse = trimmed.as_string();
|
| + return CONFLICT;
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +} // namespace content
|
|
|