diff options
Diffstat (limited to 'chromium/chrome/browser/devtools')
22 files changed, 852 insertions, 98 deletions
diff --git a/chromium/chrome/browser/devtools/BUILD.gn b/chromium/chrome/browser/devtools/BUILD.gn index 36b76413f8e..e89c23aab2d 100644 --- a/chromium/chrome/browser/devtools/BUILD.gn +++ b/chromium/chrome/browser/devtools/BUILD.gn @@ -26,6 +26,8 @@ if (!is_android) { "protocol/page.h", "protocol/protocol.cc", "protocol/protocol.h", + "protocol/security.cc", + "protocol/security.h", "protocol/target.cc", "protocol/target.h", ] @@ -198,8 +200,10 @@ static_library("devtools") { sources += [ "devtools_dock_tile.cc" ] } if (!is_android) { - deps += [ ":protocol_generated_sources", - "//third_party/inspector_protocol:encoding" ] + deps += [ + ":protocol_generated_sources", + "//third_party/inspector_protocol:encoding", + ] sources += [ "protocol/browser_handler.cc", "protocol/browser_handler.h", @@ -207,6 +211,8 @@ static_library("devtools") { "protocol/cast_handler.h", "protocol/page_handler.cc", "protocol/page_handler.h", + "protocol/security_handler.cc", + "protocol/security_handler.h", "protocol/target_handler.cc", "protocol/target_handler.h", ] diff --git a/chromium/chrome/browser/devtools/OWNERS b/chromium/chrome/browser/devtools/OWNERS index 1329473478e..e1277e85c22 100644 --- a/chromium/chrome/browser/devtools/OWNERS +++ b/chromium/chrome/browser/devtools/OWNERS @@ -1,6 +1,7 @@ dgozman@chromium.org pfeldman@chromium.org caseq@chromium.org +yangguo@chromium.org per-file devtools_embedder_message_dispatcher.*=set noparent per-file devtools_embedder_message_dispatcher.*=file://ipc/SECURITY_OWNERS diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc index d34abfa43f3..3564def60ef 100644 --- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc +++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.cc @@ -227,9 +227,8 @@ ChromeDevToolsManagerDelegate::CreateNewTarget(const GURL& url) { } std::string ChromeDevToolsManagerDelegate::GetDiscoveryPageHTML() { - return ui::ResourceBundle::GetSharedInstance() - .GetRawDataResource(IDR_DEVTOOLS_DISCOVERY_PAGE_HTML) - .as_string(); + return ui::ResourceBundle::GetSharedInstance().DecompressDataResource( + IDR_DEVTOOLS_DISCOVERY_PAGE_HTML); } std::vector<content::BrowserContext*> diff --git a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h index 27a2dd29112..0e7bdc0eb74 100644 --- a/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h +++ b/chromium/chrome/browser/devtools/chrome_devtools_manager_delegate.h @@ -17,6 +17,7 @@ #include "chrome/browser/devtools/protocol/protocol.h" #include "content/public/browser/devtools_agent_host_observer.h" #include "content/public/browser/devtools_manager_delegate.h" +#include "net/base/host_port_pair.h" class ChromeDevToolsSession; using RemoteLocations = std::set<net::HostPortPair>; diff --git a/chromium/chrome/browser/devtools/chrome_devtools_session.cc b/chromium/chrome/browser/devtools/chrome_devtools_session.cc index 4b75e59539e..846cdec8776 100644 --- a/chromium/chrome/browser/devtools/chrome_devtools_session.cc +++ b/chromium/chrome/browser/devtools/chrome_devtools_session.cc @@ -9,6 +9,7 @@ #include "chrome/browser/devtools/protocol/browser_handler.h" #include "chrome/browser/devtools/protocol/cast_handler.h" #include "chrome/browser/devtools/protocol/page_handler.h" +#include "chrome/browser/devtools/protocol/security_handler.h" #include "chrome/browser/devtools/protocol/target_handler.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/devtools_agent_host_client.h" @@ -64,6 +65,8 @@ ChromeDevToolsSession::ChromeDevToolsSession( agent_host->GetType() == content::DevToolsAgentHost::kTypePage) { page_handler_ = std::make_unique<PageHandler>(agent_host->GetWebContents(), dispatcher_.get()); + security_handler_ = std::make_unique<SecurityHandler>( + agent_host->GetWebContents(), dispatcher_.get()); if (client->MayAttachToBrowser()) { cast_handler_ = std::make_unique<CastHandler>( agent_host->GetWebContents(), dispatcher_.get()); diff --git a/chromium/chrome/browser/devtools/chrome_devtools_session.h b/chromium/chrome/browser/devtools/chrome_devtools_session.h index 2665df6eb43..4a181761533 100644 --- a/chromium/chrome/browser/devtools/chrome_devtools_session.h +++ b/chromium/chrome/browser/devtools/chrome_devtools_session.h @@ -22,6 +22,7 @@ class DevToolsAgentHostClient; class BrowserHandler; class CastHandler; class PageHandler; +class SecurityHandler; class TargetHandler; class WindowManagerHandler; @@ -61,6 +62,7 @@ class ChromeDevToolsSession : public protocol::FrontendChannel { std::unique_ptr<BrowserHandler> browser_handler_; std::unique_ptr<CastHandler> cast_handler_; std::unique_ptr<PageHandler> page_handler_; + std::unique_ptr<SecurityHandler> security_handler_; std::unique_ptr<TargetHandler> target_handler_; #if defined(OS_CHROMEOS) std::unique_ptr<WindowManagerHandler> window_manager_protocl_handler_; diff --git a/chromium/chrome/browser/devtools/device/android_device_manager.cc b/chromium/chrome/browser/devtools/device/android_device_manager.cc index a59205407f5..4c4cf0b8584 100644 --- a/chromium/chrome/browser/devtools/device/android_device_manager.cc +++ b/chromium/chrome/browser/devtools/device/android_device_manager.cc @@ -18,6 +18,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/task/post_task.h" +#include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/devtools/device/usb/usb_device_manager_helper.h" #include "chrome/browser/devtools/device/usb/usb_device_provider.h" diff --git a/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc index ec895142198..f799123063b 100644 --- a/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc +++ b/chromium/chrome/browser/devtools/device/devtools_android_bridge.cc @@ -166,8 +166,8 @@ DevToolsAndroidBridge::DevToolsAndroidBridge(Profile* profile) base::Bind(&DevToolsAndroidBridge::CreateDeviceProviders, base::Unretained(this))); base::Value target_discovery(base::Value::Type::LIST); - target_discovery.GetList().emplace_back(kChromeDiscoveryURL); - target_discovery.GetList().emplace_back(kNodeDiscoveryURL); + target_discovery.Append(kChromeDiscoveryURL); + target_discovery.Append(kNodeDiscoveryURL); profile->GetPrefs()->SetDefaultPrefValue(prefs::kDevToolsTCPDiscoveryConfig, std::move(target_discovery)); CreateDeviceProviders(); diff --git a/chromium/chrome/browser/devtools/device/devtools_device_discovery.h b/chromium/chrome/browser/devtools/device/devtools_device_discovery.h index 7b97e2f90e5..a83ad1f91b4 100644 --- a/chromium/chrome/browser/devtools/device/devtools_device_discovery.h +++ b/chromium/chrome/browser/devtools/device/devtools_device_discovery.h @@ -13,6 +13,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "base/values.h" #include "chrome/browser/devtools/device/android_device_manager.h" #include "content/public/browser/devtools_agent_host.h" diff --git a/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc index 99fe24c6d13..2fbd2e47362 100644 --- a/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc +++ b/chromium/chrome/browser/devtools/device/port_forwarding_controller.cc @@ -28,7 +28,7 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/storage_partition.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "net/base/address_list.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" @@ -153,20 +153,18 @@ class PortForwardingHostResolver : public network::ResolveHostClientBase { const std::string& host, int port, ResolveHostCallback resolve_host_callback) - : binding_(this), - resolve_host_callback_(std::move(resolve_host_callback)) { + : resolve_host_callback_(std::move(resolve_host_callback)) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!binding_); + DCHECK(!receiver_.is_bound()); - network::mojom::ResolveHostClientPtr client_ptr; - binding_.Bind(mojo::MakeRequest(&client_ptr)); - binding_.set_connection_error_handler( - base::BindOnce(&PortForwardingHostResolver::OnComplete, - base::Unretained(this), net::ERR_FAILED, base::nullopt)); net::HostPortPair host_port_pair(host, port); content::BrowserContext::GetDefaultStoragePartition(profile) ->GetNetworkContext() - ->ResolveHost(host_port_pair, nullptr, std::move(client_ptr)); + ->ResolveHost(host_port_pair, nullptr, + receiver_.BindNewPipeAndPassRemote()); + receiver_.set_disconnect_handler( + base::BindOnce(&PortForwardingHostResolver::OnComplete, + base::Unretained(this), net::ERR_FAILED, base::nullopt)); } private: @@ -190,7 +188,7 @@ class PortForwardingHostResolver : public network::ResolveHostClientBase { delete this; } - mojo::Binding<network::mojom::ResolveHostClient> binding_; + mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this}; ResolveHostCallback resolve_host_callback_; DISALLOW_COPY_AND_ASSIGN(PortForwardingHostResolver); diff --git a/chromium/chrome/browser/devtools/device/tcp_device_provider.cc b/chromium/chrome/browser/devtools/device/tcp_device_provider.cc index 49f340aae90..394538ff6cc 100644 --- a/chromium/chrome/browser/devtools/device/tcp_device_provider.cc +++ b/chromium/chrome/browser/devtools/device/tcp_device_provider.cc @@ -18,7 +18,7 @@ #include "chrome/browser/devtools/device/adb/adb_client_socket.h" #include "chrome/browser/net/system_network_context_manager.h" #include "content/public/browser/browser_task_traits.h" -#include "mojo/public/cpp/bindings/binding.h" +#include "mojo/public/cpp/bindings/receiver.h" #include "net/base/completion_repeating_callback.h" #include "net/base/net_errors.h" #include "net/log/net_log_source.h" @@ -41,17 +41,16 @@ static void RunSocketCallback( class ResolveHostAndOpenSocket final : public network::ResolveHostClientBase { public: - ResolveHostAndOpenSocket(const net::HostPortPair& address, - const AdbClientSocket::SocketCallback& callback, - network::mojom::HostResolverPtr* host_resolver) - : callback_(callback), binding_(this) { - network::mojom::ResolveHostClientPtr client_ptr; - binding_.Bind(mojo::MakeRequest(&client_ptr)); - binding_.set_connection_error_handler( + ResolveHostAndOpenSocket( + const net::HostPortPair& address, + const AdbClientSocket::SocketCallback& callback, + mojo::Remote<network::mojom::HostResolver>* host_resolver) + : callback_(callback) { + (*host_resolver) + ->ResolveHost(address, nullptr, receiver_.BindNewPipeAndPassRemote()); + receiver_.set_disconnect_handler( base::BindOnce(&ResolveHostAndOpenSocket::OnComplete, base::Unretained(this), net::ERR_FAILED, base::nullopt)); - - (*host_resolver)->ResolveHost(address, nullptr, std::move(client_ptr)); } private: @@ -77,7 +76,7 @@ class ResolveHostAndOpenSocket final : public network::ResolveHostClientBase { } AdbClientSocket::SocketCallback callback_; - mojo::Binding<network::mojom::ResolveHostClient> binding_; + mojo::Receiver<network::mojom::ResolveHostClient> receiver_{this}; }; } // namespace @@ -157,16 +156,18 @@ TCPDeviceProvider::~TCPDeviceProvider() { } void TCPDeviceProvider::InitializeHostResolver() { - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&TCPDeviceProvider::InitializeHostResolverOnUI, - this, mojo::MakeRequest(&host_resolver_))); - host_resolver_.set_connection_error_handler(base::BindOnce( + host_resolver_.reset(); + base::PostTask( + FROM_HERE, {content::BrowserThread::UI}, + base::BindOnce(&TCPDeviceProvider::InitializeHostResolverOnUI, this, + host_resolver_.BindNewPipeAndPassReceiver())); + host_resolver_.set_disconnect_handler(base::BindOnce( &TCPDeviceProvider::InitializeHostResolver, base::Unretained(this))); } void TCPDeviceProvider::InitializeHostResolverOnUI( - network::mojom::HostResolverRequest request) { + mojo::PendingReceiver<network::mojom::HostResolver> receiver) { g_browser_process->system_network_context_manager() ->GetContext() - ->CreateHostResolver(base::nullopt, std::move(request)); + ->CreateHostResolver(base::nullopt, std::move(receiver)); } diff --git a/chromium/chrome/browser/devtools/device/tcp_device_provider.h b/chromium/chrome/browser/devtools/device/tcp_device_provider.h index 5f1dae62d0b..17dea4f6d02 100644 --- a/chromium/chrome/browser/devtools/device/tcp_device_provider.h +++ b/chromium/chrome/browser/devtools/device/tcp_device_provider.h @@ -10,7 +10,10 @@ #include <set> #include "chrome/browser/devtools/device/android_device_manager.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/remote.h" #include "net/base/host_port_pair.h" +#include "services/network/public/mojom/host_resolver.mojom.h" // Instantiate this class only in a test and/or when the DEBUG_DEVTOOLS // BUILDFLAG is set. @@ -40,11 +43,12 @@ class TCPDeviceProvider : public AndroidDeviceManager::DeviceProvider { ~TCPDeviceProvider() override; void InitializeHostResolver(); - void InitializeHostResolverOnUI(network::mojom::HostResolverRequest request); + void InitializeHostResolverOnUI( + mojo::PendingReceiver<network::mojom::HostResolver> receiver); HostPortSet targets_; base::Closure release_callback_; - network::mojom::HostResolverPtr host_resolver_; + mojo::Remote<network::mojom::HostResolver> host_resolver_; }; #endif // CHROME_BROWSER_DEVTOOLS_DEVICE_TCP_DEVICE_PROVIDER_H_ diff --git a/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc index ad76170f21e..c290c153251 100644 --- a/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc +++ b/chromium/chrome/browser/devtools/devtools_sanity_browsertest.cc @@ -99,6 +99,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/resource_response.h" #include "third_party/blink/public/platform/web_input_event.h" #include "ui/compositor/compositor_switches.h" #include "ui/gl/gl_switches.h" @@ -762,13 +763,9 @@ IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, // Tests that BeforeUnload event gets called on devtools that are opened // on another devtools. -#if defined(OS_CHROMEOS) || defined(OS_LINUX) // TODO(https://crbug.com/1000654): Re-enable this test. -#define MAYBE_TestDevToolsOnDevTools DISABLED_TestDevToolsOnDevTools -#else -#define MAYBE_TestDevToolsOnDevTools TestDevToolsOnDevTools -#endif -IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, MAYBE_TestDevToolsOnDevTools) { +IN_PROC_BROWSER_TEST_F(DevToolsBeforeUnloadTest, + DISABLED_TestDevToolsOnDevTools) { ASSERT_TRUE(spawned_test_server()->Start()); LoadTestPage(kDebuggerTestPage); @@ -831,9 +828,16 @@ IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestShowScriptsTab) { // Tests that scripts tab is populated with inspected scripts even if it // hadn't been shown by the moment inspected paged refreshed. // @see http://crbug.com/26312 -IN_PROC_BROWSER_TEST_F( - DevToolsSanityTest, - TestScriptsTabIsPopulatedOnInspectedPageRefresh) { +// This test is flaky on windows and linux asan. See https://crbug.com/1013003 +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh \ + DISABLED_TestScriptsTabIsPopulatedOnInspectedPageRefresh +#else +#define MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh \ + TestScriptsTabIsPopulatedOnInspectedPageRefresh +#endif +IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, + MAYBE_TestScriptsTabIsPopulatedOnInspectedPageRefresh) { RunTest("testScriptsTabIsPopulatedOnInspectedPageRefresh", kDebuggerTestPage); } diff --git a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc index 1acc82d6f38..b43e8b7f7fc 100644 --- a/chromium/chrome/browser/devtools/devtools_ui_bindings.cc +++ b/chromium/chrome/browser/devtools/devtools_ui_bindings.cc @@ -77,6 +77,7 @@ #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/network/public/cpp/simple_url_loader.h" #include "services/network/public/cpp/simple_url_loader_stream_consumer.h" +#include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/blink/public/mojom/renderer_preferences.mojom.h" #include "third_party/blink/public/public_buildflags.h" #include "ui/base/page_transition_types.h" @@ -199,9 +200,17 @@ InfoBarService* DefaultBindingsDelegate::GetInfoBarService() { } std::unique_ptr<base::DictionaryValue> BuildObjectForResponse( - const net::HttpResponseHeaders* rh) { + const net::HttpResponseHeaders* rh, + bool success) { auto response = std::make_unique<base::DictionaryValue>(); - response->SetInteger("statusCode", rh ? rh->response_code() : 200); + int responseCode = 200; + if (rh) { + responseCode = rh->response_code(); + } else if (!success) { + // In case of no headers, assume file:// URL and failed to load + responseCode = 404; + } + response->SetInteger("statusCode", responseCode); auto headers = std::make_unique<base::DictionaryValue>(); size_t iterator = 0; @@ -312,7 +321,7 @@ std::string SanitizeFrontendQueryParam( // Convert boolean flags to true. if (key == "can_dock" || key == "debugFrontend" || key == "experiments" || key == "isSharedWorker" || key == "v8only" || key == "remoteFrontend" || - key == "nodeFrontend" || key == "hasOtherClients") + key == "nodeFrontend" || key == "hasOtherClients" || key == "uiDevTools") return "true"; // Pass connection endpoints as is. @@ -372,28 +381,86 @@ GURL SanitizeFrontendURL(const GURL& url, return result; } +constexpr base::TimeDelta kInitialBackoffDelay = + base::TimeDelta::FromMilliseconds(250); +constexpr base::TimeDelta kMaxBackoffDelay = base::TimeDelta::FromSeconds(10); + } // namespace class DevToolsUIBindings::NetworkResourceLoader : public network::SimpleURLLoaderStreamConsumer { public: - NetworkResourceLoader(int stream_id, - DevToolsUIBindings* bindings, - std::unique_ptr<network::SimpleURLLoader> loader, - network::mojom::URLLoaderFactory* url_loader_factory, - const DispatchCallback& callback) + class URLLoaderFactoryHolder { + public: + network::mojom::URLLoaderFactory* get() { + return ptr_.get() ? ptr_.get() : refptr_.get(); + } + void operator=(std::unique_ptr<network::mojom::URLLoaderFactory>&& ptr) { + ptr_ = std::move(ptr); + } + void operator=(scoped_refptr<network::SharedURLLoaderFactory>&& refptr) { + refptr_ = std::move(refptr); + } + + private: + std::unique_ptr<network::mojom::URLLoaderFactory> ptr_; + scoped_refptr<network::SharedURLLoaderFactory> refptr_; + }; + + static void Create(int stream_id, + DevToolsUIBindings* bindings, + const network::ResourceRequest& resource_request, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + URLLoaderFactoryHolder url_loader_factory, + const DevToolsUIBindings::DispatchCallback& callback, + base::TimeDelta retry_delay = base::TimeDelta()) { + auto resource_loader = + std::make_unique<DevToolsUIBindings::NetworkResourceLoader>( + stream_id, bindings, resource_request, traffic_annotation, + std::move(url_loader_factory), callback, retry_delay); + bindings->loaders_.insert(std::move(resource_loader)); + } + + NetworkResourceLoader( + int stream_id, + DevToolsUIBindings* bindings, + const network::ResourceRequest& resource_request, + const net::NetworkTrafficAnnotationTag& traffic_annotation, + URLLoaderFactoryHolder url_loader_factory, + const DispatchCallback& callback, + base::TimeDelta delay) : stream_id_(stream_id), bindings_(bindings), - loader_(std::move(loader)), - callback_(callback) { + resource_request_(resource_request), + traffic_annotation_(traffic_annotation), + loader_(network::SimpleURLLoader::Create( + std::make_unique<network::ResourceRequest>(resource_request), + traffic_annotation)), + url_loader_factory_(std::move(url_loader_factory)), + callback_(callback), + retry_delay_(delay) { loader_->SetOnResponseStartedCallback(base::BindOnce( &NetworkResourceLoader::OnResponseStarted, base::Unretained(this))); - loader_->DownloadAsStream(url_loader_factory, this); + timer_.Start(FROM_HERE, delay, + base::BindRepeating(&NetworkResourceLoader::DownloadAsStream, + base::Unretained(this))); } private: + void DownloadAsStream() { + loader_->DownloadAsStream(url_loader_factory_.get(), this); + } + + base::TimeDelta GetNextExponentialBackoffDelay(const base::TimeDelta& delta) { + if (delta.is_zero()) { + return kInitialBackoffDelay; + } else { + return delta * 1.3; + } + } + void OnResponseStarted(const GURL& final_url, - const network::ResourceResponseHead& response_head) { + const network::mojom::URLResponseHead& response_head) { response_headers_ = response_head.headers; } @@ -418,9 +485,21 @@ class DevToolsUIBindings::NetworkResourceLoader } void OnComplete(bool success) override { - auto response = BuildObjectForResponse(response_headers_.get()); - callback_.Run(response.get()); - + if (!success && loader_->NetError() == net::ERR_INSUFFICIENT_RESOURCES && + retry_delay_ < kMaxBackoffDelay) { + const base::TimeDelta delay = + GetNextExponentialBackoffDelay(retry_delay_); + LOG(WARNING) << "DevToolsUIBindings::NetworkResourceLoader id = " + << stream_id_ + << " failed with insufficient resources, retrying in " + << delay << "." << std::endl; + NetworkResourceLoader::Create( + stream_id_, bindings_, resource_request_, traffic_annotation_, + std::move(url_loader_factory_), callback_, delay); + } else { + auto response = BuildObjectForResponse(response_headers_.get(), success); + callback_.Run(response.get()); + } bindings_->loaders_.erase(bindings_->loaders_.find(this)); } @@ -428,9 +507,14 @@ class DevToolsUIBindings::NetworkResourceLoader const int stream_id_; DevToolsUIBindings* const bindings_; + const network::ResourceRequest resource_request_; + const net::NetworkTrafficAnnotationTag traffic_annotation_; std::unique_ptr<network::SimpleURLLoader> loader_; + URLLoaderFactoryHolder url_loader_factory_; DispatchCallback callback_; scoped_refptr<net::HttpResponseHeaders> response_headers_; + base::OneShotTimer timer_; + base::TimeDelta retry_delay_; DISALLOW_COPY_AND_ASSIGN(NetworkResourceLoader); }; @@ -738,22 +822,18 @@ void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback, } })"); - auto resource_request = std::make_unique<network::ResourceRequest>(); - resource_request->url = gurl; + network::ResourceRequest resource_request; + resource_request.url = gurl; // TODO(caseq): this preserves behavior of URLFetcher-based implementation. // We really need to pass proper first party origin from the front-end. - resource_request->site_for_cookies = gurl; - resource_request->headers.AddHeadersFromString(headers); + resource_request.site_for_cookies = gurl; + resource_request.headers.AddHeadersFromString(headers); - std::unique_ptr<network::mojom::URLLoaderFactory> file_url_loader_factory; - scoped_refptr<network::SharedURLLoaderFactory> network_url_loader_factory; - std::unique_ptr<network::mojom::URLLoaderFactory> webui_url_loader_factory; - network::mojom::URLLoaderFactory* url_loader_factory; + NetworkResourceLoader::URLLoaderFactoryHolder url_loader_factory; if (gurl.SchemeIsFile()) { - file_url_loader_factory = content::CreateFileURLLoaderFactory( + url_loader_factory = content::CreateFileURLLoaderFactory( base::FilePath() /* profile_path */, nullptr /* shared_cors_origin_access_list */); - url_loader_factory = file_url_loader_factory.get(); } else if (content::HasWebUIScheme(gurl)) { content::WebContents* target_tab; #ifndef NDEBUG @@ -769,10 +849,9 @@ void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback, if (allow_web_ui_scheme) { std::vector<std::string> allowed_webui_hosts; content::RenderFrameHost* frame_host = web_contents()->GetMainFrame(); - webui_url_loader_factory = content::CreateWebUIURLLoader( + url_loader_factory = content::CreateWebUIURLLoader( frame_host, target_tab->GetURL().scheme(), std::move(allowed_webui_hosts)); - url_loader_factory = webui_url_loader_factory.get(); } else { base::DictionaryValue response; response.SetInteger("statusCode", 403); @@ -782,17 +861,12 @@ void DevToolsUIBindings::LoadNetworkResource(const DispatchCallback& callback, } else { auto* partition = content::BrowserContext::GetStoragePartitionForSite( web_contents_->GetBrowserContext(), gurl); - network_url_loader_factory = - partition->GetURLLoaderFactoryForBrowserProcess(); - url_loader_factory = network_url_loader_factory.get(); + url_loader_factory = partition->GetURLLoaderFactoryForBrowserProcess(); } - auto simple_url_loader = network::SimpleURLLoader::Create( - std::move(resource_request), traffic_annotation); - auto resource_loader = std::make_unique<NetworkResourceLoader>( - stream_id, this, std::move(simple_url_loader), url_loader_factory, - callback); - loaders_.insert(std::move(resource_loader)); + NetworkResourceLoader::Create(stream_id, this, resource_request, + traffic_annotation, + std::move(url_loader_factory), callback); } void DevToolsUIBindings::OpenInNewTab(const std::string& url) { diff --git a/chromium/chrome/browser/devtools/devtools_window.cc b/chromium/chrome/browser/devtools/devtools_window.cc index 14bc702c89f..f0e249161a7 100644 --- a/chromium/chrome/browser/devtools/devtools_window.cc +++ b/chromium/chrome/browser/devtools/devtools_window.cc @@ -5,6 +5,7 @@ #include "chrome/browser/devtools/devtools_window.h" #include <algorithm> +#include <set> #include <utility> #include "base/base64.h" @@ -21,7 +22,6 @@ #include "chrome/browser/devtools/devtools_eye_dropper.h" #include "chrome/browser/file_select_helper.h" #include "chrome/browser/infobars/infobar_service.h" -#include "chrome/browser/performance_manager/performance_manager_tab_helper.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/task_manager/web_contents_tags.h" @@ -38,6 +38,7 @@ #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "components/app_modal/javascript_dialog_manager.h" +#include "components/performance_manager/performance_manager_tab_helper.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/sync_preferences/pref_service_syncable.h" @@ -170,8 +171,8 @@ content::WebContents* DevToolsToolboxDelegate::OpenURLFromTab( DCHECK(source == web_contents()); if (!params.url.SchemeIs(content::kChromeDevToolsScheme)) return NULL; - content::NavigationController::LoadURLParams load_url_params(params.url); - source->GetController().LoadURLWithParams(load_url_params); + source->GetController().LoadURLWithParams( + content::NavigationController::LoadURLParams(params)); return source; } @@ -218,7 +219,7 @@ GURL DecorateFrontendURL(const GURL& base_url) { std::string url_string( frontend_url + ((frontend_url.find("?") == std::string::npos) ? "?" : "&") + - "dockSide=undocked"); // TODO(dgozman): remove this support in M38. + "dockSide=undocked"); // TODO(dgozman): remove this support in M38. base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kEnableDevToolsExperiments)) url_string += "&experiments=true"; @@ -770,6 +771,16 @@ DevToolsWindow::MaybeCreateNavigationThrottle( return std::make_unique<Throttle>(handle, window); } +void DevToolsWindow::UpdateInspectedWebContents( + content::WebContents* new_web_contents) { + inspected_contents_observer_ = + std::make_unique<ObserverWithAccessor>(new_web_contents); + bindings_->AttachTo( + content::DevToolsAgentHost::GetOrCreateFor(new_web_contents)); + bindings_->CallClientFunction("DevToolsAPI.reattachMainTarget", nullptr, + nullptr, nullptr); +} + void DevToolsWindow::ScheduleShow(const DevToolsToggleAction& action) { if (life_stage_ == kLoadCompleted) { Show(action); @@ -800,7 +811,7 @@ void DevToolsWindow::Show(const DevToolsToggleAction& action) { &inspected_browser, &inspected_tab_index); DCHECK(inspected_browser); - DCHECK(inspected_tab_index != -1); + DCHECK_NE(-1, inspected_tab_index); RegisterModalDialogManager(inspected_browser); @@ -821,7 +832,7 @@ void DevToolsWindow::Show(const DevToolsToggleAction& action) { main_web_contents_->SetInitialFocus(); PrefsTabHelper::CreateForWebContents(main_web_contents_); - main_web_contents_->GetRenderViewHost()->SyncRendererPrefs(); + main_web_contents_->SyncRendererPrefs(); DoAction(action); return; @@ -1545,7 +1556,7 @@ void DevToolsWindow::CreateDevToolsBrowser() { browser_->tab_strip_model()->AddWebContents( std::move(owned_main_web_contents_), -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL, TabStripModel::ADD_ACTIVE); - main_web_contents_->GetRenderViewHost()->SyncRendererPrefs(); + main_web_contents_->SyncRendererPrefs(); } BrowserWindow* DevToolsWindow::GetInspectedBrowserWindow() { diff --git a/chromium/chrome/browser/devtools/devtools_window.h b/chromium/chrome/browser/devtools/devtools_window.h index b47e506fff9..0a14822e2c4 100644 --- a/chromium/chrome/browser/devtools/devtools_window.h +++ b/chromium/chrome/browser/devtools/devtools_window.h @@ -5,6 +5,9 @@ #ifndef CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ #define CHROME_BROWSER_DEVTOOLS_DEVTOOLS_WINDOW_H_ +#include <memory> +#include <string> + #include "base/macros.h" #include "chrome/browser/devtools/devtools_contents_resizing_strategy.h" #include "chrome/browser/devtools/devtools_toggle_action.h" @@ -122,6 +125,11 @@ class DevToolsWindow : public DevToolsUIBindings::Delegate, static std::unique_ptr<content::NavigationThrottle> MaybeCreateNavigationThrottle(content::NavigationHandle* handle); + // Updates the WebContents inspected by the DevToolsWindow by reattaching + // the binding to |new_web_contents|. Called when swapping an outer + // WebContents with its inner WebContents. + void UpdateInspectedWebContents(content::WebContents* new_web_contents); + // Sets closure to be called after load is done. If already loaded, calls // closure immediately. void SetLoadCompletedCallback(const base::Closure& closure); diff --git a/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html b/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html index 88291aa4d80..52880365af5 100644 --- a/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html +++ b/chromium/chrome/browser/devtools/frontend/devtools_discovery_page.html @@ -1,5 +1,5 @@ -<html> -<head> +<!doctype html> +<html lang="en"> <title>Inspectable pages</title> <meta name="referrer" content="no-referrer"> <style> @@ -102,6 +102,19 @@ function onReady() { } } +function onBlur(event) { + const selection = window.getSelection(); + selection.removeAllRanges(); + event.stopPropagation(); + event.preventDefault(); +} + +function onFocus(selectElement, event) { + selectNodeText(selectElement, event); + event.stopPropagation(); + event.preventDefault(); +} + function customFrontendURL(url) { if (!url || !window.location.hash) return null; @@ -164,6 +177,9 @@ function appendItem(item_object) { var urlValue = document.createElement('div'); urlValue.classList.add("custom-url-value"); urlValue.textContent = customURL; + urlValue.tabIndex = 0; + urlValue.addEventListener('blur', onBlur); + urlValue.addEventListener('focus', event => onFocus(urlValue, event)); urlContainer.appendChild(urlValue); description.appendChild(urlContainer); item_element.addEventListener('click', selectNodeText.bind(null, urlValue)); @@ -189,9 +205,11 @@ function selectNodeText(selectElement, event) </script> </head> <body onload='onLoad()'> - <div id='caption'>Inspectable pages</div> - <hr> - <div id='items'> + <div role='main'> + <div id='caption' role='heading' aria-level='1'>Inspectable pages</div> + <hr> + <div id='items'> + </div> </div> </body> </html> diff --git a/chromium/chrome/browser/devtools/inspector_protocol_config.json b/chromium/chrome/browser/devtools/inspector_protocol_config.json index 53897298398..431eaa6b34a 100644 --- a/chromium/chrome/browser/devtools/inspector_protocol_config.json +++ b/chromium/chrome/browser/devtools/inspector_protocol_config.json @@ -28,6 +28,11 @@ }, { "domain": "WindowManager" + }, + { + "domain": "Security", + "include": [ "enable", "disable" ], + "include_events": [ "visibleSecurityStateChanged" ] } ] }, diff --git a/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc new file mode 100644 index 00000000000..cebdc256278 --- /dev/null +++ b/chromium/chrome/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -0,0 +1,336 @@ +// Copyright 2019 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 <memory> +#include <string> + +#include "base/base64.h" +#include "base/callback.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/values.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/ssl_status.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" +#include "net/ssl/ssl_cipher_suite_names.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" + +namespace { + +const char kIdParam[] = "id"; +const char kMethodParam[] = "method"; + +} // namespace + +class DevToolsProtocolTest : public InProcessBrowserTest, + public content::DevToolsAgentHostClient { + public: + DevToolsProtocolTest() : last_sent_id_(0) {} + + protected: + typedef base::RepeatingCallback<bool(const base::Value&)> NotificationMatcher; + + // InProcessBrowserTest interface + void TearDownOnMainThread() override { Detach(); } + + // DevToolsAgentHostClient interface + void DispatchProtocolMessage(content::DevToolsAgentHost* agent_host, + const std::string& message) override { + auto parsed_message = base::JSONReader::Read(message); + auto id = parsed_message->FindIntPath("id"); + if (id) { + // TODO: implement handling of results from method calls (when needed). + } else { + std::string* notification = parsed_message->FindStringPath("method"); + EXPECT_TRUE(notification); + notifications_.push_back(*notification); + base::Value* params = parsed_message->FindPath("params"); + notification_params_.push_back(params ? params->Clone() : base::Value()); + if (waiting_for_notification_ == *notification && + (waiting_for_notification_matcher_.is_null() || + waiting_for_notification_matcher_.Run( + notification_params_.back()))) { + waiting_for_notification_ = std::string(); + waiting_for_notification_matcher_ = NotificationMatcher(); + waiting_for_notification_params_ = notification_params_.back().Clone(); + std::move(run_loop_quit_closure_).Run(); + } + } + } + + void SendCommand(const std::string& method) { + base::Value command(base::Value::Type::DICTIONARY); + command.SetKey(kIdParam, base::Value(++last_sent_id_)); + command.SetKey(kMethodParam, base::Value(method)); + std::string json_command; + base::JSONWriter::Write(command, &json_command); + agent_host_->DispatchProtocolMessage(this, json_command); + } + + void RunLoopUpdatingQuitClosure() { + base::RunLoop run_loop; + CHECK(!run_loop_quit_closure_); + run_loop_quit_closure_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + void Attach() { + agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents()); + agent_host_->AttachClient(this); + } + + void Detach() { + if (agent_host_) { + agent_host_->DetachClient(this); + agent_host_ = nullptr; + } + } + + content::WebContents* web_contents() { + return browser()->tab_strip_model()->GetWebContentsAt(0); + } + + base::Value WaitForNotification(const std::string& notification) { + auto always_match = base::Bind([](const base::Value&) { return true; }); + return WaitForMatchingNotification(notification, always_match); + } + + base::Value WaitForMatchingNotification(const std::string& notification, + const NotificationMatcher& matcher) { + for (size_t i = 0; i < notifications_.size(); ++i) { + if (notifications_[i] == notification && + matcher.Run(notification_params_[i])) { + base::Value result = std::move(notification_params_[i]); + notifications_.erase(notifications_.begin() + i); + notification_params_.erase(notification_params_.begin() + i); + return result; + } + } + waiting_for_notification_ = notification; + waiting_for_notification_matcher_ = matcher; + RunLoopUpdatingQuitClosure(); + return std::move(waiting_for_notification_params_); + } + + private: + // DevToolsAgentHostClient interface + void AgentHostClosed(content::DevToolsAgentHost* agent_host) override {} + + scoped_refptr<content::DevToolsAgentHost> agent_host_; + int last_sent_id_; + base::OnceClosure run_loop_quit_closure_; + std::vector<std::string> notifications_; + std::vector<base::Value> notification_params_; + std::string waiting_for_notification_; + NotificationMatcher waiting_for_notification_matcher_; + base::Value waiting_for_notification_params_; +}; + +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, + VisibleSecurityStateChangedNeutralState) { + ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); + content::WaitForLoadStop(web_contents()); + + Attach(); + SendCommand("Security.enable"); + base::Value params = + WaitForNotification("Security.visibleSecurityStateChanged"); + + std::string* security_state = + params.FindStringPath("visibleSecurityState.securityState"); + ASSERT_TRUE(security_state); + ASSERT_EQ(std::string("neutral"), *security_state); + ASSERT_FALSE( + params.FindPath("visibleSecurityState.certificateSecurityState")); + const base::Value* security_state_issue_ids = + params.FindListPath("visibleSecurityState.securityStateIssueIds"); + ASSERT_TRUE(std::find(security_state_issue_ids->GetList().begin(), + security_state_issue_ids->GetList().end(), + base::Value("scheme-is-not-cryptographic")) != + security_state_issue_ids->GetList().end()); +} + +IN_PROC_BROWSER_TEST_F(DevToolsProtocolTest, VisibleSecurityStateSecureState) { + net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); + https_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir()); + ASSERT_TRUE(https_server.Start()); + + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( + browser(), https_server.GetURL("/title1.html"), 1); + content::NavigationEntry* entry = + web_contents()->GetController().GetLastCommittedEntry(); + ASSERT_TRUE(entry); + + // Extract SSL status data from the navigation entry. + scoped_refptr<net::X509Certificate> page_cert = entry->GetSSL().certificate; + ASSERT_TRUE(page_cert); + + int ssl_version = + net::SSLConnectionStatusToVersion(entry->GetSSL().connection_status); + const char* page_protocol; + net::SSLVersionToString(&page_protocol, ssl_version); + + const char* page_key_exchange_str; + const char* page_cipher; + const char* page_mac; + bool is_aead; + bool is_tls13; + uint16_t page_cipher_suite = + net::SSLConnectionStatusToCipherSuite(entry->GetSSL().connection_status); + net::SSLCipherSuiteToStrings(&page_key_exchange_str, &page_cipher, &page_mac, + &is_aead, &is_tls13, page_cipher_suite); + std::string page_key_exchange; + if (page_key_exchange_str) + page_key_exchange = page_key_exchange_str; + + const char* page_key_exchange_group = + SSL_get_curve_name(entry->GetSSL().key_exchange_group); + + std::string page_subject_name; + std::string page_issuer_name; + double page_valid_from = 0.0; + double page_valid_to = 0.0; + if (entry->GetSSL().certificate) { + page_subject_name = entry->GetSSL().certificate->subject().common_name; + page_issuer_name = entry->GetSSL().certificate->issuer().common_name; + page_valid_from = entry->GetSSL().certificate->valid_start().ToDoubleT(); + page_valid_to = entry->GetSSL().certificate->valid_expiry().ToDoubleT(); + } + + bool page_certificate_has_weak_signature = + (entry->GetSSL().cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM); + + int status = net::ObsoleteSSLStatus(entry->GetSSL().connection_status, + entry->GetSSL().peer_signature_algorithm); + bool page_modern_ssl = status == net::OBSOLETE_SSL_NONE; + bool page_obsolete_ssl_protocol = status & net::OBSOLETE_SSL_MASK_PROTOCOL; + bool page_obsolete_ssl_key_exchange = + status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE; + bool page_obsolete_ssl_cipher = status & net::OBSOLETE_SSL_MASK_CIPHER; + bool page_obsolete_ssl_signature = status & net::OBSOLETE_SSL_MASK_SIGNATURE; + + Attach(); + SendCommand("Security.enable"); + auto has_certificate = [](const base::Value& params) { + return params.FindListPath( + "visibleSecurityState.certificateSecurityState.certificate") != + nullptr; + }; + base::Value params = WaitForMatchingNotification( + "Security.visibleSecurityStateChanged", base::Bind(has_certificate)); + + // Verify that the visibleSecurityState payload matches the SSL status data. + std::string* security_state = + params.FindStringPath("visibleSecurityState.securityState"); + ASSERT_TRUE(security_state); + ASSERT_EQ(std::string("secure"), *security_state); + + base::Value* certificate_security_state = + params.FindPath("visibleSecurityState.certificateSecurityState"); + ASSERT_TRUE(certificate_security_state); + + std::string* protocol = + certificate_security_state->FindStringPath("protocol"); + ASSERT_TRUE(protocol); + ASSERT_EQ(*protocol, page_protocol); + + std::string* key_exchange = + certificate_security_state->FindStringPath("keyExchange"); + ASSERT_TRUE(key_exchange); + ASSERT_EQ(*key_exchange, page_key_exchange); + + std::string* key_exchange_group = + certificate_security_state->FindStringPath("keyExchangeGroup"); + if (key_exchange_group) { + ASSERT_EQ(*key_exchange_group, page_key_exchange_group); + } + + std::string* mac = certificate_security_state->FindStringPath("mac"); + if (mac) { + ASSERT_EQ(*mac, page_mac); + } + + std::string* cipher = certificate_security_state->FindStringPath("cipher"); + ASSERT_TRUE(cipher); + ASSERT_EQ(*cipher, page_cipher); + + std::string* subject_name = + certificate_security_state->FindStringPath("subjectName"); + ASSERT_TRUE(subject_name); + ASSERT_EQ(*subject_name, page_subject_name); + + std::string* issuer = certificate_security_state->FindStringPath("issuer"); + ASSERT_TRUE(issuer); + ASSERT_EQ(*issuer, page_issuer_name); + + auto valid_from = certificate_security_state->FindDoublePath("validFrom"); + ASSERT_TRUE(valid_from); + ASSERT_EQ(*valid_from, page_valid_from); + + auto valid_to = certificate_security_state->FindDoublePath("validTo"); + ASSERT_TRUE(valid_to); + ASSERT_EQ(*valid_to, page_valid_to); + + auto certificate_has_weak_signature = + certificate_security_state->FindBoolPath("certifcateHasWeakSignature"); + ASSERT_TRUE(certificate_has_weak_signature); + ASSERT_EQ(*certificate_has_weak_signature, + page_certificate_has_weak_signature); + + auto modern_ssl = certificate_security_state->FindBoolPath("modernSSL"); + ASSERT_TRUE(modern_ssl); + ASSERT_EQ(*modern_ssl, page_modern_ssl); + + auto obsolete_ssl_protocol = + certificate_security_state->FindBoolPath("obsoleteSslProtocol"); + ASSERT_TRUE(obsolete_ssl_protocol); + ASSERT_EQ(*obsolete_ssl_protocol, page_obsolete_ssl_protocol); + + auto obsolete_ssl_key_exchange = + certificate_security_state->FindBoolPath("obsoleteSslKeyExchange"); + ASSERT_TRUE(obsolete_ssl_key_exchange); + ASSERT_EQ(*obsolete_ssl_key_exchange, page_obsolete_ssl_key_exchange); + + auto obsolete_ssl_cipher = + certificate_security_state->FindBoolPath("obsoleteSslCipher"); + ASSERT_TRUE(obsolete_ssl_cipher); + ASSERT_EQ(*obsolete_ssl_cipher, page_obsolete_ssl_cipher); + + auto obsolete_ssl_signature = + certificate_security_state->FindBoolPath("obsoleteSslSignature"); + ASSERT_TRUE(obsolete_ssl_signature); + ASSERT_EQ(*obsolete_ssl_signature, page_obsolete_ssl_signature); + + const base::Value* certificate_value = + certificate_security_state->FindListPath("certificate"); + std::vector<std::string> der_certs; + for (const auto& cert : certificate_value->GetList()) { + std::string decoded; + ASSERT_TRUE(base::Base64Decode(cert.GetString(), &decoded)); + der_certs.push_back(decoded); + } + std::vector<base::StringPiece> cert_string_piece; + for (const auto& str : der_certs) { + cert_string_piece.push_back(str); + } + + // Check that the certificateSecurityState.certificate matches. + net::SHA256HashValue page_cert_chain_fingerprint = + page_cert->CalculateChainFingerprint256(); + scoped_refptr<net::X509Certificate> certificate = + net::X509Certificate::CreateFromDERCertChain(cert_string_piece); + ASSERT_TRUE(certificate); + EXPECT_EQ(page_cert_chain_fingerprint, + certificate->CalculateChainFingerprint256()); + const base::Value* security_state_issue_ids = + params.FindListPath("visibleSecurityState.securityStateIssueIds"); + EXPECT_EQ(security_state_issue_ids->GetList().size(), 0u); +} diff --git a/chromium/chrome/browser/devtools/protocol/security_handler.cc b/chromium/chrome/browser/devtools/protocol/security_handler.cc new file mode 100644 index 00000000000..a9db9463a58 --- /dev/null +++ b/chromium/chrome/browser/devtools/protocol/security_handler.cc @@ -0,0 +1,246 @@ +// Copyright 2019 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/devtools/protocol/security_handler.h" + +#include <string> +#include <vector> + +#include "base/base64.h" +#include "chrome/browser/ssl/security_state_tab_helper.h" +#include "components/security_state/content/content_utils.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/origin_util.h" +#include "net/cert/x509_certificate.h" +#include "net/cert/x509_util.h" +#include "net/ssl/ssl_cipher_suite_names.h" +#include "net/ssl/ssl_connection_status_flags.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" + +namespace { + +const char kInsecureOriginSecurityStateIssueId[] = "insecure-origin"; +const char kSchemeIsNotCryptographicSecurityStateIssueId[] = + "scheme-is-not-cryptographic"; +const char kMalicousContentSecurityStateIssueId[] = "malicious-content"; +const char kDisplayedMixedContentSecurityStateIssueId[] = + "displayed-mixed-content"; +const char kContainedMixedFormSecurityStateIssueId[] = "contained-mixed-form"; +const char kRanMixedContentSecurityStateIssueId[] = "ran-mixed-content"; +const char kDisplayedContentWithCertErrorsSecurityStateIssueId[] = + "displayed-content-with-cert-errors"; +const char kRanContentWithCertErrorSecurityStateIssueId[] = + "ran-content-with-cert-error"; +const char kPkpBypassedSecurityStateIssueId[] = "pkp-bypassed"; +const char kIsErrorPageSecurityStateIssueId[] = "is-error-page"; +const char kInsecureInputEventsSecurityStateIssueId[] = "insecure-input-events"; + +std::string SecurityLevelToProtocolSecurityState( + security_state::SecurityLevel security_level) { + switch (security_level) { + case security_state::NONE: + case security_state::WARNING: + return protocol::Security::SecurityStateEnum::Neutral; + case security_state::SECURE_WITH_POLICY_INSTALLED_CERT: + case security_state::EV_SECURE: + case security_state::SECURE: + return protocol::Security::SecurityStateEnum::Secure; + case security_state::DANGEROUS: + return protocol::Security::SecurityStateEnum::Insecure; + case security_state::SECURITY_LEVEL_COUNT: + NOTREACHED(); + return protocol::Security::SecurityStateEnum::Neutral; + } + + NOTREACHED(); + return protocol::Security::SecurityStateEnum::Neutral; +} + +std::unique_ptr<protocol::Security::CertificateSecurityState> +CreateCertificateSecurityState( + const security_state::VisibleSecurityState& state) { + auto certificate = std::make_unique<protocol::Array<protocol::String>>(); + if (state.certificate) { + certificate->emplace_back(); + base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece( + state.certificate->cert_buffer()), + &certificate->back()); + for (const auto& cert : state.certificate->intermediate_buffers()) { + certificate->emplace_back(); + base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(cert.get()), + &certificate->back()); + } + } + + int ssl_version = net::SSLConnectionStatusToVersion(state.connection_status); + const char* protocol; + net::SSLVersionToString(&protocol, ssl_version); + + const char* key_exchange_str; + const char* cipher; + const char* mac; + bool is_aead; + bool is_tls13; + uint16_t cipher_suite = + net::SSLConnectionStatusToCipherSuite(state.connection_status); + net::SSLCipherSuiteToStrings(&key_exchange_str, &cipher, &mac, &is_aead, + &is_tls13, cipher_suite); + std::string key_exchange; + if (key_exchange_str) + key_exchange = key_exchange_str; + + const char* key_exchange_group = SSL_get_curve_name(state.key_exchange_group); + + std::string subject_name; + std::string issuer_name; + double valid_from = 0.0; + double valid_to = 0.0; + if (state.certificate) { + subject_name = state.certificate->subject().common_name; + issuer_name = state.certificate->issuer().common_name; + valid_from = state.certificate->valid_start().ToDoubleT(); + valid_to = state.certificate->valid_expiry().ToDoubleT(); + } + + bool certificate_has_weak_signature = + (state.cert_status & net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM); + + int status = net::ObsoleteSSLStatus(state.connection_status, + state.peer_signature_algorithm); + bool modern_ssl = status == net::OBSOLETE_SSL_NONE; + bool obsolete_ssl_protocol = status & net::OBSOLETE_SSL_MASK_PROTOCOL; + bool obsolete_ssl_key_exchange = status & net::OBSOLETE_SSL_MASK_KEY_EXCHANGE; + bool obsolete_ssl_cipher = status & net::OBSOLETE_SSL_MASK_CIPHER; + bool obsolete_ssl_signature = status & net::OBSOLETE_SSL_MASK_SIGNATURE; + + auto certificate_security_state = + protocol::Security::CertificateSecurityState::Create() + .SetProtocol(protocol) + .SetKeyExchange(key_exchange) + .SetCipher(cipher) + .SetCertificate(std::move(certificate)) + .SetSubjectName(subject_name) + .SetIssuer(issuer_name) + .SetValidFrom(valid_from) + .SetValidTo(valid_to) + .SetCertifcateHasWeakSignature(certificate_has_weak_signature) + .SetModernSSL(modern_ssl) + .SetObsoleteSslProtocol(obsolete_ssl_protocol) + .SetObsoleteSslKeyExchange(obsolete_ssl_key_exchange) + .SetObsoleteSslCipher(obsolete_ssl_cipher) + .SetObsoleteSslSignature(obsolete_ssl_signature) + .Build(); + + if (key_exchange_group) + certificate_security_state->SetKeyExchangeGroup(key_exchange_group); + if (mac) + certificate_security_state->SetMac(mac); + + return certificate_security_state; +} + +std::unique_ptr<protocol::Security::VisibleSecurityState> +CreateVisibleSecurityState(const security_state::VisibleSecurityState& state, + content::WebContents* web_contents) { + SecurityStateTabHelper* helper = + SecurityStateTabHelper::FromWebContents(web_contents); + DCHECK(helper); + std::string security_state = + SecurityLevelToProtocolSecurityState(helper->GetSecurityLevel()); + + bool scheme_is_cryptographic = + security_state::IsSchemeCryptographic(state.url); + bool malicious_content = state.malicious_content_status != + security_state::MALICIOUS_CONTENT_STATUS_NONE; + bool insecure_input_events = + state.insecure_input_events.insecure_field_edited; + + bool secure_origin = scheme_is_cryptographic; + if (!scheme_is_cryptographic) + secure_origin = content::IsOriginSecure(state.url); + + std::vector<std::string> security_state_issue_ids; + if (!secure_origin) + security_state_issue_ids.push_back(kInsecureOriginSecurityStateIssueId); + if (!scheme_is_cryptographic) + security_state_issue_ids.push_back( + kSchemeIsNotCryptographicSecurityStateIssueId); + if (malicious_content) + security_state_issue_ids.push_back(kMalicousContentSecurityStateIssueId); + if (state.displayed_mixed_content) + security_state_issue_ids.push_back( + kDisplayedMixedContentSecurityStateIssueId); + if (state.contained_mixed_form) + security_state_issue_ids.push_back(kContainedMixedFormSecurityStateIssueId); + if (state.ran_mixed_content) + security_state_issue_ids.push_back(kRanMixedContentSecurityStateIssueId); + if (state.displayed_content_with_cert_errors) + security_state_issue_ids.push_back( + kDisplayedContentWithCertErrorsSecurityStateIssueId); + if (state.ran_content_with_cert_errors) + security_state_issue_ids.push_back( + kRanContentWithCertErrorSecurityStateIssueId); + if (state.pkp_bypassed) + security_state_issue_ids.push_back(kPkpBypassedSecurityStateIssueId); + if (state.is_error_page) + security_state_issue_ids.push_back(kIsErrorPageSecurityStateIssueId); + if (insecure_input_events) + security_state_issue_ids.push_back( + kInsecureInputEventsSecurityStateIssueId); + + auto visible_security_state = + protocol::Security::VisibleSecurityState::Create() + .SetSecurityState(security_state) + .SetSecurityStateIssueIds( + std::make_unique<protocol::Array<std::string>>( + security_state_issue_ids)) + .Build(); + + if (state.connection_status != 0) { + auto certificate_security_state = CreateCertificateSecurityState(state); + visible_security_state->SetCertificateSecurityState( + std::move(certificate_security_state)); + } + return visible_security_state; +} + +} // namespace + +SecurityHandler::SecurityHandler(content::WebContents* web_contents, + protocol::UberDispatcher* dispatcher) + : content::WebContentsObserver(web_contents) { + DCHECK(web_contents); + frontend_ = + std::make_unique<protocol::Security::Frontend>(dispatcher->channel()); + protocol::Security::Dispatcher::wire(dispatcher, this); +} + +SecurityHandler::~SecurityHandler() {} + +protocol::Response SecurityHandler::Enable() { + if (enabled_) + return protocol::Response::FallThrough(); + enabled_ = true; + DidChangeVisibleSecurityState(); + // Do not mark the command as handled. Let it fall through instead, so that + // the handler in content gets a chance to process the command. + return protocol::Response::FallThrough(); +} + +protocol::Response SecurityHandler::Disable() { + enabled_ = false; + // Do not mark the command as handled. Let it fall through instead, so that + // the handler in content gets a chance to process the command. + return protocol::Response::FallThrough(); +} + +void SecurityHandler::DidChangeVisibleSecurityState() { + if (!enabled_) + return; + + auto state = security_state::GetVisibleSecurityState(web_contents()); + auto visible_security_state = + CreateVisibleSecurityState(*state.get(), web_contents()); + frontend_->VisibleSecurityStateChanged(std::move(visible_security_state)); +} diff --git a/chromium/chrome/browser/devtools/protocol/security_handler.h b/chromium/chrome/browser/devtools/protocol/security_handler.h new file mode 100644 index 00000000000..e4f3c880dac --- /dev/null +++ b/chromium/chrome/browser/devtools/protocol/security_handler.h @@ -0,0 +1,37 @@ +// Copyright 2019 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_DEVTOOLS_PROTOCOL_SECURITY_HANDLER_H_ +#define CHROME_BROWSER_DEVTOOLS_PROTOCOL_SECURITY_HANDLER_H_ + +#include "chrome/browser/devtools/protocol/forward.h" +#include "chrome/browser/devtools/protocol/security.h" +#include "content/public/browser/web_contents_observer.h" + +namespace content { +class WebContents; +} // namespace content + +class SecurityHandler : public protocol::Security::Backend, + public content::WebContentsObserver { + public: + SecurityHandler(content::WebContents* web_contents, + protocol::UberDispatcher* dispatcher); + ~SecurityHandler() override; + + // Security::Backend: + protocol::Response Enable() override; + protocol::Response Disable() override; + + private: + // WebContentsObserver overrides + void DidChangeVisibleSecurityState() override; + + bool enabled_ = false; + std::unique_ptr<protocol::Security::Frontend> frontend_; + + DISALLOW_COPY_AND_ASSIGN(SecurityHandler); +}; + +#endif // CHROME_BROWSER_DEVTOOLS_PROTOCOL_SECURITY_HANDLER_H_ diff --git a/chromium/chrome/browser/devtools/serialize_host_descriptions.cc b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc index d1bdc912536..054acd9be6b 100644 --- a/chromium/chrome/browser/devtools/serialize_host_descriptions.cc +++ b/chromium/chrome/browser/devtools/serialize_host_descriptions.cc @@ -27,8 +27,7 @@ base::DictionaryValue Serialize( auto child_it = children.find(root); if (child_it != children.end()) { for (base::DictionaryValue* child : child_it->second) { - children_list->base::Value::GetList().push_back( - Serialize(child_key, child, children)); + children_list->base::Value::Append(Serialize(child_key, child, children)); } } @@ -98,8 +97,7 @@ base::ListValue SerializeHostDescriptions( base::ListValue list_value; for (auto* root : roots) { - list_value.base::Value::GetList().push_back( - Serialize(child_key, root, children)); + list_value.base::Value::Append(Serialize(child_key, root, children)); } return list_value; } |