summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-12-19 12:53:25 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-12-19 12:53:25 +0100
commita1d7154bb73b41145be17283002eb10ee215574c (patch)
treedb5772d1b814594f5ed24051dd2237ddeef8fff1
parent0793ceb388fc29ede5cfe2bf5790e74344e4ce83 (diff)
parentabefd5095b41dac94ca451d784ab6e27372e981a (diff)
downloadqtwebengine-chromium-a1d7154bb73b41145be17283002eb10ee215574c.tar.gz
Merge branch 'upstream-master' into 63-based
Change-Id: I9909647008cf4786e87f83733a4d82ce9286ea85
-rw-r--r--chromium/DEPS4
-rw-r--r--chromium/build/util/LASTCHANGE2
-rw-r--r--chromium/build/util/LASTCHANGE.blink2
-rw-r--r--chromium/chrome/VERSION2
-rw-r--r--chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc135
-rw-r--r--chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h84
-rw-r--r--chromium/chrome/common/extensions/api/_permission_features.json1
-rw-r--r--chromium/components/google/core/browser/google_util.cc8
-rw-r--r--chromium/components/google/core/browser/google_util_unittest.cc8
-rw-r--r--chromium/content/browser/BUILD.gn2
-rw-r--r--chromium/content/browser/browser_main_loop.cc3
-rw-r--r--chromium/content/browser/frame_host/navigation_request.cc17
-rw-r--r--chromium/content/browser/frame_host/navigator_impl.cc7
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.cc30
-rw-r--r--chromium/content/browser/frame_host/render_frame_host_manager.h3
-rw-r--r--chromium/content/browser/loader/DEPS1
-rw-r--r--chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc380
-rw-r--r--chromium/content/browser/loader/cross_site_document_resource_handler.cc420
-rw-r--r--chromium/content/browser/loader/cross_site_document_resource_handler.h156
-rw-r--r--chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc836
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_impl.cc18
-rw-r--r--chromium/content/browser/loader/resource_dispatcher_host_impl.h1
-rw-r--r--chromium/content/browser/loader/url_loader_factory_impl_unittest.cc32
-rw-r--r--chromium/content/browser/web_contents/web_contents_view_android.cc2
-rw-r--r--chromium/content/child/site_isolation_stats_gatherer_browsertest.cc47
-rw-r--r--chromium/content/common/cross_site_document_classifier.cc7
-rw-r--r--chromium/content/common/site_isolation_policy.cc41
-rw-r--r--chromium/content/common/site_isolation_policy.h13
-rw-r--r--chromium/content/public/browser/content_browser_client.cc7
-rw-r--r--chromium/content/public/browser/content_browser_client.h7
-rw-r--r--chromium/content/public/common/content_features.cc10
-rw-r--r--chromium/content/public/common/content_features.h2
-rw-r--r--chromium/content/renderer/fetchers/resource_fetcher_browsertest.cc42
-rw-r--r--chromium/content/test/BUILD.gn2
-rw-r--r--chromium/extensions/common/api/_permission_features.json3
-rw-r--r--chromium/net/dns/dns_transaction.cc11
-rw-r--r--chromium/net/dns/dns_transaction_unittest.cc48
-rw-r--r--chromium/skia/ext/skia_commit_hash.h2
-rw-r--r--chromium/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp6
-rw-r--r--chromium/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp5
-rw-r--r--chromium/third_party/WebKit/Source/modules/accessibility/AXARIAGrid.cpp7
-rw-r--r--chromium/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp15
-rw-r--r--chromium/third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp3
-rw-r--r--chromium/ui/compositor/compositor.cc6
-rw-r--r--chromium/v8/include/v8-version.h2
-rw-r--r--chromium/v8/src/builtins/builtins-typedarray-gen.cc10
-rw-r--r--chromium/v8/src/debug/debug-coverage.cc3
47 files changed, 2341 insertions, 112 deletions
diff --git a/chromium/DEPS b/chromium/DEPS
index 0ae0dad7656..6add5aa83f8 100644
--- a/chromium/DEPS
+++ b/chromium/DEPS
@@ -171,7 +171,7 @@ deps = {
(Var("chromium_git")) + '/android_tools.git@ca9dc7245b888c75307f0619e4a39fb46a82de66'
},
'src/third_party/angle':
- (Var("chromium_git")) + '/angle/angle.git@9095f2b44801efef46a440c391d89278432169d5',
+ (Var("chromium_git")) + '/angle/angle.git@2ff870db3a3ba33c6dbf4b88c9ec2e52127b7206',
'src/third_party/apache-portable-runtime/src': {
'condition':
'checkout_android',
@@ -529,7 +529,7 @@ deps = {
'src/tools/swarming_client':
(Var("chromium_git")) + '/infra/luci/client-py.git@5e8001d9a710121ce7a68efd0804430a34b4f9e4',
'src/v8':
- (Var("chromium_git")) + '/v8/v8.git@310263b31fd87280b80e4bdd4ecfba5128b4cda7'
+ (Var("chromium_git")) + '/v8/v8.git@0ffdb062d3ed8067ede1f8771e24dbf822be1a39'
}
hooks = [
diff --git a/chromium/build/util/LASTCHANGE b/chromium/build/util/LASTCHANGE
index fe21d9bad56..b6f9abde98c 100644
--- a/chromium/build/util/LASTCHANGE
+++ b/chromium/build/util/LASTCHANGE
@@ -1 +1 @@
-LASTCHANGE=9f7c6464592fa694a05b7db5d521510df19243db-
+LASTCHANGE=9d8e15517677de090afcca02259a80ffa552235e-
diff --git a/chromium/build/util/LASTCHANGE.blink b/chromium/build/util/LASTCHANGE.blink
index fe21d9bad56..b6f9abde98c 100644
--- a/chromium/build/util/LASTCHANGE.blink
+++ b/chromium/build/util/LASTCHANGE.blink
@@ -1 +1 @@
-LASTCHANGE=9f7c6464592fa694a05b7db5d521510df19243db-
+LASTCHANGE=9d8e15517677de090afcca02259a80ffa552235e-
diff --git a/chromium/chrome/VERSION b/chromium/chrome/VERSION
index 2538ca30ef8..ae5f39b57d7 100644
--- a/chromium/chrome/VERSION
+++ b/chromium/chrome/VERSION
@@ -1,4 +1,4 @@
MAJOR=63
MINOR=0
BUILD=3239
-PATCH=87
+PATCH=117
diff --git a/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
new file mode 100644
index 00000000000..4ff6fcecef9
--- /dev/null
+++ b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.cc
@@ -0,0 +1,135 @@
+// 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 "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/singleton.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/common/service_manager_connection.h"
+
+namespace {
+
+constexpr base::TimeDelta kUtilityProcessIdleTimeout =
+ base::TimeDelta::FromSeconds(5);
+
+void BindConnectorOnUIThread(service_manager::mojom::ConnectorRequest request) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->BindConnectorRequest(std::move(request));
+}
+
+} // namespace
+
+// static
+ChromeMojoProxyResolverFactory* ChromeMojoProxyResolverFactory::GetInstance() {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+ return base::Singleton<
+ ChromeMojoProxyResolverFactory,
+ base::LeakySingletonTraits<ChromeMojoProxyResolverFactory>>::get();
+}
+
+ChromeMojoProxyResolverFactory::ChromeMojoProxyResolverFactory()
+ : factory_idle_timeout_(kUtilityProcessIdleTimeout) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+}
+
+ChromeMojoProxyResolverFactory::~ChromeMojoProxyResolverFactory() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+}
+
+std::unique_ptr<base::ScopedClosureRunner>
+ChromeMojoProxyResolverFactory::CreateResolver(
+ const std::string& pac_script,
+ mojo::InterfaceRequest<proxy_resolver::mojom::ProxyResolver> req,
+ proxy_resolver::mojom::ProxyResolverFactoryRequestClientPtr client) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!resolver_factory_)
+ CreateFactory();
+
+ if (!resolver_factory_) {
+ // If factory creation failed, close |req|'s message pipe, which should
+ // cause a connection error.
+ req = nullptr;
+ return nullptr;
+ }
+ idle_timer_.Stop();
+ num_proxy_resolvers_++;
+ resolver_factory_->CreateResolver(pac_script, std::move(req),
+ std::move(client));
+ return base::MakeUnique<base::ScopedClosureRunner>(
+ base::Bind(&ChromeMojoProxyResolverFactory::OnResolverDestroyed,
+ base::Unretained(this)));
+}
+
+void ChromeMojoProxyResolverFactory::SetFactoryIdleTimeoutForTests(
+ const base::TimeDelta& timeout) {
+ factory_idle_timeout_ = timeout;
+}
+
+void ChromeMojoProxyResolverFactory::InitServiceManagerConnector() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (service_manager_connector_)
+ return;
+
+ // The existing ServiceManagerConnection retrieved with
+ // ServiceManagerConnection::GetForProcess() lives on the UI thread, so we
+ // can't access it from here. We create our own connector so it can be used
+ // right away and will bind it on the UI thread.
+ service_manager::mojom::ConnectorRequest request;
+ service_manager_connector_ = service_manager::Connector::Create(&request);
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(&BindConnectorOnUIThread, base::Passed(&request)));
+}
+
+void ChromeMojoProxyResolverFactory::CreateFactory() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(!resolver_factory_);
+
+ InitServiceManagerConnector();
+
+ service_manager_connector_->BindInterface(
+ proxy_resolver::mojom::kProxyResolverServiceName,
+ mojo::MakeRequest(&resolver_factory_));
+
+ resolver_factory_.set_connection_error_handler(base::Bind(
+ &ChromeMojoProxyResolverFactory::DestroyFactory, base::Unretained(this)));
+}
+
+void ChromeMojoProxyResolverFactory::DestroyFactory() {
+ resolver_factory_.reset();
+}
+
+void ChromeMojoProxyResolverFactory::OnResolverDestroyed() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_GT(num_proxy_resolvers_, 0u);
+ if (--num_proxy_resolvers_ == 0) {
+ // When all proxy resolvers have been destroyed, the proxy resolver factory
+ // is no longer needed. However, new proxy resolvers may be created
+ // shortly after being destroyed (e.g. due to a network change).
+ //
+ // On desktop, where a utility process is used, if the utility process is
+ // shut down immediately, this would cause unnecessary process churn, so
+ // wait for an idle timeout before shutting down the proxy resolver utility
+ // process.
+ idle_timer_.Start(FROM_HERE, factory_idle_timeout_, this,
+ &ChromeMojoProxyResolverFactory::OnIdleTimeout);
+ }
+}
+
+void ChromeMojoProxyResolverFactory::OnIdleTimeout() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK_EQ(num_proxy_resolvers_, 0u);
+ DestroyFactory();
+}
diff --git a/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
new file mode 100644
index 00000000000..dd6d7702a98
--- /dev/null
+++ b/chromium/chrome/browser/net/chrome_mojo_proxy_resolver_factory.h
@@ -0,0 +1,84 @@
+// 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.
+
+#ifndef CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
+#define CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "content/public/network/mojo_proxy_resolver_factory.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/interfaces/connector.mojom.h"
+
+namespace base {
+template <typename Type>
+struct DefaultSingletonTraits;
+} // namespace base
+
+// A factory used to create connections to Mojo proxy resolver services. On
+// Android, the proxy resolvers will run in the browser process, and on other
+// platforms, they'll all be run in the same utility process.
+class ChromeMojoProxyResolverFactory
+ : public content::MojoProxyResolverFactory {
+ public:
+ static ChromeMojoProxyResolverFactory* GetInstance();
+
+ // Overridden from proxy_content::MojoProxyResolverFactory:
+ std::unique_ptr<base::ScopedClosureRunner> CreateResolver(
+ const std::string& pac_script,
+ mojo::InterfaceRequest<proxy_resolver::mojom::ProxyResolver> req,
+ proxy_resolver::mojom::ProxyResolverFactoryRequestClientPtr client)
+ override;
+
+ // Used by tests to override the default timeout used when no resolver is
+ // connected before disconnecting the factory (and causing the service to
+ // stop).
+ void SetFactoryIdleTimeoutForTests(const base::TimeDelta& timeout);
+
+ private:
+ friend struct base::DefaultSingletonTraits<ChromeMojoProxyResolverFactory>;
+ ChromeMojoProxyResolverFactory();
+ ~ChromeMojoProxyResolverFactory() override;
+
+ // Initializes the ServiceManager's connector if it hasn't been already.
+ void InitServiceManagerConnector();
+
+ // Creates the proxy resolver factory. On desktop, creates a new utility
+ // process before creating it out of process. On Android, creates it on the
+ // current thread.
+ void CreateFactory();
+
+ // Destroys |resolver_factory_|.
+ void DestroyFactory();
+
+ // Invoked each time a proxy resolver is destroyed.
+ void OnResolverDestroyed();
+
+ // Invoked once an idle timeout has elapsed after all proxy resolvers are
+ // destroyed.
+ void OnIdleTimeout();
+
+ proxy_resolver::mojom::ProxyResolverFactoryPtr resolver_factory_;
+
+ std::unique_ptr<service_manager::Connector> service_manager_connector_;
+
+ size_t num_proxy_resolvers_ = 0;
+
+ base::OneShotTimer idle_timer_;
+
+ base::ThreadChecker thread_checker_;
+
+ base::TimeDelta factory_idle_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(ChromeMojoProxyResolverFactory);
+};
+
+#endif // CHROME_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
diff --git a/chromium/chrome/common/extensions/api/_permission_features.json b/chromium/chrome/common/extensions/api/_permission_features.json
index 5dfafe651f4..2c43b4faf42 100644
--- a/chromium/chrome/common/extensions/api/_permission_features.json
+++ b/chromium/chrome/common/extensions/api/_permission_features.json
@@ -139,6 +139,7 @@
"B6C2EFAB3EC3BF6EF03701408B6B09A67B2D0069", // http://crbug.com/642141
"96FF2FFA5C9173C76D47184B3E86D267B37781DE", // http://crbug.com/642141
"0136FCB13DB29FD5CD442F56E59E53B61F1DF96F", // http://crbug.com/642141
+ "9834387FDA1F66A1B5CA06CB442137B556F12F2A", // http://crbug.com/772346
"930F7D9989A5FBCDCCD7D85BB5C3B7006C24D91D" // http://crbug.com/782139
]
},
diff --git a/chromium/components/google/core/browser/google_util.cc b/chromium/components/google/core/browser/google_util.cc
index 845e22a633c..2820e033dbf 100644
--- a/chromium/components/google/core/browser/google_util.cc
+++ b/chromium/components/google/core/browser/google_util.cc
@@ -142,11 +142,9 @@ bool IsGoogleSearchSubdomainUrl(const GURL& url) {
bool HasGoogleSearchQueryParam(base::StringPiece str) {
url::Component query(0, static_cast<int>(str.length())), key, value;
while (url::ExtractQueryKeyValue(str.data(), &query, &key, &value)) {
- if (value.is_nonempty()) {
- base::StringPiece key_str = str.substr(key.begin, key.len);
- if (key_str == "q" || key_str == "as_q")
- return true;
- }
+ base::StringPiece key_str = str.substr(key.begin, key.len);
+ if (key_str == "q" || key_str == "as_q")
+ return true;
}
return false;
}
diff --git a/chromium/components/google/core/browser/google_util_unittest.cc b/chromium/components/google/core/browser/google_util_unittest.cc
index 46072bf5703..477d3ae9ded 100644
--- a/chromium/components/google/core/browser/google_util_unittest.cc
+++ b/chromium/components/google/core/browser/google_util_unittest.cc
@@ -147,6 +147,8 @@ TEST(GoogleUtilTest, GoodSearches) {
"%s://www.google.co.uk/search?%s=something",
// It's actually valid for both to have the query parameter.
"%s://www.google.com/search?%s=something#q=other",
+ // Also valid to have an empty query parameter
+ "%s://www.google.com/search?%s=",
// Queries with path "/webhp", "/" or "" need to have the query parameter
// in the hash fragment.
@@ -216,12 +218,6 @@ TEST(GoogleUtilTest, BadSearches) {
}
const std::string patterns_q[] = {
- // Can't have an empty query parameter.
- "%s://www.google.com/search?%s=",
- "%s://www.google.com/search?name=bob&%s=",
- "%s://www.google.com/webhp#%s=",
- "%s://www.google.com/webhp#name=bob&%s=",
-
// Home page searches without a hash fragment query parameter are invalid.
"%s://www.google.com/webhp?%s=something",
"%s://www.google.com/webhp?%s=something#no=good",
diff --git a/chromium/content/browser/BUILD.gn b/chromium/content/browser/BUILD.gn
index ce5d712da7f..8b5b0277c6f 100644
--- a/chromium/content/browser/BUILD.gn
+++ b/chromium/content/browser/BUILD.gn
@@ -926,6 +926,8 @@ source_set("browser") {
"leveldb_wrapper_impl.h",
"loader/async_resource_handler.cc",
"loader/async_resource_handler.h",
+ "loader/cross_site_document_resource_handler.cc",
+ "loader/cross_site_document_resource_handler.h",
"loader/detachable_resource_handler.cc",
"loader/detachable_resource_handler.h",
"loader/downloaded_temp_file_impl.cc",
diff --git a/chromium/content/browser/browser_main_loop.cc b/chromium/content/browser/browser_main_loop.cc
index b0c9546fdc8..af8fed43857 100644
--- a/chromium/content/browser/browser_main_loop.cc
+++ b/chromium/content/browser/browser_main_loop.cc
@@ -877,6 +877,9 @@ int BrowserMainLoop::PreCreateThreads() {
EVP_set_buggy_rsa_parser(
base::FeatureList::IsEnabled(features::kBuggyRSAParser));
+ // Record metrics about which site isolation flags have been turned on.
+ SiteIsolationPolicy::RecordSiteIsolationFlagUsage();
+
return result_code_;
}
diff --git a/chromium/content/browser/frame_host/navigation_request.cc b/chromium/content/browser/frame_host/navigation_request.cc
index 66cbc43ab67..a9d1909d17f 100644
--- a/chromium/content/browser/frame_host/navigation_request.cc
+++ b/chromium/content/browser/frame_host/navigation_request.cc
@@ -1006,14 +1006,25 @@ void NavigationRequest::OnStartChecksComplete(
// Mark the fetch_start (Navigation Timing API).
request_params_.navigation_timing.fetch_start = base::TimeTicks::Now();
+ GURL base_url;
+#if defined(OS_ANDROID)
+ // On Android, a base URL can be set for the frame. If this the case, it is
+ // the URL to use for cookies.
+ NavigationEntry* last_committed_entry =
+ frame_tree_node_->navigator()->GetController()->GetLastCommittedEntry();
+ if (last_committed_entry)
+ base_url = last_committed_entry->GetBaseURLForDataURL();
+#endif
+ const GURL& top_document_url =
+ !base_url.is_empty()
+ ? base_url
+ : frame_tree_node_->frame_tree()->root()->current_url();
// TODO(mkwst): This is incorrect. It ought to use the definition from
// 'Document::firstPartyForCookies()' in Blink, which walks the ancestor tree
// and verifies that all origins are PSL-matches (and special-cases extension
// URLs).
const GURL& site_for_cookies =
- frame_tree_node_->IsMainFrame()
- ? common_params_.url
- : frame_tree_node_->frame_tree()->root()->current_url();
+ frame_tree_node_->IsMainFrame() ? common_params_.url : top_document_url;
bool parent_is_main_frame = !frame_tree_node_->parent()
? false
: frame_tree_node_->parent()->IsMainFrame();
diff --git a/chromium/content/browser/frame_host/navigator_impl.cc b/chromium/content/browser/frame_host/navigator_impl.cc
index 6c4c6b7b0e8..ef506cebcde 100644
--- a/chromium/content/browser/frame_host/navigator_impl.cc
+++ b/chromium/content/browser/frame_host/navigator_impl.cc
@@ -1177,9 +1177,12 @@ void NavigatorImpl::RequestNavigation(
// a Javascript URL should not interrupt a previous navigation.
// Note: The scoped_request will be destroyed at the end of this function.
if (dest_url.SchemeIs(url::kJavaScriptScheme)) {
+ // Don't call frame_tree_node->render_manager()->GetFrameHostForNavigation
+ // as that might clear the speculative RFH of an ongoing navigation.
RenderFrameHostImpl* render_frame_host =
- frame_tree_node->render_manager()->GetFrameHostForNavigation(
- *scoped_request.get());
+ frame_tree_node->current_frame_host();
+ frame_tree_node->render_manager()->InitializeRenderFrameIfNecessary(
+ render_frame_host);
render_frame_host->CommitNavigation(
nullptr, // response
nullptr, // body
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.cc b/chromium/content/browser/frame_host/render_frame_host_manager.cc
index 5541f127d36..f7b53cd5b94 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.cc
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.cc
@@ -1232,6 +1232,36 @@ RenderFrameHostManager::GetSiteInstanceForNavigation(
return new_instance;
}
+void RenderFrameHostManager::InitializeRenderFrameIfNecessary(
+ RenderFrameHostImpl* render_frame_host) {
+ // TODO: this copies some logic inside GetFrameHostForNavigation, which also
+ // duplicates logic in Navigate. They should all use this method, but that
+ // involves slight reordering.
+ if (render_frame_host->IsRenderFrameLive())
+ return;
+
+ if (!ReinitializeRenderFrame(render_frame_host))
+ return;
+
+ if (render_frame_host != render_frame_host_.get())
+ return;
+
+ EnsureRenderFrameHostVisibilityConsistent();
+
+ // TODO: uncomment this when the method is shared. Not adding the call now
+ // to make merge to 63 easier.
+ // EnsureRenderFrameHostPageFocusConsistent();
+
+ // TODO(nasko): This is a very ugly hack. The Chrome extensions process
+ // manager still uses NotificationService and expects to see a
+ // RenderViewHost changed notification after WebContents and
+ // RenderFrameHostManager are completely initialized. This should be
+ // removed once the process manager moves away from NotificationService.
+ // See https://crbug.com/462682.
+ delegate_->NotifyMainFrameSwappedFromRenderManager(
+ nullptr, render_frame_host_->render_view_host());
+}
+
RenderFrameHostManager::SiteInstanceDescriptor
RenderFrameHostManager::DetermineSiteInstanceForURL(
const GURL& dest_url,
diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.h b/chromium/content/browser/frame_host/render_frame_host_manager.h
index 67a3e97149a..9f33a6eb40c 100644
--- a/chromium/content/browser/frame_host/render_frame_host_manager.h
+++ b/chromium/content/browser/frame_host/render_frame_host_manager.h
@@ -515,6 +515,9 @@ class CONTENT_EXPORT RenderFrameHostManager
scoped_refptr<SiteInstance> GetSiteInstanceForNavigationRequest(
const NavigationRequest& navigation_request);
+ // Helper to initialize the RenderFrame if it's not initialized.
+ void InitializeRenderFrameIfNecessary(RenderFrameHostImpl* render_frame_host);
+
private:
friend class NavigatorTestWithBrowserSideNavigation;
friend class RenderFrameHostManagerTest;
diff --git a/chromium/content/browser/loader/DEPS b/chromium/content/browser/loader/DEPS
index 03317bf0cc9..194a58803b2 100644
--- a/chromium/content/browser/loader/DEPS
+++ b/chromium/content/browser/loader/DEPS
@@ -111,6 +111,7 @@ specific_include_rules = {
"resource_dispatcher_host_impl\.(cc|h)": [
"-content",
"+content/browser/loader/async_resource_handler.h",
+ "+content/browser/loader/cross_site_document_resource_handler.h",
"+content/browser/loader/global_routing_id.h",
"+content/browser/loader/loader_delegate.h",
"+content/browser/loader/mojo_async_resource_handler.h",
diff --git a/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc
new file mode 100644
index 00000000000..101bc198bee
--- /dev/null
+++ b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc
@@ -0,0 +1,380 @@
+// Copyright 2017 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/command_line.h"
+#include "base/macros.h"
+#include "base/strings/pattern.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "content/public/common/content_features.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace content {
+
+// These tests verify that the browser process blocks cross-site HTML, XML,
+// JSON, and some plain text responses when they are not otherwise permitted
+// (e.g., by CORS). This ensures that such responses never end up in the
+// renderer process where they might be accessible via a bug. Careful attention
+// is paid to allow other cross-site resources necessary for rendering,
+// including cases that may be mislabeled as blocked MIME type.
+//
+// Many of these tests work by turning off the Same Origin Policy in the
+// renderer process via --disable-web-security, and then trying to access the
+// resource via a cross-origin XHR. If the response is blocked, the XHR should
+// see an empty response body.
+//
+// Note that this BaseTest class does not specify an isolation mode via
+// command-line flags. Most of the tests are in the --site-per-process subclass
+// below.
+class CrossSiteDocumentBlockingBaseTest : public ContentBrowserTest {
+ public:
+ CrossSiteDocumentBlockingBaseTest() {}
+ ~CrossSiteDocumentBlockingBaseTest() override {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ // EmbeddedTestServer::InitializeAndListen() initializes its |base_url_|
+ // which is required below. This cannot invoke Start() however as that kicks
+ // off the "EmbeddedTestServer IO Thread" which then races with
+ // initialization in ContentBrowserTest::SetUp(), http://crbug.com/674545.
+ ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
+
+ // Add a host resolver rule to map all outgoing requests to the test server.
+ // This allows us to use "real" hostnames and standard ports in URLs (i.e.,
+ // without having to inject the port number into all URLs), which we can use
+ // to create arbitrary SiteInstances.
+ command_line->AppendSwitchASCII(
+ switches::kHostResolverRules,
+ "MAP * " + embedded_test_server()->host_port_pair().ToString() +
+ ",EXCLUDE localhost");
+
+ // To test that the renderer process does not receive blocked documents, we
+ // disable the same origin policy to let it see cross-origin fetches if they
+ // are received.
+ command_line->AppendSwitch(switches::kDisableWebSecurity);
+ }
+
+ void SetUpOnMainThread() override {
+ // Complete the manual Start() after ContentBrowserTest's own
+ // initialization, ref. comment on InitializeAndListen() above.
+ embedded_test_server()->StartAcceptingConnections();
+ }
+
+ // Ensure the correct histograms are incremented for blocking events.
+ // Assumes the resource type is XHR.
+ void InspectHistograms(const base::HistogramTester& histograms,
+ bool should_be_blocked,
+ bool should_be_sniffed,
+ const std::string& resource_name,
+ ResourceType resource_type) {
+ std::string bucket;
+ if (base::MatchPattern(resource_name, "*.html")) {
+ bucket = "HTML";
+ } else if (base::MatchPattern(resource_name, "*.xml")) {
+ bucket = "XML";
+ } else if (base::MatchPattern(resource_name, "*.json")) {
+ bucket = "JSON";
+ } else if (base::MatchPattern(resource_name, "*.txt")) {
+ bucket = "Plain";
+ } else {
+ EXPECT_FALSE(should_be_blocked);
+ bucket = "Other";
+ }
+
+ // Determine the appropriate histograms, including a start and end action
+ // (which are verified in unit tests), a read size if it was sniffed, and
+ // additional blocked metrics if it was blocked.
+ base::HistogramTester::CountsMap expected_counts;
+ std::string base = "SiteIsolation.XSD.Browser";
+ expected_counts[base + ".Action"] = 2;
+ if (should_be_sniffed)
+ expected_counts[base + ".BytesReadForSniffing"] = 1;
+ if (should_be_blocked) {
+ expected_counts[base + ".Blocked"] = 1;
+ expected_counts[base + ".Blocked." + bucket] = 1;
+ }
+
+ // Make sure that the expected metrics, and only those metrics, were
+ // incremented.
+ EXPECT_THAT(histograms.GetTotalCountsForPrefix("SiteIsolation.XSD.Browser"),
+ testing::ContainerEq(expected_counts))
+ << "For resource_name=" << resource_name
+ << ", should_be_blocked=" << should_be_blocked;
+
+ // Determine if the bucket for the resource type (XHR) was incremented.
+ if (should_be_blocked) {
+ EXPECT_THAT(histograms.GetAllSamples(base + ".Blocked"),
+ testing::ElementsAre(base::Bucket(resource_type, 1)))
+ << "The wrong Blocked bucket was incremented.";
+ EXPECT_THAT(histograms.GetAllSamples(base + ".Blocked." + bucket),
+ testing::ElementsAre(base::Bucket(resource_type, 1)))
+ << "The wrong Blocked bucket was incremented.";
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingBaseTest);
+};
+
+// Most tests here use --site-per-process, which enables document blocking
+// everywhere.
+class CrossSiteDocumentBlockingTest : public CrossSiteDocumentBlockingBaseTest {
+ public:
+ CrossSiteDocumentBlockingTest() {}
+ ~CrossSiteDocumentBlockingTest() override {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ IsolateAllSitesForTesting(command_line);
+ CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingTest);
+};
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockDocuments) {
+ // Load a page that issues illegal cross-site document requests to bar.com.
+ // The page uses XHR to request HTML/XML/JSON documents from bar.com, and
+ // inspects if any of them were successfully received. This test is only
+ // possible since we run the browser without the same origin policy, allowing
+ // it to see the response body if it makes it to the renderer (even if the
+ // renderer would normally block access to it).
+ GURL foo_url("http://foo.com/cross_site_document_request.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+
+ // The following are files under content/test/data/site_isolation. All
+ // should be disallowed for cross site XHR under the document blocking policy.
+ // valid.* - Correctly labeled HTML/XML/JSON files.
+ // *.txt - Plain text that sniffs as HTML, XML, or JSON.
+ // htmlN_dtd.* - Various HTML templates to test.
+ const char* blocked_resources[] = {
+ "valid.html", "valid.xml", "valid.json", "html.txt",
+ "xml.txt", "json.txt", "comment_valid.html", "html4_dtd.html",
+ "html4_dtd.txt", "html5_dtd.html", "html5_dtd.txt"};
+ for (const char* resource : blocked_resources) {
+ SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
+ &was_blocked));
+ EXPECT_TRUE(was_blocked);
+ InspectHistograms(histograms, true /* should_be_blocked */,
+ true /* should_be_sniffed */, resource,
+ RESOURCE_TYPE_XHR);
+ }
+
+ // These files should be disallowed without sniffing.
+ // nosniff.* - Won't sniff correctly, but blocked because of nosniff.
+ const char* nosniff_blocked_resources[] = {"nosniff.html", "nosniff.xml",
+ "nosniff.json", "nosniff.txt"};
+ for (const char* resource : nosniff_blocked_resources) {
+ SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
+ &was_blocked));
+ EXPECT_TRUE(was_blocked);
+ InspectHistograms(histograms, true /* should_be_blocked */,
+ false /* should_be_sniffed */, resource,
+ RESOURCE_TYPE_XHR);
+ }
+
+ // These files are allowed for XHR under the document blocking policy because
+ // the sniffing logic determines they are not actually documents.
+ // *js.* - JavaScript mislabeled as a document.
+ // jsonp.* - JSONP (i.e., script) mislabeled as a document.
+ // img.* - Contents that won't match the document label.
+ const char* sniff_allowed_resources[] = {
+ "js.html", "comment_js.html", "js.xml", "js.json", "js.txt",
+ "jsonp.html", "jsonp.xml", "jsonp.json", "jsonp.txt", "img.html",
+ "img.xml", "img.json", "img.txt"};
+ for (const char* resource : sniff_allowed_resources) {
+ SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
+ &was_blocked));
+ EXPECT_FALSE(was_blocked);
+ InspectHistograms(histograms, false /* should_be_blocked */,
+ true /* should_be_sniffed */, resource,
+ RESOURCE_TYPE_XHR);
+ }
+
+ // These files should be allowed for XHR under the document blocking policy.
+ // cors.* - Correctly labeled documents with valid CORS headers.
+ // valid.* - Correctly labeled responses of non-document types.
+ const char* allowed_resources[] = {"cors.html", "cors.xml", "cors.json",
+ "cors.txt", "valid.js"};
+ for (const char* resource : allowed_resources) {
+ SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
+ &was_blocked));
+ EXPECT_FALSE(was_blocked);
+ InspectHistograms(histograms, false /* should_be_blocked */,
+ false /* should_be_sniffed */, resource,
+ RESOURCE_TYPE_XHR);
+ }
+}
+
+// Verify that range requests disable the sniffing logic, so that attackers
+// can't cause sniffing to fail to force a response to be allowed. This won't
+// be a problem for script files mislabeled as HTML/XML/JSON/text (i.e., the
+// reason for sniffing), since script tags won't send Range headers.
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, RangeRequest) {
+ GURL foo_url("http://foo.com/cross_site_document_request.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+
+ {
+ // Try to skip the first byte using a range request in an attempt to get the
+ // response to fail sniffing and be allowed through. It should still be
+ // blocked because sniffing is disabled.
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest('valid.html', 'bytes=1-24');", &was_blocked));
+ EXPECT_TRUE(was_blocked);
+ InspectHistograms(histograms, true /* should_be_blocked */,
+ false /* should_be_sniffed */, "valid.html",
+ RESOURCE_TYPE_XHR);
+ }
+ {
+ // Verify that a response which would have been allowed by MIME type anyway
+ // is still allowed for range requests.
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest('valid.js', 'bytes=1-5');", &was_blocked));
+ EXPECT_FALSE(was_blocked);
+ InspectHistograms(histograms, false /* should_be_blocked */,
+ false /* should_be_sniffed */, "valid.js",
+ RESOURCE_TYPE_XHR);
+ }
+ {
+ // Verify that a response which would have been allowed by CORS anyway is
+ // still allowed for range requests.
+ base::HistogramTester histograms;
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest('cors.json', 'bytes=2-7');", &was_blocked));
+ EXPECT_FALSE(was_blocked);
+ InspectHistograms(histograms, false /* should_be_blocked */,
+ false /* should_be_sniffed */, "cors.json",
+ RESOURCE_TYPE_XHR);
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockForVariousTargets) {
+ // This webpage loads a cross-site HTML page in different targets such as
+ // <img>,<link>,<embed>, etc. Since the requested document is blocked, and one
+ // character string (' ') is returned instead, this tests that the renderer
+ // does not crash even when it receives a response body which is " ", whose
+ // length is different from what's described in "content-length" for such
+ // different targets.
+
+ // TODO(nick): Split up these cases, and add positive assertions here about
+ // what actually happens in these various resource-block cases.
+ GURL foo("http://foo.com/cross_site_document_request_target.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo));
+ WaitForLoadStop(shell()->web_contents());
+
+ // TODO(creis): Wait for all the subresources to load and ensure renderer
+ // process is still alive.
+}
+
+class CrossSiteDocumentBlockingKillSwitchTest
+ : public CrossSiteDocumentBlockingTest {
+ public:
+ CrossSiteDocumentBlockingKillSwitchTest() {
+ // Simulate flipping the kill switch.
+ scoped_feature_list_.InitAndDisableFeature(
+ features::kCrossSiteDocumentBlockingIfIsolating);
+ }
+
+ ~CrossSiteDocumentBlockingKillSwitchTest() override {}
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingKillSwitchTest);
+};
+
+// After the kill switch is flipped, there should be no document blocking.
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingKillSwitchTest,
+ NoBlockingWithKillSwitch) {
+ // Load a page that issues illegal cross-site document requests to bar.com.
+ GURL foo_url("http://foo.com/cross_site_document_request.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest(\"valid.html\");", &was_blocked));
+ EXPECT_FALSE(was_blocked);
+}
+
+// Without any Site Isolation (in the base test class), there should be no
+// document blocking.
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingBaseTest,
+ DontBlockDocumentsByDefault) {
+ if (AreAllSitesIsolatedForTesting())
+ return;
+
+ // Load a page that issues illegal cross-site document requests to bar.com.
+ GURL foo_url("http://foo.com/cross_site_document_request.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest(\"valid.html\");", &was_blocked));
+ EXPECT_FALSE(was_blocked);
+}
+
+// Test class to verify that documents are blocked for isolated origins as well.
+class CrossSiteDocumentBlockingIsolatedOriginTest
+ : public CrossSiteDocumentBlockingBaseTest {
+ public:
+ CrossSiteDocumentBlockingIsolatedOriginTest() {}
+ ~CrossSiteDocumentBlockingIsolatedOriginTest() override {}
+
+ void SetUpCommandLine(base::CommandLine* command_line) override {
+ command_line->AppendSwitchASCII(switches::kIsolateOrigins,
+ "http://bar.com");
+ CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingIsolatedOriginTest);
+};
+
+IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingIsolatedOriginTest,
+ BlockDocumentsFromIsolatedOrigin) {
+ if (AreAllSitesIsolatedForTesting())
+ return;
+
+ // Load a page that issues illegal cross-site document requests to the
+ // isolated origin.
+ GURL foo_url("http://foo.com/cross_site_document_request.html");
+ EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+
+ bool was_blocked;
+ ASSERT_TRUE(ExecuteScriptAndExtractBool(
+ shell(), "sendRequest(\"valid.html\");", &was_blocked));
+ EXPECT_TRUE(was_blocked);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler.cc b/chromium/content/browser/loader/cross_site_document_resource_handler.cc
new file mode 100644
index 00000000000..475ead7f3af
--- /dev/null
+++ b/chromium/content/browser/loader/cross_site_document_resource_handler.cc
@@ -0,0 +1,420 @@
+// Copyright 2017 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/loader/cross_site_document_resource_handler.h"
+
+#include <string.h>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_piece.h"
+#include "base/trace_event/trace_event.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/loader/detachable_resource_handler.h"
+#include "content/browser/loader/resource_request_info_impl.h"
+#include "content/browser/site_instance_impl.h"
+#include "content/common/site_isolation_policy.h"
+#include "content/public/browser/content_browser_client.h"
+#include "content/public/browser/resource_context.h"
+#include "content/public/common/content_client.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
+#include "net/url_request/url_request.h"
+
+namespace content {
+
+namespace {
+
+void LogCrossSiteDocumentAction(
+ CrossSiteDocumentResourceHandler::Action action) {
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Action", action,
+ CrossSiteDocumentResourceHandler::Action::kCount);
+}
+
+} // namespace
+
+// ResourceController used in OnWillRead in cases that sniffing will happen.
+// When invoked, it runs the corresponding method on the ResourceHandler.
+class CrossSiteDocumentResourceHandler::OnWillReadController
+ : public ResourceController {
+ public:
+ // Keeps track of the addresses of the ResourceLoader's buffer and size,
+ // which will be populated by the downstream ResourceHandler by the time that
+ // Resume() is called.
+ explicit OnWillReadController(
+ CrossSiteDocumentResourceHandler* document_handler,
+ scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size)
+ : document_handler_(document_handler), buf_(buf), buf_size_(buf_size) {}
+
+ ~OnWillReadController() override {}
+
+ // ResourceController implementation:
+ void Resume() override {
+ MarkAsUsed();
+
+ // Now that |buf_| has a buffer written into it by the downstream handler,
+ // set up sniffing in the CrossSiteDocumentResourceHandler.
+ document_handler_->ResumeOnWillRead(buf_, buf_size_);
+ }
+
+ void Cancel() override {
+ MarkAsUsed();
+ document_handler_->Cancel();
+ }
+
+ void CancelWithError(int error_code) override {
+ MarkAsUsed();
+ document_handler_->CancelWithError(error_code);
+ }
+
+ private:
+ void MarkAsUsed() {
+#if DCHECK_IS_ON()
+ DCHECK(!used_);
+ used_ = true;
+#endif
+ }
+
+#if DCHECK_IS_ON()
+ bool used_ = false;
+#endif
+
+ CrossSiteDocumentResourceHandler* document_handler_;
+
+ // Address of the ResourceLoader's buffer, which will be populated by the
+ // downstream handler before Resume() is called.
+ scoped_refptr<net::IOBuffer>* buf_;
+
+ // Address of the size of |buf_|, similarly populated downstream.
+ int* buf_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(OnWillReadController);
+};
+
+CrossSiteDocumentResourceHandler::CrossSiteDocumentResourceHandler(
+ std::unique_ptr<ResourceHandler> next_handler,
+ net::URLRequest* request,
+ bool is_nocors_plugin_request)
+ : LayeredResourceHandler(request, std::move(next_handler)),
+ is_nocors_plugin_request_(is_nocors_plugin_request) {}
+
+CrossSiteDocumentResourceHandler::~CrossSiteDocumentResourceHandler() {}
+
+void CrossSiteDocumentResourceHandler::OnResponseStarted(
+ ResourceResponse* response,
+ std::unique_ptr<ResourceController> controller) {
+ has_response_started_ = true;
+ LogCrossSiteDocumentAction(
+ CrossSiteDocumentResourceHandler::Action::kResponseStarted);
+
+ should_block_based_on_headers_ = ShouldBlockBasedOnHeaders(response);
+ next_handler_->OnResponseStarted(response, std::move(controller));
+}
+
+void CrossSiteDocumentResourceHandler::OnWillRead(
+ scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size,
+ std::unique_ptr<ResourceController> controller) {
+ DCHECK(has_response_started_);
+
+ // On the next read attempt after the response was blocked, either cancel the
+ // rest of the request or allow it to proceed in a detached state.
+ if (blocked_read_completed_) {
+ DCHECK(should_block_based_on_headers_);
+ DCHECK(!allow_based_on_sniffing_);
+ const ResourceRequestInfoImpl* info = GetRequestInfo();
+ if (info && info->detachable_handler()) {
+ // Ensure that prefetch, etc, continue to cache the response, without
+ // sending it to the renderer.
+ info->detachable_handler()->Detach();
+ } else {
+ // If it's not detachable, cancel the rest of the request.
+ controller->Cancel();
+ }
+ return;
+ }
+
+ // If we intended to block the response and haven't yet decided to allow it
+ // due to sniffing, we will read some of the data to a local buffer to sniff
+ // it. Since the downstream handler may defer during the OnWillRead call
+ // below, the values of |buf| and |buf_size| may not be available right away.
+ // Instead, create an OnWillReadController to start the sniffing after the
+ // downstream handler has called Resume on it.
+ if (should_block_based_on_headers_ && !allow_based_on_sniffing_) {
+ HoldController(std::move(controller));
+ controller = std::make_unique<OnWillReadController>(this, buf, buf_size);
+ }
+
+ // Have the downstream handler(s) allocate the real buffer to use.
+ next_handler_->OnWillRead(buf, buf_size, std::move(controller));
+}
+
+void CrossSiteDocumentResourceHandler::ResumeOnWillRead(
+ scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size) {
+ // We should only get here in cases that we intend to sniff the data, after
+ // downstream handler finishes its work from OnWillRead.
+ DCHECK(should_block_based_on_headers_);
+ DCHECK(!allow_based_on_sniffing_);
+ DCHECK(!blocked_read_completed_);
+
+ // For most blocked responses, we need to sniff the data to confirm it looks
+ // like the claimed MIME type (to avoid blocking mislabeled JavaScript,
+ // JSONP, etc). Read this data into a separate buffer (not shared with the
+ // renderer), which we will only copy over if we decide to allow it through.
+ // This is only done when we suspect the response should be blocked.
+ //
+ // Make it as big as the downstream handler's buffer to make it easy to copy
+ // over in one operation. This will be large, since the MIME sniffing
+ // handler is downstream. Technically we could use a smaller buffer if
+ // |needs_sniffing_| is false, but there's no need for the extra complexity.
+ DCHECK_GE(*buf_size, net::kMaxBytesToSniff);
+ local_buffer_ =
+ base::MakeRefCounted<net::IOBuffer>(static_cast<size_t>(*buf_size));
+
+ // Store the next handler's buffer but don't read into it while sniffing,
+ // since we probably won't want to send the data to the renderer process.
+ next_handler_buffer_ = *buf;
+ next_handler_buffer_size_ = *buf_size;
+ *buf = local_buffer_;
+
+ Resume();
+}
+
+void CrossSiteDocumentResourceHandler::OnReadCompleted(
+ int bytes_read,
+ std::unique_ptr<ResourceController> controller) {
+ DCHECK(has_response_started_);
+ DCHECK(!blocked_read_completed_);
+
+ // If we intended to block the response and haven't sniffed yet, try to
+ // confirm that we should block it. If sniffing is needed, look at the local
+ // buffer and either report that zero bytes were read (to indicate the
+ // response is empty and complete), or copy the sniffed data to the next
+ // handler's buffer and resume the response without blocking.
+ if (should_block_based_on_headers_ && !allow_based_on_sniffing_) {
+ bool confirmed_blockable = false;
+ if (!needs_sniffing_) {
+ // TODO(creis): Also consider the MIME type confirmed if |bytes_read| is
+ // too small to do sniffing, or restructure to allow buffering enough.
+ // For now, responses with small initial reads may be allowed through.
+ confirmed_blockable = true;
+ } else {
+ // Sniff the data to see if it likely matches the MIME type that caused us
+ // to decide to block it. If it doesn't match, it may be JavaScript,
+ // JSONP, or another allowable data type and we should let it through.
+ // Record how many bytes were read to see how often it's too small. (This
+ // will typically be under 100,000.)
+ UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.Browser.BytesReadForSniffing",
+ bytes_read);
+ DCHECK_LE(bytes_read, next_handler_buffer_size_);
+ base::StringPiece data(local_buffer_->data(), bytes_read);
+
+ // Confirm whether the data is HTML, XML, or JSON.
+ if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_HTML) {
+ confirmed_blockable = CrossSiteDocumentClassifier::SniffForHTML(data);
+ } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_XML) {
+ confirmed_blockable = CrossSiteDocumentClassifier::SniffForXML(data);
+ } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_JSON) {
+ confirmed_blockable = CrossSiteDocumentClassifier::SniffForJSON(data);
+ } else if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN) {
+ // For responses labeled as plain text, only block them if the data
+ // sniffs as one of the formats we would block in the first place.
+ confirmed_blockable = CrossSiteDocumentClassifier::SniffForHTML(data) ||
+ CrossSiteDocumentClassifier::SniffForXML(data) ||
+ CrossSiteDocumentClassifier::SniffForJSON(data);
+ }
+ }
+
+ if (confirmed_blockable) {
+ // Block the response and throw away the data. Report zero bytes read.
+ bytes_read = 0;
+ blocked_read_completed_ = true;
+
+ // Log the blocking event. Inline the Serialize call to avoid it when
+ // tracing is disabled.
+ TRACE_EVENT2("navigation",
+ "CrossSiteDocumentResourceHandler::ShouldBlockResponse",
+ "initiator",
+ request()->initiator().has_value()
+ ? request()->initiator().value().Serialize()
+ : "null",
+ "url", request()->url().spec());
+
+ LogCrossSiteDocumentAction(
+ needs_sniffing_
+ ? CrossSiteDocumentResourceHandler::Action::kBlockedAfterSniffing
+ : CrossSiteDocumentResourceHandler::Action::
+ kBlockedWithoutSniffing);
+ ResourceType resource_type = GetRequestInfo()->GetResourceType();
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked",
+ resource_type,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ switch (canonical_mime_type_) {
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_HTML:
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.HTML",
+ resource_type,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_XML:
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.XML",
+ resource_type,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_JSON:
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.JSON",
+ resource_type,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN:
+ UMA_HISTOGRAM_ENUMERATION("SiteIsolation.XSD.Browser.Blocked.Plain",
+ resource_type,
+ content::RESOURCE_TYPE_LAST_TYPE);
+ break;
+ default:
+ NOTREACHED();
+ }
+ } else {
+ // Allow the response through instead and proceed with reading more.
+ // Copy sniffed data into the next handler's buffer before proceeding.
+ // Note that the size of the two buffers is the same (see OnWillRead).
+ DCHECK_LE(bytes_read, next_handler_buffer_size_);
+ memcpy(next_handler_buffer_->data(), local_buffer_->data(), bytes_read);
+ allow_based_on_sniffing_ = true;
+ }
+
+ // Clean up, whether we'll cancel or proceed from here.
+ local_buffer_ = nullptr;
+ next_handler_buffer_ = nullptr;
+ next_handler_buffer_size_ = 0;
+ }
+
+ next_handler_->OnReadCompleted(bytes_read, std::move(controller));
+}
+
+void CrossSiteDocumentResourceHandler::OnResponseCompleted(
+ const net::URLRequestStatus& status,
+ std::unique_ptr<ResourceController> controller) {
+ if (blocked_read_completed_) {
+ // Report blocked responses as successful, rather than the cancellation
+ // from OnWillRead.
+ next_handler_->OnResponseCompleted(net::URLRequestStatus(),
+ std::move(controller));
+ } else {
+ LogCrossSiteDocumentAction(
+ needs_sniffing_
+ ? CrossSiteDocumentResourceHandler::Action::kAllowedAfterSniffing
+ : CrossSiteDocumentResourceHandler::Action::
+ kAllowedWithoutSniffing);
+
+ next_handler_->OnResponseCompleted(status, std::move(controller));
+ }
+}
+
+bool CrossSiteDocumentResourceHandler::ShouldBlockBasedOnHeaders(
+ ResourceResponse* response) {
+ // The checks in this method are ordered to rule out blocking in most cases as
+ // quickly as possible. Checks that are likely to lead to returning false or
+ // that are inexpensive should be near the top.
+ const GURL& url = request()->url();
+
+ // Check if the response's site needs to have its documents protected. By
+ // default, this will usually return false.
+ // TODO(creis): This check can go away once the logic here is made fully
+ // backward compatible and we can enforce it always, regardless of Site
+ // Isolation policy.
+ switch (SiteIsolationPolicy::IsCrossSiteDocumentBlockingEnabled()) {
+ case SiteIsolationPolicy::XSDB_ENABLED_UNCONDITIONALLY:
+ break;
+ case SiteIsolationPolicy::XSDB_ENABLED_IF_ISOLATED:
+ if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites() &&
+ !ChildProcessSecurityPolicyImpl::GetInstance()->IsIsolatedOrigin(
+ url::Origin::Create(url))) {
+ return false;
+ }
+ break;
+ case SiteIsolationPolicy::XSDB_DISABLED:
+ return false;
+ }
+
+ // Look up MIME type. If it doesn't claim to be a blockable type (i.e., HTML,
+ // XML, JSON, or plain text), don't block it.
+ canonical_mime_type_ = CrossSiteDocumentClassifier::GetCanonicalMimeType(
+ response->head.mime_type);
+ if (canonical_mime_type_ == CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS)
+ return false;
+
+ // Treat a missing initiator as an empty origin to be safe, though we don't
+ // expect this to happen. Unfortunately, this requires a copy.
+ url::Origin initiator;
+ if (request()->initiator().has_value())
+ initiator = request()->initiator().value();
+
+ // Don't block same-site documents.
+ if (CrossSiteDocumentClassifier::IsSameSite(initiator, url))
+ return false;
+
+ // Only block documents from HTTP(S) schemes.
+ if (!CrossSiteDocumentClassifier::IsBlockableScheme(url))
+ return false;
+
+ // Allow requests from file:// URLs for now.
+ // TODO(creis): Limit this to when the allow_universal_access_from_file_urls
+ // preference is set. See https://crbug.com/789781.
+ if (initiator.scheme() == url::kFileScheme)
+ return false;
+
+ // Only block if this is a request made from a renderer process.
+ const ResourceRequestInfoImpl* info = GetRequestInfo();
+ if (!info || info->GetChildID() == -1)
+ return false;
+
+ // Give embedder a chance to skip document blocking for this response.
+ if (GetContentClient()->browser()->ShouldBypassDocumentBlocking(
+ initiator, url, info->GetResourceType())) {
+ return false;
+ }
+
+ // Allow the response through if it has valid CORS headers.
+ std::string cors_header;
+ response->head.headers->GetNormalizedHeader("access-control-allow-origin",
+ &cors_header);
+ if (CrossSiteDocumentClassifier::IsValidCorsHeaderSet(initiator, url,
+ cors_header)) {
+ return false;
+ }
+
+ // Don't block plugin requests with universal access (e.g., Flash). Such
+ // requests are made without CORS, and thus dont have an Origin request
+ // header. Other plugin requests (e.g., NaCl) are made using CORS and have an
+ // Origin request header. If they fail the CORS check above, they should be
+ // blocked.
+ if (info->GetResourceType() == RESOURCE_TYPE_PLUGIN_RESOURCE &&
+ is_nocors_plugin_request_) {
+ return false;
+ }
+
+ // We intend to block the response at this point. However, we will usually
+ // sniff the contents to confirm the MIME type, to avoid blocking incorrectly
+ // labeled JavaScript, JSONP, etc files.
+ //
+ // Note: only sniff if there isn't a nosniff header, and if it is not a range
+ // request. Range requests would let an attacker bypass blocking by
+ // requesting a range that fails to sniff as a protected type.
+ std::string nosniff_header;
+ response->head.headers->GetNormalizedHeader("x-content-type-options",
+ &nosniff_header);
+ std::string range_header;
+ response->head.headers->GetNormalizedHeader("content-range", &range_header);
+ needs_sniffing_ = !base::LowerCaseEqualsASCII(nosniff_header, "nosniff") &&
+ range_header.empty();
+
+ return true;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler.h b/chromium/content/browser/loader/cross_site_document_resource_handler.h
new file mode 100644
index 00000000000..4dd1d4a89b5
--- /dev/null
+++ b/chromium/content/browser/loader/cross_site_document_resource_handler.h
@@ -0,0 +1,156 @@
+// Copyright 2017 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_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "content/browser/loader/layered_resource_handler.h"
+#include "content/common/cross_site_document_classifier.h"
+#include "content/public/common/resource_type.h"
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+namespace content {
+
+// A ResourceHandler that prevents the renderer process from receiving network
+// responses that contain cross-site documents (HTML, XML, some plain text) or
+// similar data that should be opaque (JSON), with appropriate exceptions to
+// preserve compatibility. Other cross-site resources such as scripts, images,
+// stylesheets, etc are still allowed.
+//
+// This handler is not used for navigations, which create a new security context
+// based on the origin of the response. It currently only protects documents
+// from sites that require dedicated renderer processes, though it could be
+// expanded to apply to all sites.
+//
+// When a response is blocked, the renderer is sent an empty response body
+// instead of seeing a failed request. A failed request would change page-
+// visible behavior (e.g., for a blocked XHR). An empty response can generally
+// be consumed by the renderer without noticing the difference.
+//
+// For more details, see:
+// http://chromium.org/developers/design-documents/blocking-cross-site-documents
+class CONTENT_EXPORT CrossSiteDocumentResourceHandler
+ : public LayeredResourceHandler {
+ public:
+ // This enum backs a histogram. Update enums.xml if you make any updates, and
+ // put new entries before |kCount|.
+ enum class Action {
+ // Logged at OnResponseStarted.
+ kResponseStarted,
+
+ // Logged when a response is blocked without requiring sniffing.
+ kBlockedWithoutSniffing,
+
+ // Logged when a response is blocked as a result of sniffing the content.
+ kBlockedAfterSniffing,
+
+ // Logged when a response is allowed without requiring sniffing.
+ kAllowedWithoutSniffing,
+
+ // Logged when a response is allowed as a result of sniffing the content.
+ kAllowedAfterSniffing,
+
+ kCount
+ };
+
+ CrossSiteDocumentResourceHandler(
+ std::unique_ptr<ResourceHandler> next_handler,
+ net::URLRequest* request,
+ bool is_nocors_plugin_request);
+ ~CrossSiteDocumentResourceHandler() override;
+
+ // LayeredResourceHandler overrides:
+ void OnResponseStarted(
+ ResourceResponse* response,
+ std::unique_ptr<ResourceController> controller) override;
+ void OnWillRead(scoped_refptr<net::IOBuffer>* buf,
+ int* buf_size,
+ std::unique_ptr<ResourceController> controller) override;
+ void OnReadCompleted(int bytes_read,
+ std::unique_ptr<ResourceController> controller) override;
+ void OnResponseCompleted(
+ const net::URLRequestStatus& status,
+ std::unique_ptr<ResourceController> controller) override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(CrossSiteDocumentResourceHandlerTest,
+ ResponseBlocking);
+ FRIEND_TEST_ALL_PREFIXES(CrossSiteDocumentResourceHandlerTest,
+ OnWillReadDefer);
+
+ // ResourceController that manages the read buffer if a downstream handler
+ // defers during OnWillRead.
+ class OnWillReadController;
+
+ // Computes whether this response contains a cross-site document that needs to
+ // be blocked from the renderer process. This is a first approximation based
+ // on the headers, and may be revised after some of the data is sniffed.
+ bool ShouldBlockBasedOnHeaders(ResourceResponse* response);
+
+ // Once the downstream handler has allocated the buffer for OnWillRead
+ // (possibly after deferring), this sets up sniffing into a local buffer.
+ // Called by the OnWillReadController.
+ void ResumeOnWillRead(scoped_refptr<net::IOBuffer>* buf, int* buf_size);
+
+ // A local buffer for sniffing content and using for throwaway reads.
+ // This is not shared with the renderer process.
+ scoped_refptr<net::IOBuffer> local_buffer_;
+
+ // The buffer allocated by the next ResourceHandler for reads, which is used
+ // if sniffing determines that we should proceed with the response.
+ scoped_refptr<net::IOBuffer> next_handler_buffer_;
+
+ // The size of |next_handler_buffer_|.
+ int next_handler_buffer_size_ = 0;
+
+ // A canonicalization of the specified MIME type, to determine if blocking the
+ // response is needed, as well as which type of sniffing to perform.
+ CrossSiteDocumentMimeType canonical_mime_type_ =
+ CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS;
+
+ // Indicates whether this request was made by a plugin and was not using CORS.
+ // Such requests are exempt from blocking, while other plugin requests must be
+ // blocked if the CORS check fails.
+ // TODO(creis, nick): Replace this with a plugin process ID check to see if
+ // the plugin has universal access.
+ bool is_nocors_plugin_request_;
+
+ // Tracks whether OnResponseStarted has been called, to ensure that it happens
+ // before OnWillRead and OnReadCompleted.
+ bool has_response_started_ = false;
+
+ // Whether this response is a cross-site document that should be blocked,
+ // pending the outcome of sniffing the content. Set in OnResponseStarted and
+ // should only be read afterwards.
+ bool should_block_based_on_headers_ = false;
+
+ // Whether the response data should be sniffed before blocking it, to avoid
+ // blocking mislabeled responses (e.g., JSONP labeled as HTML). This is
+ // usually true when |should_block_based_on_headers_| is set, unless there is
+ // a nosniff header or range request.
+ bool needs_sniffing_ = false;
+
+ // Whether this response will be allowed through despite being flagged for
+ // blocking (via |should_block_based_on_headers_), because sniffing determined
+ // it was incorrectly labeled and might be needed for compatibility (e.g.,
+ // in case it is Javascript).
+ bool allow_based_on_sniffing_ = false;
+
+ // Whether the next ResourceHandler has already been told that the read has
+ // completed, and thus it is safe to cancel or detach on the next read.
+ bool blocked_read_completed_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentResourceHandler);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_LOADER_CROSS_SITE_DOCUMENT_RESOURCE_HANDLER_H_
diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc b/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc
new file mode 100644
index 00000000000..ff3f1423c00
--- /dev/null
+++ b/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc
@@ -0,0 +1,836 @@
+// Copyright 2017 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/loader/cross_site_document_resource_handler.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/histogram_tester.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/loader/mock_resource_loader.h"
+#include "content/browser/loader/resource_controller.h"
+#include "content/browser/loader/test_resource_handler.h"
+#include "content/public/browser/resource_request_info.h"
+#include "content/public/common/resource_response.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/common/webplugininfo.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "net/base/net_errors.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_status.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+enum class OriginHeader { kOmit, kInclude };
+
+enum class AccessControlAllowOriginHeader {
+ kOmit,
+ kAllowAny,
+ kAllowNull,
+ kAllowInitiatorOrigin,
+ kAllowExampleDotCom
+};
+
+enum class Verdict {
+ kAllowWithoutSniffing,
+ kBlockWithoutSniffing,
+ kAllowAfterSniffing,
+ kBlockAfterSniffing
+};
+
+// This struct is used to describe each test case in this file. It's passed as
+// a test parameter to each TEST_P test.
+struct TestScenario {
+ // Attributes to make test failure messages useful.
+ const char* description;
+ int source_line;
+
+ // Attributes of the HTTP Request.
+ const char* target_url;
+ ResourceType resource_type;
+ const char* initiator_origin;
+ OriginHeader cors_request;
+
+ // Attributes of the HTTP response.
+ const char* response_mime_type;
+ CrossSiteDocumentMimeType canonical_mime_type;
+ bool include_no_sniff_header;
+ AccessControlAllowOriginHeader cors_response;
+ const char* first_chunk;
+
+ // Expected result.
+ Verdict verdict;
+};
+
+// Stream operator to let GetParam() print a useful result if any tests fail.
+::std::ostream& operator<<(::std::ostream& os, const TestScenario& scenario) {
+ std::string cors_response;
+ switch (scenario.cors_response) {
+ case AccessControlAllowOriginHeader::kOmit:
+ cors_response = "AccessControlAllowOriginHeader::kOmit";
+ break;
+ case AccessControlAllowOriginHeader::kAllowAny:
+ cors_response = "AccessControlAllowOriginHeader::kAllowAny";
+ break;
+ case AccessControlAllowOriginHeader::kAllowNull:
+ cors_response = "AccessControlAllowOriginHeader::kAllowNull";
+ break;
+ case AccessControlAllowOriginHeader::kAllowInitiatorOrigin:
+ cors_response = "AccessControlAllowOriginHeader::kAllowInitiatorOrigin";
+ break;
+ case AccessControlAllowOriginHeader::kAllowExampleDotCom:
+ cors_response = "AccessControlAllowOriginHeader::kAllowExampleDotCom";
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ std::string verdict;
+ switch (scenario.verdict) {
+ case Verdict::kAllowWithoutSniffing:
+ verdict = "Verdict::kAllowWithoutSniffing";
+ break;
+ case Verdict::kBlockWithoutSniffing:
+ verdict = "Verdict::kBlockWithoutSniffing";
+ break;
+ case Verdict::kAllowAfterSniffing:
+ verdict = "Verdict::kAllowAfterSniffing";
+ break;
+ case Verdict::kBlockAfterSniffing:
+ verdict = "Verdict::kBlockAfterSniffing";
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ return os << "\n description = " << scenario.description
+ << "\n target_url = " << scenario.target_url
+ << "\n resource_type = " << scenario.resource_type
+ << "\n initiator_origin = " << scenario.initiator_origin
+ << "\n cors_request = "
+ << (scenario.cors_request == OriginHeader::kOmit
+ ? "OriginHeader::kOmit"
+ : "OriginHeader::kInclude")
+ << "\n response_mime_type = " << scenario.response_mime_type
+ << "\n canonical_mime_type = " << scenario.canonical_mime_type
+ << "\n include_no_sniff = "
+ << (scenario.include_no_sniff_header ? "true" : "false")
+ << "\n cors_response = " << cors_response
+ << "\n first_chunk = " << scenario.first_chunk
+ << "\n verdict = " << verdict;
+}
+
+// A set of test cases that verify CrossSiteDocumentResourceHandler correctly
+// classifies network responses as allowed or blocked. These TestScenarios are
+// passed to the TEST_P tests below as test parameters.
+const TestScenario kScenarios[] = {
+ // Allowed responses:
+ {
+ "Allowed: Same-site XHR to HTML", __LINE__,
+ "http://www.a.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site script", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_SCRIPT, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "application/javascript", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "var x=3;", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to HTML with CORS for origin", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kAllowInitiatorOrigin, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to XML with CORS for any", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "application/rss+xml", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kAllowAny, // cors_response
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to JSON with CORS for null", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "text/json", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kAllowNull, // cors_response
+ "{\"x\" : 3}", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to HTML over FTP", __LINE__,
+ "ftp://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to HTML from file://", __LINE__,
+ "file:///foo/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site fetch HTML from Flash without CORS", __LINE__,
+ "http://www.b.com/plugin.html", // target_url
+ RESOURCE_TYPE_PLUGIN_RESOURCE, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site fetch HTML from NaCl with CORS response", __LINE__,
+ "http://www.b.com/plugin.html", // target_url
+ RESOURCE_TYPE_PLUGIN_RESOURCE, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kAllowInitiatorOrigin, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kAllowWithoutSniffing, // verdict
+ },
+
+ // Allowed responses due to sniffing:
+ {
+ "Allowed: Cross-site script to JSONP labeled as HTML", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_SCRIPT, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "foo({\"x\" : 3})", // first_chunk
+ Verdict::kAllowAfterSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site script to JavaScript labeled as text", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_SCRIPT, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/plain", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "var x = 3;", // first_chunk
+ Verdict::kAllowAfterSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to nonsense labeled as XML", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "application/xml", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "Won't sniff as XML", // first_chunk
+ Verdict::kAllowAfterSniffing, // verdict
+ },
+ {
+ "Allowed: Cross-site XHR to nonsense labeled as JSON", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/x-json", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "Won't sniff as JSON", // first_chunk
+ Verdict::kAllowAfterSniffing, // verdict
+ },
+ // TODO(creis): We should block the following response since there isn't
+ // enough data to confirm it as HTML by sniffing.
+ {
+ "Allowed for now: Cross-site XHR to HTML with small first read",
+ __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<htm", // first_chunk
+ Verdict::kAllowAfterSniffing, // verdict
+ },
+
+ // Blocked responses:
+ {
+ "Blocked: Cross-site XHR to HTML without CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to XML without CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "application/xml", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_XML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to JSON without CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "application/json", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_JSON, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "{\"x\" : 3}", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to HTML labeled as text without CORS",
+ __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/plain", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to nosniff HTML without CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ true, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kBlockWithoutSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to nosniff response without CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ true, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "Wouldn't sniff as HTML", // first_chunk
+ Verdict::kBlockWithoutSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site <script> inclusion of HTML w/ DTD without CORS",
+ __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_SCRIPT, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kOmit, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<!doctype html><html itemscope=\"\" "
+ "itemtype=\"http://schema.org/SearchResultsPage\" "
+ "lang=\"en\"><head>", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site XHR to HTML with wrong CORS", __LINE__,
+ "http://www.b.com/resource.html", // target_url
+ RESOURCE_TYPE_XHR, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kAllowExampleDotCom, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+ {
+ "Blocked: Cross-site fetch HTML from NaCl without CORS response",
+ __LINE__,
+ "http://www.b.com/plugin.html", // target_url
+ RESOURCE_TYPE_PLUGIN_RESOURCE, // resource_type
+ "http://www.a.com/", // initiator_origin
+ OriginHeader::kInclude, // cors_request
+ "text/html", // response_mime_type
+ CROSS_SITE_DOCUMENT_MIME_TYPE_HTML, // canonical_mime_type
+ false, // include_no_sniff_header
+ AccessControlAllowOriginHeader::kOmit, // cors_response
+ "<html><head>this should sniff as HTML", // first_chunk
+ Verdict::kBlockAfterSniffing, // verdict
+ },
+};
+
+} // namespace
+
+// Tests that verify CrossSiteDocumentResourceHandler correctly classifies
+// network responses as allowed or blocked, and ensures that empty responses are
+// sent for the blocked cases.
+//
+// The various test cases are passed as a list of TestScenario structs.
+class CrossSiteDocumentResourceHandlerTest
+ : public testing::Test,
+ public testing::WithParamInterface<TestScenario> {
+ public:
+ CrossSiteDocumentResourceHandlerTest()
+ : stream_sink_status_(
+ net::URLRequestStatus::FromError(net::ERR_IO_PENDING)) {
+ IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
+ }
+
+ // Sets up the request, downstream ResourceHandler, test ResourceHandler, and
+ // ResourceLoader.
+ void Initialize(const std::string& target_url,
+ ResourceType resource_type,
+ const std::string& initiator_origin,
+ OriginHeader cors_request) {
+ stream_sink_status_ = net::URLRequestStatus::FromError(net::ERR_IO_PENDING);
+
+ // Initialize |request_| from the parameters.
+ request_ = context_.CreateRequest(GURL(target_url), net::DEFAULT_PRIORITY,
+ &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
+ ResourceRequestInfo::AllocateForTesting(request_.get(), resource_type,
+ nullptr, // context
+ 3, // render_process_id
+ 2, // render_view_id
+ 1, // render_frame_id
+ true, // is_main_frame
+ true, // allow_download
+ true, // is_async
+ PREVIEWS_OFF); // previews_state
+ request_->set_initiator(url::Origin::Create(GURL(initiator_origin)));
+
+ // Create a sink handler to capture results.
+ auto stream_sink = std::make_unique<TestResourceHandler>(
+ &stream_sink_status_, &stream_sink_body_);
+ stream_sink_ = stream_sink->GetWeakPtr();
+
+ // Create the CrossSiteDocumentResourceHandler.
+ bool is_nocors_plugin_request =
+ resource_type == RESOURCE_TYPE_PLUGIN_RESOURCE &&
+ cors_request == OriginHeader::kOmit;
+ document_blocker_ = std::make_unique<CrossSiteDocumentResourceHandler>(
+ std::move(stream_sink), request_.get(), is_nocors_plugin_request);
+
+ // Create a mock loader to drive the CrossSiteDocumentResourceHandler.
+ mock_loader_ =
+ std::make_unique<MockResourceLoader>(document_blocker_.get());
+ }
+
+ // Returns a ResourceResponse that matches the TestScenario's parameters.
+ scoped_refptr<ResourceResponse> CreateResponse(
+ const char* response_mime_type,
+ bool include_no_sniff_header,
+ AccessControlAllowOriginHeader cors_response,
+ const char* initiator_origin) {
+ scoped_refptr<ResourceResponse> response =
+ base::MakeRefCounted<ResourceResponse>();
+ response->head.mime_type = response_mime_type;
+ scoped_refptr<net::HttpResponseHeaders> response_headers =
+ base::MakeRefCounted<net::HttpResponseHeaders>("");
+
+ // No sniff header.
+ if (include_no_sniff_header)
+ response_headers->AddHeader("X-Content-Type-Options: nosniff");
+
+ // CORS header.
+ if (cors_response == AccessControlAllowOriginHeader::kAllowAny) {
+ response_headers->AddHeader("Access-Control-Allow-Origin: *");
+ } else if (cors_response ==
+ AccessControlAllowOriginHeader::kAllowInitiatorOrigin) {
+ response_headers->AddHeader(base::StringPrintf(
+ "Access-Control-Allow-Origin: %s", initiator_origin));
+ } else if (cors_response == AccessControlAllowOriginHeader::kAllowNull) {
+ response_headers->AddHeader("Access-Control-Allow-Origin: null");
+ } else if (cors_response ==
+ AccessControlAllowOriginHeader::kAllowExampleDotCom) {
+ response_headers->AddHeader(
+ "Access-Control-Allow-Origin: http://example.com");
+ }
+
+ response->head.headers = response_headers;
+
+ return response;
+ }
+
+ protected:
+ TestBrowserThreadBundle thread_bundle_;
+ net::TestURLRequestContext context_;
+ net::TestDelegate delegate_;
+ std::unique_ptr<net::URLRequest> request_;
+
+ // |stream_sink_| is the handler that's immediately after |document_blocker_|
+ // in the ResourceHandler chain; it records the values passed to it into
+ // |stream_sink_status_| and |stream_sink_body_|, which our tests assert
+ // against.
+ //
+ // |stream_sink_| is owned by |document_blocker_|, but we retain a reference
+ // to it.
+ base::WeakPtr<TestResourceHandler> stream_sink_;
+ net::URLRequestStatus stream_sink_status_;
+ std::string stream_sink_body_;
+
+ // |document_blocker_| is the CrossSiteDocuemntResourceHandler instance under
+ // test.
+ std::unique_ptr<CrossSiteDocumentResourceHandler> document_blocker_;
+
+ // |mock_loader_| is the mock loader used to drive |document_blocker_|.
+ std::unique_ptr<MockResourceLoader> mock_loader_;
+};
+
+// Runs a particular TestScenario (passed as the test's parameter) through the
+// ResourceLoader and CrossSiteDocumentResourceHandler, verifying that the
+// response is correctly allowed or blocked based on the scenario.
+TEST_P(CrossSiteDocumentResourceHandlerTest, ResponseBlocking) {
+ const TestScenario scenario = GetParam();
+ SCOPED_TRACE(testing::Message()
+ << "\nScenario at " << __FILE__ << ":" << scenario.source_line);
+
+ Initialize(scenario.target_url, scenario.resource_type,
+ scenario.initiator_origin, scenario.cors_request);
+ base::HistogramTester histograms;
+
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnWillStart(request_->url()));
+
+ // Set up response based on scenario.
+ scoped_refptr<ResourceResponse> response = CreateResponse(
+ scenario.response_mime_type, scenario.include_no_sniff_header,
+ scenario.cors_response, scenario.initiator_origin);
+
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseStarted(response));
+
+ // Verify MIME type was classified correctly.
+ EXPECT_EQ(scenario.canonical_mime_type,
+ document_blocker_->canonical_mime_type_);
+
+ // Verify that we correctly decide whether to block based on headers. Note
+ // that this includes cases that will later be allowed after sniffing.
+ bool expected_to_block_based_on_headers =
+ scenario.verdict == Verdict::kBlockWithoutSniffing ||
+ scenario.verdict == Verdict::kBlockAfterSniffing ||
+ scenario.verdict == Verdict::kAllowAfterSniffing;
+ EXPECT_EQ(expected_to_block_based_on_headers,
+ document_blocker_->should_block_based_on_headers_);
+
+ // Verify that we will sniff content into a different buffer if sniffing is
+ // needed. Note that the different buffer is used even for blocking cases
+ // where no sniffing is needed, to avoid complexity in the handler. The
+ // handler doesn't look at the data in that case, but there's no way to verify
+ // it in the test.
+ bool expected_to_sniff = scenario.verdict == Verdict::kAllowAfterSniffing ||
+ scenario.verdict == Verdict::kBlockAfterSniffing;
+ EXPECT_EQ(expected_to_sniff, document_blocker_->needs_sniffing_);
+
+ // Tell the ResourceHandlers to allocate the buffer for reading. In this
+ // test, the buffer will be allocated immediately by the downstream handler
+ // and possibly replaced by a different buffer for sniffing.
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ EXPECT_EQ(1, stream_sink_->on_will_read_called());
+ EXPECT_NE(nullptr, mock_loader_->io_buffer());
+ if (expected_to_sniff || scenario.verdict == Verdict::kBlockWithoutSniffing) {
+ EXPECT_EQ(mock_loader_->io_buffer(), document_blocker_->local_buffer_.get())
+ << "Should have used a different IOBuffer for sniffing";
+ } else {
+ EXPECT_EQ(mock_loader_->io_buffer(), stream_sink_->buffer())
+ << "Should have used original IOBuffer when sniffing not needed";
+ }
+
+ // Deliver the first chunk of the response body; this allows sniffing to
+ // occur.
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(scenario.first_chunk));
+ EXPECT_EQ(nullptr, mock_loader_->io_buffer());
+
+ // Verify that the response is blocked or allowed as expected.
+ bool should_be_blocked = scenario.verdict == Verdict::kBlockWithoutSniffing ||
+ scenario.verdict == Verdict::kBlockAfterSniffing;
+ if (should_be_blocked) {
+ EXPECT_EQ("", stream_sink_body_)
+ << "Response should not have been delivered to the renderer.";
+ EXPECT_TRUE(document_blocker_->blocked_read_completed_);
+ EXPECT_FALSE(document_blocker_->allow_based_on_sniffing_);
+ } else {
+ EXPECT_EQ(scenario.first_chunk, stream_sink_body_)
+ << "Response should have been delivered to the renderer.";
+ EXPECT_FALSE(document_blocker_->blocked_read_completed_);
+ if (scenario.verdict == Verdict::kAllowAfterSniffing)
+ EXPECT_TRUE(document_blocker_->allow_based_on_sniffing_);
+ }
+
+ if (should_be_blocked) {
+ // The next OnWillRead should cancel and complete the response.
+ ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->OnWillRead());
+ net::URLRequestStatus status(net::URLRequestStatus::CANCELED,
+ net::ERR_ABORTED);
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseCompleted(status));
+ } else {
+ // Simulate the next read being empty to end the response.
+ ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(""));
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseCompleted(
+ net::URLRequestStatus::FromError(net::OK)));
+ }
+
+ // Verify that histograms are correctly incremented.
+ base::HistogramTester::CountsMap expected_counts;
+ std::string histogram_base = "SiteIsolation.XSD.Browser";
+ std::string bucket;
+ switch (scenario.canonical_mime_type) {
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_HTML:
+ bucket = "HTML";
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_XML:
+ bucket = "XML";
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_JSON:
+ bucket = "JSON";
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_PLAIN:
+ bucket = "Plain";
+ break;
+ case CROSS_SITE_DOCUMENT_MIME_TYPE_OTHERS:
+ EXPECT_FALSE(should_be_blocked);
+ bucket = "Others";
+ break;
+ default:
+ NOTREACHED();
+ }
+ int start_action = static_cast<int>(
+ CrossSiteDocumentResourceHandler::Action::kResponseStarted);
+ int end_action = -1;
+ switch (scenario.verdict) {
+ case Verdict::kBlockWithoutSniffing:
+ end_action = static_cast<int>(
+ CrossSiteDocumentResourceHandler::Action::kBlockedWithoutSniffing);
+ break;
+ case Verdict::kBlockAfterSniffing:
+ end_action = static_cast<int>(
+ CrossSiteDocumentResourceHandler::Action::kBlockedAfterSniffing);
+ break;
+ case Verdict::kAllowWithoutSniffing:
+ end_action = static_cast<int>(
+ CrossSiteDocumentResourceHandler::Action::kAllowedWithoutSniffing);
+ break;
+ case Verdict::kAllowAfterSniffing:
+ end_action = static_cast<int>(
+ CrossSiteDocumentResourceHandler::Action::kAllowedAfterSniffing);
+ break;
+ default:
+ NOTREACHED();
+ }
+ // Expecting two actions: ResponseStarted and one of the outcomes.
+ expected_counts[histogram_base + ".Action"] = 2;
+ EXPECT_THAT(histograms.GetAllSamples(histogram_base + ".Action"),
+ testing::ElementsAre(base::Bucket(start_action, 1),
+ base::Bucket(end_action, 1)))
+ << "Should have incremented the right actions.";
+ // Expect to hear the number of bytes in the first read when sniffing is
+ // required.
+ if (expected_to_sniff) {
+ std::string first_chunk = scenario.first_chunk;
+ expected_counts[histogram_base + ".BytesReadForSniffing"] = 1;
+ EXPECT_EQ(
+ 1, histograms.GetBucketCount(histogram_base + ".BytesReadForSniffing",
+ first_chunk.size()));
+ }
+ if (should_be_blocked) {
+ expected_counts[histogram_base + ".Blocked"] = 1;
+ expected_counts[histogram_base + ".Blocked." + bucket] = 1;
+ EXPECT_THAT(histograms.GetAllSamples(histogram_base + ".Blocked"),
+ testing::ElementsAre(base::Bucket(scenario.resource_type, 1)))
+ << "Should have incremented aggregate blocking.";
+ EXPECT_THAT(histograms.GetAllSamples(histogram_base + ".Blocked." + bucket),
+ testing::ElementsAre(base::Bucket(scenario.resource_type, 1)))
+ << "Should have incremented blocking for resource type.";
+ }
+ // Make sure that the expected metrics, and only those metrics, were
+ // incremented.
+ EXPECT_THAT(histograms.GetTotalCountsForPrefix("SiteIsolation.XSD.Browser"),
+ testing::ContainerEq(expected_counts));
+}
+
+// Similar to the ResponseBlocking test above, but simulates the case that the
+// downstream handler does not immediately resume from OnWillRead, in which case
+// the downstream buffer may not be allocated until later.
+TEST_P(CrossSiteDocumentResourceHandlerTest, OnWillReadDefer) {
+ const TestScenario scenario = GetParam();
+ SCOPED_TRACE(testing::Message()
+ << "\nScenario at " << __FILE__ << ":" << scenario.source_line);
+
+ Initialize(scenario.target_url, scenario.resource_type,
+ scenario.initiator_origin, scenario.cors_request);
+
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnWillStart(request_->url()));
+
+ // Set up response based on scenario.
+ scoped_refptr<ResourceResponse> response = CreateResponse(
+ scenario.response_mime_type, scenario.include_no_sniff_header,
+ scenario.cors_response, scenario.initiator_origin);
+
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnResponseStarted(response));
+
+ // Verify that we will sniff content into a different buffer if sniffing is
+ // needed. Note that the different buffer is used even for blocking cases
+ // where no sniffing is needed, to avoid complexity in the handler. The
+ // handler doesn't look at the data in that case, but there's no way to verify
+ // it in the test.
+ bool expected_to_sniff = scenario.verdict == Verdict::kAllowAfterSniffing ||
+ scenario.verdict == Verdict::kBlockAfterSniffing;
+ EXPECT_EQ(expected_to_sniff, document_blocker_->needs_sniffing_);
+
+ // Cause the TestResourceHandler to defer when OnWillRead is called, to make
+ // sure the test scenarios still work when the downstream handler's buffer
+ // isn't allocated in the same call.
+ stream_sink_->set_defer_on_will_read(true);
+ ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
+ mock_loader_->OnWillRead());
+ EXPECT_EQ(1, stream_sink_->on_will_read_called());
+
+ // No buffers have been allocated yet.
+ EXPECT_EQ(nullptr, mock_loader_->io_buffer());
+ EXPECT_EQ(nullptr, document_blocker_->local_buffer_.get());
+
+ // Resume the downstream handler, which should establish a buffer for the
+ // ResourceLoader (either the downstream one or a local one for sniffing).
+ stream_sink_->Resume();
+ EXPECT_NE(nullptr, mock_loader_->io_buffer());
+ if (expected_to_sniff || scenario.verdict == Verdict::kBlockWithoutSniffing) {
+ EXPECT_EQ(mock_loader_->io_buffer(), document_blocker_->local_buffer_.get())
+ << "Should have used a different IOBuffer for sniffing";
+ } else {
+ EXPECT_EQ(mock_loader_->io_buffer(), stream_sink_->buffer())
+ << "Should have used original IOBuffer when sniffing not needed";
+ }
+
+ // Deliver the first chunk of the response body; this allows sniffing to
+ // occur.
+ ASSERT_EQ(MockResourceLoader::Status::IDLE,
+ mock_loader_->OnReadCompleted(scenario.first_chunk));
+ EXPECT_EQ(nullptr, mock_loader_->io_buffer());
+
+ // Verify that the response is blocked or allowed as expected.
+ if (scenario.verdict == Verdict::kBlockWithoutSniffing ||
+ scenario.verdict == Verdict::kBlockAfterSniffing) {
+ EXPECT_EQ("", stream_sink_body_)
+ << "Response should not have been delivered to the renderer.";
+ EXPECT_TRUE(document_blocker_->blocked_read_completed_);
+ EXPECT_FALSE(document_blocker_->allow_based_on_sniffing_);
+ } else {
+ EXPECT_EQ(scenario.first_chunk, stream_sink_body_)
+ << "Response should have been delivered to the renderer.";
+ EXPECT_FALSE(document_blocker_->blocked_read_completed_);
+ if (scenario.verdict == Verdict::kAllowAfterSniffing)
+ EXPECT_TRUE(document_blocker_->allow_based_on_sniffing_);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ CrossSiteDocumentResourceHandlerTest,
+ ::testing::ValuesIn(kScenarios));
+
+} // namespace content
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
index 88663ce2ae2..b372f8a6f76 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
+++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc
@@ -44,6 +44,7 @@
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/navigation_request_info.h"
#include "content/browser/loader/async_resource_handler.h"
+#include "content/browser/loader/cross_site_document_resource_handler.h"
#include "content/browser/loader/detachable_resource_handler.h"
#include "content/browser/loader/intercepting_resource_handler.h"
#include "content/browser/loader/loader_delegate.h"
@@ -1489,7 +1490,7 @@ ResourceDispatcherHostImpl::CreateResourceHandler(
}
return AddStandardHandlers(request, request_data.resource_type,
- resource_context,
+ resource_context, request_data.fetch_request_mode,
request_data.fetch_request_context_type,
request_data.fetch_mixed_content_context_type,
requester_info->appcache_service(), child_id,
@@ -1518,6 +1519,7 @@ ResourceDispatcherHostImpl::AddStandardHandlers(
net::URLRequest* request,
ResourceType resource_type,
ResourceContext* resource_context,
+ FetchRequestMode fetch_request_mode,
RequestContextType fetch_request_context_type,
blink::WebMixedContentContextType fetch_mixed_content_context_type,
AppCacheService* appcache_service,
@@ -1616,6 +1618,17 @@ ResourceDispatcherHostImpl::AddStandardHandlers(
handler.reset(new ThrottlingResourceHandler(
std::move(handler), request, std::move(pre_mime_sniffing_throttles)));
+ if (!IsResourceTypeFrame(resource_type)) {
+ // Add a handler to block cross-site documents from the renderer process.
+ // This should be pre mime-sniffing, since it affects whether the response
+ // will be read, and since it looks at the original mime type.
+ bool is_nocors_plugin_request =
+ resource_type == RESOURCE_TYPE_PLUGIN_RESOURCE &&
+ fetch_request_mode == FETCH_REQUEST_MODE_NO_CORS;
+ handler.reset(new CrossSiteDocumentResourceHandler(
+ std::move(handler), request, is_nocors_plugin_request));
+ }
+
return handler;
}
@@ -2188,11 +2201,12 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest(
->stream()
->CreateHandle();
+ // Safe to consider navigations as NO_CORS.
// TODO(davidben): Fix the dependency on child_id/route_id. Those are used
// by the ResourceScheduler. currently it's a no-op.
handler = AddStandardHandlers(
new_request.get(), resource_type, resource_context,
- info.begin_params.request_context_type,
+ FETCH_REQUEST_MODE_NO_CORS, info.begin_params.request_context_type,
info.begin_params.mixed_content_context_type,
appcache_handle_core ? appcache_handle_core->GetAppCacheService()
: nullptr,
diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h
index 3ef287d8739..2fb715de446 100644
--- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h
+++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h
@@ -638,6 +638,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl
net::URLRequest* request,
ResourceType resource_type,
ResourceContext* resource_context,
+ FetchRequestMode fetch_request_mode,
RequestContextType fetch_request_context_type,
blink::WebMixedContentContextType fetch_mixed_content_context_type,
AppCacheService* appcache_service,
diff --git a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc
index 7d58b15a607..fcdfcb865df 100644
--- a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc
+++ b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc
@@ -162,8 +162,8 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), kRoutingId, kRequestId,
mojom::kURLLoadOptionNone, request, client.CreateInterfacePtr(),
@@ -239,8 +239,8 @@ TEST_P(URLLoaderFactoryImplTest, GetFailedResponse) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 2, 1, mojom::kURLLoadOptionNone, request,
client.CreateInterfacePtr(),
@@ -269,8 +269,8 @@ TEST_P(URLLoaderFactoryImplTest, GetFailedResponse2) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 2, 1, mojom::kURLLoadOptionNone, request,
client.CreateInterfacePtr(),
@@ -296,8 +296,8 @@ TEST_P(URLLoaderFactoryImplTest, InvalidURL) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
ASSERT_FALSE(request.url.is_valid());
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 2, 1, mojom::kURLLoadOptionNone, request,
@@ -324,8 +324,8 @@ TEST_P(URLLoaderFactoryImplTest, ShouldNotRequestURL) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), 2, 1, mojom::kURLLoadOptionNone, request,
client.CreateInterfacePtr(),
@@ -355,7 +355,7 @@ TEST_P(URLLoaderFactoryImplTest, DownloadToFile) {
request.method = "GET";
request.resource_type = RESOURCE_TYPE_XHR;
request.download_to_file = true;
- request.request_initiator = url::Origin();
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), kRoutingId, kRequestId, 0, request,
client.CreateInterfacePtr(),
@@ -423,7 +423,7 @@ TEST_P(URLLoaderFactoryImplTest, DownloadToFileFailure) {
request.method = "GET";
request.resource_type = RESOURCE_TYPE_XHR;
request.download_to_file = true;
- request.request_initiator = url::Origin();
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), kRoutingId, kRequestId, 0, request,
client.CreateInterfacePtr(),
@@ -484,8 +484,8 @@ TEST_P(URLLoaderFactoryImplTest, OnTransferSizeUpdated) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
request.report_raw_headers = true;
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), kRoutingId, kRequestId,
@@ -546,8 +546,8 @@ TEST_P(URLLoaderFactoryImplTest, CancelFromRenderer) {
// enabled, the url scheme of frame type requests from the renderer process
// must be blob scheme.
request.resource_type = RESOURCE_TYPE_XHR;
- // Need to set |request_initiator| for non main frame type request.
- request.request_initiator = url::Origin();
+ // Need to set same-site |request_initiator| for non main frame type request.
+ request.request_initiator = url::Origin::Create(request.url);
factory_->CreateLoaderAndStart(
mojo::MakeRequest(&loader), kRoutingId, kRequestId,
mojom::kURLLoadOptionNone, request, client.CreateInterfacePtr(),
diff --git a/chromium/content/browser/web_contents/web_contents_view_android.cc b/chromium/content/browser/web_contents/web_contents_view_android.cc
index 5e0a0aeac2e..95f5ad6734d 100644
--- a/chromium/content/browser/web_contents/web_contents_view_android.cc
+++ b/chromium/content/browser/web_contents/web_contents_view_android.cc
@@ -196,7 +196,7 @@ void WebContentsViewAndroid::Focus() {
RenderWidgetHostViewAndroid* rwhv = GetRenderWidgetHostViewAndroid();
if (web_contents_->ShowingInterstitialPage()) {
web_contents_->GetInterstitialPage()->Focus();
- } else {
+ } else if (rwhv) {
rwhv->Focus();
}
}
diff --git a/chromium/content/child/site_isolation_stats_gatherer_browsertest.cc b/chromium/content/child/site_isolation_stats_gatherer_browsertest.cc
index 83be84591d6..5df256242ee 100644
--- a/chromium/content/child/site_isolation_stats_gatherer_browsertest.cc
+++ b/chromium/content/child/site_isolation_stats_gatherer_browsertest.cc
@@ -13,6 +13,7 @@
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -107,6 +108,10 @@ class SiteIsolationStatsGathererBrowserTest
expected_metrics[base + ".NotBlocked.MaybeJS"] = 1;
}
}
+ // This metric from the browser process also records start and stop actions
+ // for the blocking logic, even though blocking is disabled in the browser
+ // process when this test runs.
+ expected_metrics["SiteIsolation.XSD.Browser.Action"] = 2;
// Make sure that the expected metrics, and only those metrics, were
// incremented.
@@ -133,6 +138,12 @@ class SiteIsolationStatsGathererBrowserTest
IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
CrossSiteDocumentBlockingForMimeType) {
+ // This test is disabled in --site-per-process, since the documents are
+ // blocked before arriving in the renderer process and thus the existing
+ // histograms do not work.
+ if (AreAllSitesIsolatedForTesting())
+ return;
+
// Load a page that issues illegal cross-site document requests to bar.com.
// The page uses XHR to request HTML/XML/JSON documents from bar.com, and
// inspects if any of them were successfully received. Currently, on illegal
@@ -141,7 +152,7 @@ IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
// we run the browser without the same origin policy.
GURL foo("http://foo.com/cross_site_document_request.html");
- NavigateToURL(shell(), foo);
+ EXPECT_TRUE(NavigateToURL(shell(), foo));
// Flush out existing histogram activity.
FetchHistogramsFromChildProcesses();
@@ -149,18 +160,9 @@ IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
// The following are files under content/test/data/site_isolation. All
// should be disallowed for cross site XHR under the document blocking policy.
const char* blocked_resources[] = {
- "comment_valid.html",
- "html.txt",
- "html4_dtd.html",
- "html4_dtd.txt",
- "html5_dtd.html",
- "html5_dtd.txt",
- "json.txt",
- "valid.html",
- "valid.json",
- "valid.xml",
- "xml.txt",
- };
+ "comment_valid.html", "html.txt", "html4_dtd.html", "html4_dtd.txt",
+ "html5_dtd.html", "html5_dtd.txt", "json.txt", "valid.html",
+ "valid.json", "valid.xml", "xml.txt"};
for (const char* resource : blocked_resources) {
SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
@@ -168,7 +170,7 @@ IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
bool was_blocked;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
- shell(), base::StringPrintf("sendRequest(\"%s\");", resource),
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
&was_blocked));
ASSERT_FALSE(was_blocked);
@@ -176,23 +178,16 @@ IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
}
// These files should be allowed for XHR under the document blocking policy.
- const char* allowed_resources[] = {"js.html",
- "comment_js.html",
- "js.xml",
- "js.json",
- "js.txt",
- "img.html",
- "img.xml",
- "img.json",
- "img.txt",
- "comment_js.html"};
+ const char* allowed_resources[] = {"js.html", "comment_js.html", "js.xml",
+ "js.json", "js.txt", "img.html",
+ "img.xml", "img.json", "img.txt"};
for (const char* resource : allowed_resources) {
SCOPED_TRACE(base::StringPrintf("... while testing page: %s", resource));
base::HistogramTester histograms;
bool was_blocked;
ASSERT_TRUE(ExecuteScriptAndExtractBool(
- shell(), base::StringPrintf("sendRequest(\"%s\");", resource),
+ shell(), base::StringPrintf("sendRequest('%s');", resource),
&was_blocked));
ASSERT_FALSE(was_blocked);
@@ -212,7 +207,7 @@ IN_PROC_BROWSER_TEST_P(SiteIsolationStatsGathererBrowserTest,
// TODO(nick): Split up these cases, and add positive assertions here about
// what actually happens in these various resource-block cases.
GURL foo("http://foo.com/cross_site_document_request_target.html");
- NavigateToURL(shell(), foo);
+ EXPECT_TRUE(NavigateToURL(shell(), foo));
}
INSTANTIATE_TEST_CASE_P(SiteIsolationStatsGathererBrowserTest,
diff --git a/chromium/content/common/cross_site_document_classifier.cc b/chromium/content/common/cross_site_document_classifier.cc
index 78417aa2b83..3518d84f2c3 100644
--- a/chromium/content/common/cross_site_document_classifier.cc
+++ b/chromium/content/common/cross_site_document_classifier.cc
@@ -5,6 +5,7 @@
#include "content/common/cross_site_document_classifier.h"
#include <stddef.h>
+#include <string>
#include "base/command_line.h"
#include "base/lazy_instance.h"
@@ -111,12 +112,16 @@ bool CrossSiteDocumentClassifier::IsValidCorsHeaderSet(
// non-standard practice, and not supported by Chrome. Refer to
// CrossOriginAccessControl::passesAccessControlCheck().
+ // Note that "null" offers no more protection than "*" because it matches any
+ // unique origin, such as data URLs. Any origin can thus access it, so don't
+ // bother trying to block this case.
+
// TODO(dsjang): * is not allowed for the response from a request
// with cookies. This allows for more than what the renderer will
// eventually be able to receive, so we won't see illegal cross-site
// documents allowed by this. We have to find a way to see if this
// response is from a cookie-tagged request or not in the future.
- if (access_control_origin == "*")
+ if (access_control_origin == "*" || access_control_origin == "null")
return true;
// TODO(dsjang): The CORS spec only treats a fully specified URL, except for
diff --git a/chromium/content/common/site_isolation_policy.cc b/chromium/content/common/site_isolation_policy.cc
index 0d277b01a89..16035a81660 100644
--- a/chromium/content/common/site_isolation_policy.cc
+++ b/chromium/content/common/site_isolation_policy.cc
@@ -9,6 +9,7 @@
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
#include "base/strings/string_split.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
@@ -24,6 +25,21 @@ bool SiteIsolationPolicy::UseDedicatedProcessesForAllSites() {
}
// static
+SiteIsolationPolicy::CrossSiteDocumentBlockingEnabledState
+SiteIsolationPolicy::IsCrossSiteDocumentBlockingEnabled() {
+ if (base::FeatureList::IsEnabled(
+ ::features::kCrossSiteDocumentBlockingAlways))
+ return XSDB_ENABLED_UNCONDITIONALLY;
+
+ if (base::FeatureList::IsEnabled(
+ ::features::kCrossSiteDocumentBlockingIfIsolating)) {
+ return XSDB_ENABLED_IF_ISOLATED;
+ }
+
+ return XSDB_DISABLED;
+}
+
+// static
bool SiteIsolationPolicy::IsTopDocumentIsolationEnabled() {
// --site-per-process trumps --top-document-isolation.
if (UseDedicatedProcessesForAllSites())
@@ -44,8 +60,13 @@ std::vector<url::Origin> SiteIsolationPolicy::GetIsolatedOrigins() {
std::string cmdline_arg =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kIsolateOrigins);
- if (!cmdline_arg.empty())
- return ParseIsolatedOrigins(cmdline_arg);
+ if (!cmdline_arg.empty()) {
+ std::vector<url::Origin> cmdline_origins =
+ ParseIsolatedOrigins(cmdline_arg);
+ UMA_HISTOGRAM_COUNTS_1000("SiteIsolation.IsolateOrigins.Size",
+ cmdline_origins.size());
+ return cmdline_origins;
+ }
if (base::FeatureList::IsEnabled(features::kIsolateOrigins)) {
std::string field_trial_arg = base::GetFieldTrialParamValueByFeature(
@@ -73,4 +94,20 @@ std::vector<url::Origin> SiteIsolationPolicy::ParseIsolatedOrigins(
return origins;
}
+// static
+void SiteIsolationPolicy::RecordSiteIsolationFlagUsage() {
+ // For --site-per-process and --isolate-origins, include flags specified on
+ // command-line, in chrome://flags, and via enterprise policy (i.e., include
+ // switches::kSitePerProcess and switches::kIsolateOrigins). Exclude these
+ // modes being set through field trials (i.e., exclude
+ // features::kSitePerProcess and features::IsolateOrigins).
+ UMA_HISTOGRAM_BOOLEAN("SiteIsolation.Flags.IsolateOrigins",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kIsolateOrigins));
+
+ UMA_HISTOGRAM_BOOLEAN("SiteIsolation.Flags.SitePerProcess",
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kSitePerProcess));
+}
+
} // namespace content
diff --git a/chromium/content/common/site_isolation_policy.h b/chromium/content/common/site_isolation_policy.h
index 44afe7d5c6e..eb4b799d0d3 100644
--- a/chromium/content/common/site_isolation_policy.h
+++ b/chromium/content/common/site_isolation_policy.h
@@ -27,6 +27,15 @@ class CONTENT_EXPORT SiteIsolationPolicy {
// Returns true if every site should be placed in a dedicated process.
static bool UseDedicatedProcessesForAllSites();
+ // Returns whether cross-site document responses can be blocked.
+ enum CrossSiteDocumentBlockingEnabledState {
+ XSDB_ENABLED_UNCONDITIONALLY,
+ XSDB_ENABLED_IF_ISOLATED,
+ XSDB_DISABLED,
+ };
+ static CrossSiteDocumentBlockingEnabledState
+ IsCrossSiteDocumentBlockingEnabled();
+
// Returns true if third-party subframes of a page should be kept in a
// different process from the main frame.
static bool IsTopDocumentIsolationEnabled();
@@ -40,6 +49,10 @@ class CONTENT_EXPORT SiteIsolationPolicy {
// ContentBrowserClient::GetOriginsRequiringDedicatedProcess.
static std::vector<url::Origin> GetIsolatedOrigins();
+ // Records metrics about which site isolation command-line flags are present.
+ // This should be called once on browser startup.
+ static void RecordSiteIsolationFlagUsage();
+
private:
SiteIsolationPolicy(); // Not instantiable.
diff --git a/chromium/content/public/browser/content_browser_client.cc b/chromium/content/public/browser/content_browser_client.cc
index 6a264c4692f..70cc5e2e8de 100644
--- a/chromium/content/public/browser/content_browser_client.cc
+++ b/chromium/content/public/browser/content_browser_client.cc
@@ -78,6 +78,13 @@ bool ContentBrowserClient::ShouldLockToOrigin(BrowserContext* browser_context,
return true;
}
+bool ContentBrowserClient::ShouldBypassDocumentBlocking(
+ const url::Origin& initiator,
+ const GURL& url,
+ ResourceType resource_type) {
+ return false;
+}
+
void ContentBrowserClient::GetAdditionalViewSourceSchemes(
std::vector<std::string>* additional_schemes) {
GetAdditionalWebUISchemes(additional_schemes);
diff --git a/chromium/content/public/browser/content_browser_client.h b/chromium/content/public/browser/content_browser_client.h
index 711fa80d0ea..d9af3e7a6af 100644
--- a/chromium/content/public/browser/content_browser_client.h
+++ b/chromium/content/public/browser/content_browser_client.h
@@ -243,6 +243,13 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual bool ShouldLockToOrigin(BrowserContext* browser_context,
const GURL& effective_url);
+ // Returns true if the |initiator| origin should be allowed to receive a
+ // document at |url|, bypassing the usual blocking logic. Defaults to false.
+ // This is called on the IO thread.
+ virtual bool ShouldBypassDocumentBlocking(const url::Origin& initiator,
+ const GURL& url,
+ ResourceType resource_type);
+
// Returns a list additional WebUI schemes, if any. These additional schemes
// act as aliases to the chrome: scheme. The additional schemes may or may
// not serve specific WebUI pages depending on the particular URLDataSource
diff --git a/chromium/content/public/common/content_features.cc b/chromium/content/public/common/content_features.cc
index 2b88e0c7a99..f11d51a320c 100644
--- a/chromium/content/public/common/content_features.cc
+++ b/chromium/content/public/common/content_features.cc
@@ -90,6 +90,16 @@ const base::Feature kCompositorImageAnimation{
const base::Feature kCompositorTouchAction{"CompositorTouchAction",
base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables blocking cross-site document responses (not paying attention to
+// whether a site isolation mode is also enabled).
+const base::Feature kCrossSiteDocumentBlockingAlways{
+ "CrossSiteDocumentBlockingAlways", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables blocking cross-site document responses if one of site isolation modes
+// is (e.g. site-per-process or isolate-origins) is enabled.
+const base::Feature kCrossSiteDocumentBlockingIfIsolating{
+ "CrossSiteDocumentBlockingIfIsolating", base::FEATURE_ENABLED_BY_DEFAULT};
+
// Throttle tasks in Blink background timer queues based on CPU budgets
// for the background tab. Bug: https://crbug.com/639852.
const base::Feature kExpensiveBackgroundTimerThrottling{
diff --git a/chromium/content/public/common/content_features.h b/chromium/content/public/common/content_features.h
index 470b6607c3a..a72f19f4a06 100644
--- a/chromium/content/public/common/content_features.h
+++ b/chromium/content/public/common/content_features.h
@@ -32,6 +32,8 @@ CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
CONTENT_EXPORT extern const base::Feature kCompositorImageAnimation;
CONTENT_EXPORT extern const base::Feature kCompositorTouchAction;
+CONTENT_EXPORT extern const base::Feature kCrossSiteDocumentBlockingAlways;
+CONTENT_EXPORT extern const base::Feature kCrossSiteDocumentBlockingIfIsolating;
CONTENT_EXPORT extern const base::Feature kExpensiveBackgroundTimerThrottling;
CONTENT_EXPORT extern const base::Feature kFeaturePolicy;
CONTENT_EXPORT extern const base::Feature kFetchKeepaliveTimeoutSetting;
diff --git a/chromium/content/renderer/fetchers/resource_fetcher_browsertest.cc b/chromium/content/renderer/fetchers/resource_fetcher_browsertest.cc
index 689e5d3aa6a..2ff25adb0a8 100644
--- a/chromium/content/renderer/fetchers/resource_fetcher_browsertest.cc
+++ b/chromium/content/renderer/fetchers/resource_fetcher_browsertest.cc
@@ -318,10 +318,10 @@ class ResourceFetcherTests : public ContentBrowserTest {
// Test a fetch from the test server.
// If this flakes, use http://crbug.com/51622.
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDownload) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
-
+ // Need to spin up the renderer to same-site URL.
ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
+
GURL url(embedded_test_server()->GetURL("/simple_page.html"));
PostTaskToInProcessRendererAndWait(
@@ -331,10 +331,10 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDownload) {
// Test if ResourceFetcher can handle server redirects correctly.
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherRedirect) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
-
+ // Need to spin up the renderer to same-site URL.
ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
+
GURL final_url(embedded_test_server()->GetURL("/simple_page.html"));
GURL url(
embedded_test_server()->GetURL("/server-redirect?" + final_url.spec()));
@@ -345,11 +345,11 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherRedirect) {
}
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcher404) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+ // Need to spin up the renderer to same-site URL.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Test 404 response.
- ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/thisfiledoesntexist.html");
PostTaskToInProcessRendererAndWait(
@@ -368,12 +368,12 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
}
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+ // Need to spin up the renderer to same-site URL.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Grab a page that takes at least 1 sec to respond, but set the fetcher to
// timeout in 0 sec.
- ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/slow?1"));
PostTaskToInProcessRendererAndWait(
@@ -382,12 +382,12 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherTimeout) {
}
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+ // Need to spin up the renderer to same-site URL.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Grab a page that takes at least 1 sec to respond, but set the fetcher to
// timeout in 0 sec.
- ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/slow?1"));
PostTaskToInProcessRendererAndWait(base::Bind(
@@ -397,11 +397,11 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDeletedInCallback) {
// Test that ResourceFetchers can handle POSTs.
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherPost) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+ // Need to spin up the renderer to same-site URL.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Grab a page that echos the POST body.
- ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/echo"));
PostTaskToInProcessRendererAndWait(base::Bind(
@@ -410,11 +410,11 @@ IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherPost) {
// Test that ResourceFetchers can set headers.
IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherSetHeader) {
- // Need to spin up the renderer.
- NavigateToURL(shell(), GURL(url::kAboutBlankURL));
+ // Need to spin up the renderer to same-site URL.
+ ASSERT_TRUE(embedded_test_server()->Start());
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"));
// Grab a page that echos the POST body.
- ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/echoheader?header"));
PostTaskToInProcessRendererAndWait(
diff --git a/chromium/content/test/BUILD.gn b/chromium/content/test/BUILD.gn
index 2b12a2416aa..9966b7a9c6c 100644
--- a/chromium/content/test/BUILD.gn
+++ b/chromium/content/test/BUILD.gn
@@ -705,6 +705,7 @@ test("content_browsertests") {
"../browser/indexed_db/mock_browsertest_indexed_db_class_factory.h",
"../browser/isolated_origin_browsertest.cc",
"../browser/loader/async_resource_handler_browsertest.cc",
+ "../browser/loader/cross_site_document_blocking_browsertest.cc",
"../browser/loader/cross_site_resource_handler_browsertest.cc",
"../browser/loader/reload_cache_control_browsertest.cc",
"../browser/loader/resource_dispatcher_host_browsertest.cc",
@@ -1259,6 +1260,7 @@ test("content_unittests") {
"../browser/indexed_db/mock_mojo_indexed_db_database_callbacks.h",
"../browser/leveldb_wrapper_impl_unittest.cc",
"../browser/loader/async_resource_handler_unittest.cc",
+ "../browser/loader/cross_site_document_resource_handler_unittest.cc",
"../browser/loader/detachable_resource_handler_unittest.cc",
"../browser/loader/intercepting_resource_handler_unittest.cc",
"../browser/loader/mime_sniffing_resource_handler_unittest.cc",
diff --git a/chromium/extensions/common/api/_permission_features.json b/chromium/extensions/common/api/_permission_features.json
index ccdddbeac47..79d3f54c578 100644
--- a/chromium/extensions/common/api/_permission_features.json
+++ b/chromium/extensions/common/api/_permission_features.json
@@ -445,7 +445,8 @@
"E0E94FB0C01FFB9CDC7A5F098C99B5A8D2F95902", // http://crbug.com/610452
"52E0557059A7A28F74ED1D92DDD997E0CCD37806", // http://crbug.com/610452
"61FF4757F9420B62B19BA5C96084649339DB31F5", // http://crbug.com/731941
- "6FB7E1B6C0247B687AC14772E87A117F5F5E4497" // http://crbug.com/731941
+ "6FB7E1B6C0247B687AC14772E87A117F5F5E4497", // http://crbug.com/731941
+ "9834387FDA1F66A1B5CA06CB442137B556F12F2A" // http://crbug.com/772346
]
}],
"networkingPrivate": {
diff --git a/chromium/net/dns/dns_transaction.cc b/chromium/net/dns/dns_transaction.cc
index 9c0e0d8723c..b41deb4f883 100644
--- a/chromium/net/dns/dns_transaction.cc
+++ b/chromium/net/dns/dns_transaction.cc
@@ -767,8 +767,15 @@ class DnsTransactionImpl : public DnsTransaction,
previous_attempt->GetQuery()->CloneWithNewId(id);
RecordLostPacketsIfAny();
- // Cancel all other attempts, no point waiting on them.
- attempts_.clear();
+
+ // Cancel all other attempts that have not received a response, no point
+ // waiting on them.
+ for (auto it = attempts_.begin(); it != attempts_.end();) {
+ if (!(*it)->is_completed())
+ it = attempts_.erase(it);
+ else
+ ++it;
+ }
unsigned attempt_number = attempts_.size();
diff --git a/chromium/net/dns/dns_transaction_unittest.cc b/chromium/net/dns/dns_transaction_unittest.cc
index 53328d7cddc..4a13d08aee9 100644
--- a/chromium/net/dns/dns_transaction_unittest.cc
+++ b/chromium/net/dns/dns_transaction_unittest.cc
@@ -1055,6 +1055,54 @@ TEST_F(DnsTransactionTest, TCPConnectionClosedSynchronous) {
EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
}
+TEST_F(DnsTransactionTest, MismatchedThenNxdomainThenTCP) {
+ config_.attempts = 2;
+ config_.timeout = TestTimeouts::tiny_timeout();
+ ConfigureFactory();
+ std::unique_ptr<DnsSocketData> data(
+ new DnsSocketData(0 /* id */, kT0HostName, kT0Qtype, SYNCHRONOUS, false));
+ // First attempt gets a mismatched response.
+ data->AddResponseData(kT1ResponseDatagram, arraysize(kT1ResponseDatagram),
+ SYNCHRONOUS);
+ // Second read from first attempt gets TCP required.
+ data->AddRcode(dns_protocol::kFlagTC, ASYNC);
+ AddSocketData(std::move(data));
+ // Second attempt gets NXDOMAIN, which happens before the TCP required.
+ AddSyncQueryAndRcode(kT0HostName, kT0Qtype, dns_protocol::kRcodeNXDOMAIN);
+ std::unique_ptr<DnsSocketData> tcp_data(
+ new DnsSocketData(0 /* id */, kT0HostName, kT0Qtype, ASYNC, true));
+ tcp_data->AddReadError(ERR_CONNECTION_CLOSED, SYNCHRONOUS);
+ AddSocketData(std::move(tcp_data));
+
+ TransactionHelper helper0(kT0HostName, kT0Qtype, ERR_NAME_NOT_RESOLVED);
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
+}
+
+TEST_F(DnsTransactionTest, MismatchedThenOkThenTCP) {
+ config_.attempts = 2;
+ config_.timeout = TestTimeouts::tiny_timeout();
+ ConfigureFactory();
+ std::unique_ptr<DnsSocketData> data(
+ new DnsSocketData(0 /* id */, kT0HostName, kT0Qtype, SYNCHRONOUS, false));
+ // First attempt gets a mismatched response.
+ data->AddResponseData(kT1ResponseDatagram, arraysize(kT1ResponseDatagram),
+ SYNCHRONOUS);
+ // Second read from first attempt gets TCP required.
+ data->AddRcode(dns_protocol::kFlagTC, ASYNC);
+ AddSocketData(std::move(data));
+ // Second attempt gets a valid response, which happens before the TCP
+ // required.
+ AddSyncQueryAndResponse(0 /* id */, kT0HostName, kT0Qtype,
+ kT0ResponseDatagram, arraysize(kT0ResponseDatagram));
+ std::unique_ptr<DnsSocketData> tcp_data(
+ new DnsSocketData(0 /* id */, kT0HostName, kT0Qtype, ASYNC, true));
+ tcp_data->AddReadError(ERR_CONNECTION_CLOSED, SYNCHRONOUS);
+ AddSocketData(std::move(tcp_data));
+
+ TransactionHelper helper0(kT0HostName, kT0Qtype, kT0RecordCount);
+ EXPECT_TRUE(helper0.Run(transaction_factory_.get()));
+}
+
TEST_F(DnsTransactionTest, InvalidQuery) {
config_.timeout = TestTimeouts::tiny_timeout();
ConfigureFactory();
diff --git a/chromium/skia/ext/skia_commit_hash.h b/chromium/skia/ext/skia_commit_hash.h
index 064a3695dba..f5a7a58f634 100644
--- a/chromium/skia/ext/skia_commit_hash.h
+++ b/chromium/skia/ext/skia_commit_hash.h
@@ -3,6 +3,6 @@
#ifndef SKIA_EXT_SKIA_COMMIT_HASH_H_
#define SKIA_EXT_SKIA_COMMIT_HASH_H_
-#define SKIA_COMMIT_HASH "e376a767ac05c5e37627f950c86ff5c3f6e1cd4c-"
+#define SKIA_COMMIT_HASH "1a190d213cb8f711a15d3dc489fd068cd43291bd-"
#endif // SKIA_EXT_SKIA_COMMIT_HASH_H_
diff --git a/chromium/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp b/chromium/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp
index 2e34d39e1cb..69305ed3e77 100644
--- a/chromium/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp
+++ b/chromium/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp
@@ -129,7 +129,7 @@ void CompileFromResponseCallback(
"WebAssembly", "compile");
ExceptionToRejectPromiseScope reject_promise_scope(args, exception_state);
- ScriptState* script_state = ScriptState::ForRelevantRealm(args);
+ ScriptState* script_state = ScriptState::ForCurrentRealm(args);
if (!ExecutionContext::From(script_state)) {
V8SetReturnValue(args, ScriptPromise().V8Value());
return;
@@ -142,7 +142,7 @@ void CompileFromResponseCallback(
ScriptPromise::Reject(
script_state, V8ThrowException::CreateTypeError(
script_state->GetIsolate(),
- "An argument must be provided, which must be a"
+ "An argument must be provided, which must be a "
"Response or Promise<Response> object"))
.V8Value());
return;
@@ -189,7 +189,7 @@ void CompileFromResponseCallback(
// See https://crbug.com/708238 for tracking avoiding the hand-generated code.
void WasmCompileStreamingImpl(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
- ScriptState* script_state = ScriptState::ForRelevantRealm(args);
+ ScriptState* script_state = ScriptState::ForCurrentRealm(args);
v8::Local<v8::Function> compile_callback =
v8::Function::New(isolate, CompileFromResponseCallback);
diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp b/chromium/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
index a071a1b217a..47384ecd580 100644
--- a/chromium/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
+++ b/chromium/third_party/WebKit/Source/core/html/HTMLImageFallbackHelper.cpp
@@ -23,8 +23,9 @@ namespace blink {
using namespace HTMLNames;
static bool NoImageSourceSpecified(const Element& element) {
- bool no_src_specified =
- !element.hasAttribute(srcAttr) || element.getAttribute(srcAttr).IsNull();
+ bool no_src_specified = !element.hasAttribute(srcAttr) ||
+ element.getAttribute(srcAttr).IsNull() ||
+ element.getAttribute(srcAttr).IsEmpty();
bool no_srcset_specified = !element.hasAttribute(srcsetAttr) ||
element.getAttribute(srcsetAttr).IsNull();
return no_src_specified && no_srcset_specified;
diff --git a/chromium/third_party/WebKit/Source/modules/accessibility/AXARIAGrid.cpp b/chromium/third_party/WebKit/Source/modules/accessibility/AXARIAGrid.cpp
index 2bef0f90415..8e8ca06a495 100644
--- a/chromium/third_party/WebKit/Source/modules/accessibility/AXARIAGrid.cpp
+++ b/chromium/third_party/WebKit/Source/modules/accessibility/AXARIAGrid.cpp
@@ -46,10 +46,13 @@ AXARIAGrid* AXARIAGrid::Create(LayoutObject* layout_object,
}
bool AXARIAGrid::AddRow(AXObject* possible_row) {
- if (!possible_row || possible_row->RoleValue() != kRowRole)
+ // This does not yet handle the case where the row is not an AXARIAGridRow or
+ // AXTable row because it is in a canvas or is a virtual node, as those
+ // do not have a layout object, cannot be an AXARIAGridRow, and cannot
+ // currently implement the rest of our table logic.
+ if (!possible_row || !possible_row->IsTableRow())
return false;
- DCHECK(possible_row->IsTableRow());
AXTableRow* row = ToAXTableRow(possible_row);
row->SetRowIndex(static_cast<int>(rows_.size()));
rows_.push_back(possible_row);
diff --git a/chromium/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp b/chromium/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
index 46e3cf837d8..74bd9b0436e 100644
--- a/chromium/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
+++ b/chromium/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp
@@ -605,6 +605,20 @@ ResourceFetcher::PrepareRequestResult ResourceFetcher::PrepareRequest(
if (blocked_reason != ResourceRequestBlockedReason::kNone)
return kBlock;
+ const RefPtr<SecurityOrigin>& origin = options.security_origin;
+ if (origin && !origin->IsUnique() &&
+ !origin->IsSameSchemeHostPort(Context().GetSecurityOrigin())) {
+ // |options.security_origin| may differ from the document's origin if
+ // this is a fetch initiated by an isolated world execution context, with a
+ // different SecurityOrigin. In this case, plumb it through as the
+ // RequestorOrigin so that the browser process can make policy decisions for
+ // this request, based on any special permissions the isolated world may
+ // have been granted.
+ // TODO(nick, dcheng): Find a way to formalize the isolated world origin
+ // check in https://crbug.com/792154.
+ resource_request.SetRequestorOrigin(origin);
+ }
+
// For initial requests, call prepareRequest() here before revalidation
// policy is determined.
Context().PrepareRequest(resource_request,
@@ -613,7 +627,6 @@ ResourceFetcher::PrepareRequestResult ResourceFetcher::PrepareRequest(
if (!params.Url().IsValid())
return kAbort;
- RefPtr<SecurityOrigin> origin = options.security_origin;
params.MutableOptions().cors_flag =
!origin || !origin->CanRequestNoSuborigin(params.Url());
diff --git a/chromium/third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/chromium/third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index 4c75172943f..bf10646d854 100644
--- a/chromium/third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/chromium/third_party/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -1917,6 +1917,9 @@ gl::Error StateManager11::updateState(const gl::Context *context, GLenum drawMod
auto dirtyBitsCopy = mInternalDirtyBits;
mInternalDirtyBits.reset();
+ // TODO(crbug.com/792966): Workaround for bug in this dirty bit
+ dirtyBitsCopy.set(DIRTY_BIT_PROGRAM_UNIFORM_BUFFERS);
+
for (auto dirtyBit : dirtyBitsCopy)
{
switch (dirtyBit)
diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc
index bb806e183fe..b0fef6f1589 100644
--- a/chromium/ui/compositor/compositor.cc
+++ b/chromium/ui/compositor/compositor.cc
@@ -355,7 +355,11 @@ void Compositor::SetScaleAndSize(float scale,
void Compositor::SetDisplayColorSpace(const gfx::ColorSpace& color_space) {
output_color_space_ = color_space;
blending_color_space_ = output_color_space_.GetBlendingColorSpace();
- host_->SetRasterColorSpace(output_color_space_.GetRasterColorSpace());
+ // Do all ui::Compositor rasterization to sRGB because UI resources will not
+ // have their color conversion results cached, and will suffer repeated
+ // image color conversions.
+ // https://crbug.com/769677
+ host_->SetRasterColorSpace(gfx::ColorSpace::CreateSRGB());
// Color space is reset when the output surface is lost, so this must also be
// updated then.
// TODO(fsamuel): Get rid of this.
diff --git a/chromium/v8/include/v8-version.h b/chromium/v8/include/v8-version.h
index 46bb92f6506..86c50fd4f52 100644
--- a/chromium/v8/include/v8-version.h
+++ b/chromium/v8/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 6
#define V8_MINOR_VERSION 3
#define V8_BUILD_NUMBER 292
-#define V8_PATCH_LEVEL 46
+#define V8_PATCH_LEVEL 48
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/chromium/v8/src/builtins/builtins-typedarray-gen.cc b/chromium/v8/src/builtins/builtins-typedarray-gen.cc
index 07f122b9098..86ec0e7bd9a 100644
--- a/chromium/v8/src/builtins/builtins-typedarray-gen.cc
+++ b/chromium/v8/src/builtins/builtins-typedarray-gen.cc
@@ -799,7 +799,7 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(
// means we're safe from overflows in the following multiplication.
TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size);
CSA_ASSERT(this,
- IntPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
+ UintPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0)));
Label call_memmove(this), fast_c_call(this), out(this);
Branch(Word32Equal(source_el_kind, target_el_kind), &call_memmove,
@@ -821,8 +821,8 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(
TNode<IntPtrT> target_byte_length =
IntPtrMul(target_length, target_el_size);
- CSA_ASSERT(this,
- IntPtrGreaterThanOrEqual(target_byte_length, IntPtrConstant(0)));
+ CSA_ASSERT(
+ this, UintPtrGreaterThanOrEqual(target_byte_length, IntPtrConstant(0)));
TNode<IntPtrT> target_data_end_ptr =
IntPtrAdd(target_data_ptr, target_byte_length);
@@ -830,8 +830,8 @@ void TypedArrayBuiltinsAssembler::SetTypedArraySource(
IntPtrAdd(source_data_ptr, source_byte_length);
GotoIfNot(
- Word32Or(IntPtrLessThanOrEqual(target_data_end_ptr, source_data_ptr),
- IntPtrLessThanOrEqual(source_data_end_ptr, target_data_ptr)),
+ Word32Or(UintPtrLessThanOrEqual(target_data_end_ptr, source_data_ptr),
+ UintPtrLessThanOrEqual(source_data_end_ptr, target_data_ptr)),
call_runtime);
TNode<IntPtrT> source_length =
diff --git a/chromium/v8/src/debug/debug-coverage.cc b/chromium/v8/src/debug/debug-coverage.cc
index 8fe2edc08a7..8b87286d29a 100644
--- a/chromium/v8/src/debug/debug-coverage.cc
+++ b/chromium/v8/src/debug/debug-coverage.cc
@@ -544,9 +544,6 @@ void Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) {
if (!shared->IsSubjectToDebugging()) continue;
vector->clear_invocation_count();
vectors.emplace_back(vector, isolate);
- } else if (current_obj->IsJSFunction()) {
- JSFunction* function = JSFunction::cast(current_obj);
- function->set_code(function->shared()->code());
}
}
}