diff options
Diffstat (limited to 'chromium/content/browser/devtools')
59 files changed, 3223 insertions, 749 deletions
diff --git a/chromium/content/browser/devtools/BUILD.gn b/chromium/content/browser/devtools/BUILD.gn index 611d0281158..2e5ef6a415e 100644 --- a/chromium/content/browser/devtools/BUILD.gn +++ b/chromium/content/browser/devtools/BUILD.gn @@ -15,13 +15,14 @@ group("resources") { } compressed_protocol_file = - "$root_gen_dir/blink/core/inspector/protocol.json.bro" + "$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json.bro" compress_file_brotli("compressed_protocol_json") { - input_file = "$root_gen_dir/blink/core/inspector/protocol.json" + input_file = + "$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json" output_file = compressed_protocol_file deps = [ - "//third_party/WebKit/Source/core/inspector:protocol_version", + "//third_party/blink/renderer/core/inspector:protocol_version", ] } @@ -49,18 +50,18 @@ grit("devtools_resources") { ":compressed_protocol_json", # This is the action that generates out .grd input file. - "//third_party/WebKit/public:blink_generate_devtools_grd", + "//third_party/blink/public:blink_generate_devtools_grd", ] } inspector_protocol_generate("protocol_sources") { visibility = [ "//content/browser" ] deps = [ - "//third_party/WebKit/Source/core/inspector:protocol_version", + "//third_party/blink/renderer/core/inspector:protocol_version", ] - _blink_protocol_path = - rebase_path("$root_gen_dir/blink/core/inspector/protocol.json", - root_build_dir) + _blink_protocol_path = rebase_path( + "$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json", + root_build_dir) inspector_protocol_dir = "//third_party/inspector_protocol" out_dir = target_gen_dir @@ -68,7 +69,7 @@ inspector_protocol_generate("protocol_sources") { config_values = [ "protocol.path=$_blink_protocol_path" ] inputs = [ - "$root_gen_dir/blink/core/inspector/protocol.json", + "$root_gen_dir/third_party/blink/renderer/core/inspector/protocol.json", config_file, ] diff --git a/chromium/content/browser/devtools/browser_devtools_agent_host.cc b/chromium/content/browser/devtools/browser_devtools_agent_host.cc index 6475584ed6b..bd0c4705380 100644 --- a/chromium/content/browser/devtools/browser_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/browser_devtools_agent_host.cc @@ -31,7 +31,7 @@ scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::CreateForBrowser( scoped_refptr<DevToolsAgentHost> DevToolsAgentHost::CreateForDiscovery() { CreateServerSocketCallback null_callback; - return new BrowserDevToolsAgentHost(nullptr, null_callback, true); + return new BrowserDevToolsAgentHost(nullptr, std::move(null_callback), true); } BrowserDevToolsAgentHost::BrowserDevToolsAgentHost( @@ -48,12 +48,15 @@ BrowserDevToolsAgentHost::BrowserDevToolsAgentHost( BrowserDevToolsAgentHost::~BrowserDevToolsAgentHost() { } -void BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session) { + if (session->restricted()) + return false; + session->SetBrowserOnly(true); session->AddHandler( base::WrapUnique(new protocol::TargetHandler(true /* browser_only */))); if (only_discovery_) - return; + return true; session->AddHandler(base::WrapUnique(new protocol::BrowserHandler())); session->AddHandler(base::WrapUnique(new protocol::IOHandler( @@ -63,10 +66,9 @@ void BrowserDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(base::WrapUnique(new protocol::SystemInfoHandler())); session->AddHandler(base::WrapUnique(new protocol::TetheringHandler( socket_callback_, tethering_task_runner_))); - session->AddHandler(base::WrapUnique(new protocol::TracingHandler( - protocol::TracingHandler::Browser, - FrameTreeNode::kFrameTreeNodeInvalidId, - GetIOContext()))); + session->AddHandler( + base::WrapUnique(new protocol::TracingHandler(nullptr, GetIOContext()))); + return true; } void BrowserDevToolsAgentHost::DetachSession(DevToolsSession* session) {} diff --git a/chromium/content/browser/devtools/browser_devtools_agent_host.h b/chromium/content/browser/devtools/browser_devtools_agent_host.h index b7576135a7c..7022e2d167c 100644 --- a/chromium/content/browser/devtools/browser_devtools_agent_host.h +++ b/chromium/content/browser/devtools/browser_devtools_agent_host.h @@ -19,7 +19,7 @@ class BrowserDevToolsAgentHost : public DevToolsAgentHostImpl { ~BrowserDevToolsAgentHost() override; // DevToolsAgentHostImpl implementation. - void AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session) override; void DetachSession(DevToolsSession* session) override; void DispatchProtocolMessage(DevToolsSession* session, const std::string& message) override; diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.cc b/chromium/content/browser/devtools/devtools_agent_host_impl.cc index 775bec85a36..7c9b58aa5e1 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.cc +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.cc @@ -176,23 +176,37 @@ DevToolsSession* DevToolsAgentHostImpl::SessionByClient( return it == session_by_client_.end() ? nullptr : it->second.get(); } -void DevToolsAgentHostImpl::InnerAttachClient(DevToolsAgentHostClient* client) { +bool DevToolsAgentHostImpl::InnerAttachClient(DevToolsAgentHostClient* client, + bool restricted) { scoped_refptr<DevToolsAgentHostImpl> protect(this); - DevToolsSession* session = new DevToolsSession(this, client); + DevToolsSession* session = new DevToolsSession(this, client, restricted); sessions_.insert(session); session_by_client_[client].reset(session); - AttachSession(session); + if (!AttachSession(session)) { + sessions_.erase(session); + session_by_client_.erase(client); + return false; + } + if (sessions_.size() == 1) NotifyAttached(); DevToolsManager* manager = DevToolsManager::GetInstance(); if (manager->delegate()) manager->delegate()->ClientAttached(this, client); + return true; } void DevToolsAgentHostImpl::AttachClient(DevToolsAgentHostClient* client) { if (SessionByClient(client)) return; - InnerAttachClient(client); + InnerAttachClient(client, false /* restricted */); +} + +bool DevToolsAgentHostImpl::AttachRestrictedClient( + DevToolsAgentHostClient* client) { + if (SessionByClient(client)) + return false; + return InnerAttachClient(client, true /* restricted */); } void DevToolsAgentHostImpl::ForceAttachClient(DevToolsAgentHostClient* client) { @@ -200,9 +214,9 @@ void DevToolsAgentHostImpl::ForceAttachClient(DevToolsAgentHostClient* client) { return; scoped_refptr<DevToolsAgentHostImpl> protect(this); if (!sessions_.empty()) - ForceDetachAllClients(); + ForceDetachAllSessions(); DCHECK(sessions_.empty()); - InnerAttachClient(client); + InnerAttachClient(client, false /* restricted */); } bool DevToolsAgentHostImpl::DetachClient(DevToolsAgentHostClient* client) { @@ -298,16 +312,34 @@ bool DevToolsAgentHostImpl::Inspect() { return false; } -void DevToolsAgentHostImpl::ForceDetachAllClients() { +void DevToolsAgentHostImpl::ForceDetachAllSessions() { scoped_refptr<DevToolsAgentHostImpl> protect(this); - while (!session_by_client_.empty()) { - DevToolsAgentHostClient* client = session_by_client_.begin()->first; - InnerDetachClient(client); + while (!sessions_.empty()) { + DevToolsAgentHostClient* client = (*sessions_.begin())->client(); + DetachClient(client); client->AgentHostClosed(this); } } -void DevToolsAgentHostImpl::AttachSession(DevToolsSession* session) {} +void DevToolsAgentHostImpl::ForceDetachRestrictedSessions() { + if (sessions_.empty()) + return; + scoped_refptr<DevToolsAgentHostImpl> protect(this); + std::vector<DevToolsSession*> restricted; + for (DevToolsSession* session : sessions_) { + if (session->restricted()) + restricted.push_back(session); + } + for (DevToolsSession* session : restricted) { + DevToolsAgentHostClient* client = session->client(); + DetachClient(client); + client->AgentHostClosed(this); + } +} + +bool DevToolsAgentHostImpl::AttachSession(DevToolsSession* session) { + return false; +} void DevToolsAgentHostImpl::DetachSession(DevToolsSession* session) {} @@ -325,7 +357,7 @@ void DevToolsAgentHost::DetachAllClients() { DevToolsMap copy = g_devtools_instances.Get(); for (DevToolsMap::iterator it(copy.begin()); it != copy.end(); ++it) { DevToolsAgentHostImpl* agent_host = it->second; - agent_host->ForceDetachAllClients(); + agent_host->ForceDetachAllSessions(); } } diff --git a/chromium/content/browser/devtools/devtools_agent_host_impl.h b/chromium/content/browser/devtools/devtools_agent_host_impl.h index 20178938a01..c69bf5a70f2 100644 --- a/chromium/content/browser/devtools/devtools_agent_host_impl.h +++ b/chromium/content/browser/devtools/devtools_agent_host_impl.h @@ -16,7 +16,7 @@ #include "content/common/content_export.h" #include "content/public/browser/certificate_request_result_type.h" #include "content/public/browser/devtools_agent_host.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" namespace content { @@ -37,6 +37,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { // DevToolsAgentHost implementation. void AttachClient(DevToolsAgentHostClient* client) override; + bool AttachRestrictedClient(DevToolsAgentHostClient* client) override; void ForceAttachClient(DevToolsAgentHostClient* client) override; bool DetachClient(DevToolsAgentHostClient* client) override; bool DispatchProtocolMessage(DevToolsAgentHostClient* client, @@ -63,14 +64,16 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { static bool ShouldForceCreation(); - virtual void AttachSession(DevToolsSession* session); + // Returning |false| will block the attach. + virtual bool AttachSession(DevToolsSession* session); virtual void DetachSession(DevToolsSession* session); virtual void DispatchProtocolMessage(DevToolsSession* session, const std::string& message); void NotifyCreated(); void NotifyNavigated(); - void ForceDetachAllClients(); + void ForceDetachAllSessions(); + void ForceDetachRestrictedSessions(); DevToolsIOContext* GetIOContext() { return &io_context_; } base::flat_set<DevToolsSession*>& sessions() { return sessions_; } @@ -78,7 +81,7 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { private: friend class DevToolsAgentHost; // for static methods friend class DevToolsSession; - void InnerAttachClient(DevToolsAgentHostClient* client); + bool InnerAttachClient(DevToolsAgentHostClient* client, bool restricted); void InnerDetachClient(DevToolsAgentHostClient* client); void NotifyAttached(); void NotifyDetached(); diff --git a/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc b/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc deleted file mode 100644 index ab433746a7e..00000000000 --- a/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018 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/devtools/devtools_frame_trace_recorder_for_viz.h" - -#include "cc/paint/skia_paint_canvas.h" -#include "components/viz/host/host_frame_sink_manager.h" -#include "content/browser/compositor/surface_utils.h" -#include "content/browser/devtools/devtools_frame_trace_recorder.h" -#include "content/browser/devtools/devtools_traceable_screenshot.h" -#include "media/renderers/paint_canvas_video_renderer.h" - -namespace content { - -namespace { - -static constexpr gfx::Size kMaxFrameSize = gfx::Size(500, 500); - -} // namespace - -DevToolsFrameTraceRecorderForViz::DevToolsFrameTraceRecorderForViz() - : binding_(this) {} - -DevToolsFrameTraceRecorderForViz::~DevToolsFrameTraceRecorderForViz() = default; - -void DevToolsFrameTraceRecorderForViz::StartCapture() { - // Early out if we're already capturing, - if (video_capturer_) - return; - GetHostFrameSinkManager()->CreateVideoCapturer( - mojo::MakeRequest(&video_capturer_)); - video_capturer_->SetResolutionConstraints(gfx::Size(1, 1), kMaxFrameSize, - false); - video_capturer_->SetMinCapturePeriod(base::TimeDelta::FromMilliseconds(10)); - viz::mojom::FrameSinkVideoConsumerPtr consumer; - binding_.Bind(mojo::MakeRequest(&consumer)); - video_capturer_->ChangeTarget(frame_sink_id_); - video_capturer_->Start(std::move(consumer)); - number_of_screenshots_ = 0; -} - -void DevToolsFrameTraceRecorderForViz::StopCapture() { - if (!video_capturer_) - return; - binding_.Close(); - video_capturer_->Stop(); - video_capturer_.reset(); -} - -void DevToolsFrameTraceRecorderForViz::SetFrameSinkId( - const viz::FrameSinkId& frame_sink_id) { - frame_sink_id_ = frame_sink_id; - if (video_capturer_) - video_capturer_->ChangeTarget(frame_sink_id_); -} - -void DevToolsFrameTraceRecorderForViz::OnFrameCaptured( - mojo::ScopedSharedBufferHandle buffer, - uint32_t buffer_size, - ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, - const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { - if (!buffer.is_valid()) { - callbacks->Done(); - return; - } - - mojo::ScopedSharedBufferMapping mapping = buffer->Map(buffer_size); - if (!mapping) { - DLOG(ERROR) << "Shared memory mapping failed."; - return; - } - - scoped_refptr<media::VideoFrame> frame; - frame = media::VideoFrame::WrapExternalData( - info->pixel_format, info->coded_size, info->visible_rect, - info->visible_rect.size(), static_cast<uint8_t*>(mapping.get()), - buffer_size, info->timestamp); - if (!frame) - return; - frame->AddDestructionObserver(base::BindOnce( - [](mojo::ScopedSharedBufferMapping mapping) {}, std::move(mapping))); - - media::PaintCanvasVideoRenderer renderer; - SkBitmap skbitmap; - skbitmap.allocN32Pixels(info->visible_rect.width(), - info->visible_rect.height()); - cc::SkiaPaintCanvas canvas(skbitmap); - renderer.Copy(frame, &canvas, media::Context3D()); - callbacks->Done(); - - DCHECK(info->metadata); - frame->metadata()->MergeInternalValuesFrom(*info->metadata); - base::TimeTicks reference_time; - const bool had_reference_time = frame->metadata()->GetTimeTicks( - media::VideoFrameMetadata::REFERENCE_TIME, &reference_time); - DCHECK(had_reference_time); - - TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( - TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), "Screenshot", 1, - reference_time, - std::unique_ptr<base::trace_event::ConvertableToTraceFormat>( - new DevToolsTraceableScreenshot(skbitmap))); - - ++number_of_screenshots_; - if (number_of_screenshots_ >= - DevToolsFrameTraceRecorder::kMaximumNumberOfScreenshots) - StopCapture(); -} - -void DevToolsFrameTraceRecorderForViz::OnTargetLost( - const viz::FrameSinkId& frame_sink_id) {} - -void DevToolsFrameTraceRecorderForViz::OnStopped() {} - -} // namespace content diff --git a/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.h b/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.h deleted file mode 100644 index d459e1c139d..00000000000 --- a/chromium/content/browser/devtools/devtools_frame_trace_recorder_for_viz.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 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_DEVTOOLS_DEVTOOLS_FRAME_TRACE_RECORDER_FOR_VIZ_H_ -#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_FRAME_TRACE_RECORDER_FOR_VIZ_H_ - -#include "mojo/public/cpp/bindings/binding.h" -#include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h" - -namespace content { - -// Captures snapshots of the page being traced. Used when the -// VizDisplayCompositor feature is enabled. -// TODO(https://crbug.com/813929): Use this class everywhere even if viz is not -// enabled and remove DevToolsFrameTraceRecorder. -class DevToolsFrameTraceRecorderForViz - : public viz::mojom::FrameSinkVideoConsumer { - public: - DevToolsFrameTraceRecorderForViz(); - ~DevToolsFrameTraceRecorderForViz() override; - - void StartCapture(); - void StopCapture(); - void SetFrameSinkId(const viz::FrameSinkId& frame_sink_id); - - // viz::mojom::FrameSinkVideoConsumer implementation. - void OnFrameCaptured( - mojo::ScopedSharedBufferHandle buffer, - uint32_t buffer_size, - ::media::mojom::VideoFrameInfoPtr info, - const gfx::Rect& update_rect, - const gfx::Rect& content_rect, - viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; - void OnTargetLost(const viz::FrameSinkId& frame_sink_id) override; - void OnStopped() override; - - private: - viz::mojom::FrameSinkVideoCapturerPtr video_capturer_; - mojo::Binding<viz::mojom::FrameSinkVideoConsumer> binding_; - viz::FrameSinkId frame_sink_id_; - int number_of_screenshots_ = 0; - - DISALLOW_COPY_AND_ASSIGN(DevToolsFrameTraceRecorderForViz); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_FRAME_TRACE_RECORDER_FOR_VIZ_H_ diff --git a/chromium/content/browser/devtools/devtools_frontend_host_impl.cc b/chromium/content/browser/devtools/devtools_frontend_host_impl.cc index eacded0190a..8fe9445564e 100644 --- a/chromium/content/browser/devtools/devtools_frontend_host_impl.cc +++ b/chromium/content/browser/devtools/devtools_frontend_host_impl.cc @@ -12,7 +12,7 @@ #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" -#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" namespace content { diff --git a/chromium/content/browser/devtools/devtools_frontend_host_impl.h b/chromium/content/browser/devtools/devtools_frontend_host_impl.h index 0b4275e577e..43f03d2dfb3 100644 --- a/chromium/content/browser/devtools/devtools_frontend_host_impl.h +++ b/chromium/content/browser/devtools/devtools_frontend_host_impl.h @@ -8,7 +8,7 @@ #include "base/macros.h" #include "content/public/browser/devtools_frontend_host.h" #include "mojo/public/cpp/bindings/associated_binding.h" -#include "third_party/WebKit/public/web/devtools_frontend.mojom.h" +#include "third_party/blink/public/web/devtools_frontend.mojom.h" namespace content { diff --git a/chromium/content/browser/devtools/devtools_http_handler.cc b/chromium/content/browser/devtools/devtools_http_handler.cc index 26c4f50fad5..cae7c566146 100644 --- a/chromium/content/browser/devtools/devtools_http_handler.cc +++ b/chromium/content/browser/devtools/devtools_http_handler.cc @@ -369,6 +369,10 @@ static bool TimeComparator(scoped_refptr<DevToolsAgentHost> host1, // DevToolsHttpHandler ------------------------------------------------------- DevToolsHttpHandler::~DevToolsHttpHandler() { + // Disconnecting sessions might lead to the last minute messages generated + // by the targets. It is essential that this happens before we issue delete + // soon for the server wrapper. + connection_to_client_.clear(); TerminateOnUI(std::move(thread_), std::move(server_wrapper_), std::move(socket_factory_)); } @@ -568,8 +572,9 @@ void DevToolsHttpHandler::OnJsonRequest( kTargetWebSocketDebuggerUrlField, base::StringPrintf("ws://%s%s", host.c_str(), browser_guid_.c_str())); #if defined(OS_ANDROID) - version.SetString("Android-Package", - base::android::BuildInfo::GetInstance()->package_name()); + version.SetString( + "Android-Package", + base::android::BuildInfo::GetInstance()->host_package_name()); #endif SendJson(connection_id, net::HTTP_OK, &version, std::string()); return; diff --git a/chromium/content/browser/devtools/devtools_interceptor_controller.cc b/chromium/content/browser/devtools/devtools_interceptor_controller.cc index 77de8fe584f..a2c27cba5df 100644 --- a/chromium/content/browser/devtools/devtools_interceptor_controller.cc +++ b/chromium/content/browser/devtools/devtools_interceptor_controller.cc @@ -4,11 +4,11 @@ #include "content/browser/devtools/devtools_interceptor_controller.h" -#include "base/memory/ptr_util.h" #include "base/supports_user_data.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" namespace content { diff --git a/chromium/content/browser/devtools/devtools_io_context.cc b/chromium/content/browser/devtools/devtools_io_context.cc index 24a756c05ad..1e0ae3d9eee 100644 --- a/chromium/content/browser/devtools/devtools_io_context.cc +++ b/chromium/content/browser/devtools/devtools_io_context.cc @@ -8,7 +8,6 @@ #include "base/containers/queue.h" #include "base/files/file.h" #include "base/files/file_util.h" -#include "base/memory/ptr_util.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" diff --git a/chromium/content/browser/devtools/devtools_manager.cc b/chromium/content/browser/devtools/devtools_manager.cc index 6336ea21da7..241e5039fa9 100644 --- a/chromium/content/browser/devtools/devtools_manager.cc +++ b/chromium/content/browser/devtools/devtools_manager.cc @@ -5,7 +5,6 @@ #include "content/browser/devtools/devtools_manager.h" #include "base/bind.h" -#include "base/memory/ptr_util.h" #include "base/message_loop/message_loop.h" #include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/devtools_http_handler.h" diff --git a/chromium/content/browser/devtools/devtools_network_interceptor.cc b/chromium/content/browser/devtools/devtools_network_interceptor.cc index 812f543da2a..8213acf5d97 100644 --- a/chromium/content/browser/devtools/devtools_network_interceptor.cc +++ b/chromium/content/browser/devtools/devtools_network_interceptor.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "content/browser/devtools/devtools_network_interceptor.h" +#include "base/strings/pattern.h" +#include "content/browser/devtools/protocol/network_handler.h" +#include "url/gurl.h" namespace content { @@ -22,4 +25,50 @@ DevToolsNetworkInterceptor::FilterEntry::FilterEntry( DevToolsNetworkInterceptor::FilterEntry::FilterEntry(FilterEntry&&) {} DevToolsNetworkInterceptor::FilterEntry::~FilterEntry() {} +DevToolsNetworkInterceptor::Modifications::Modifications() + : mark_as_canceled(false) {} + +DevToolsNetworkInterceptor::Modifications::Modifications( + base::Optional<net::Error> error_reason, + base::Optional<std::string> raw_response, + protocol::Maybe<std::string> modified_url, + protocol::Maybe<std::string> modified_method, + protocol::Maybe<std::string> modified_post_data, + protocol::Maybe<protocol::Network::Headers> modified_headers, + protocol::Maybe<protocol::Network::AuthChallengeResponse> + auth_challenge_response, + bool mark_as_canceled) + : error_reason(std::move(error_reason)), + raw_response(std::move(raw_response)), + modified_url(std::move(modified_url)), + modified_method(std::move(modified_method)), + modified_post_data(std::move(modified_post_data)), + modified_headers(std::move(modified_headers)), + auth_challenge_response(std::move(auth_challenge_response)), + mark_as_canceled(mark_as_canceled) {} + +DevToolsNetworkInterceptor::Modifications::~Modifications() {} + +DevToolsNetworkInterceptor::Pattern::~Pattern() = default; + +DevToolsNetworkInterceptor::Pattern::Pattern(const Pattern& other) = default; + +DevToolsNetworkInterceptor::Pattern::Pattern( + const std::string& url_pattern, + base::flat_set<ResourceType> resource_types, + InterceptionStage interception_stage) + : url_pattern(url_pattern), + resource_types(std::move(resource_types)), + interception_stage(interception_stage) {} + +bool DevToolsNetworkInterceptor::Pattern::Matches( + const std::string& url, + ResourceType resource_type) const { + if (!resource_types.empty() && + resource_types.find(resource_type) == resource_types.end()) { + return false; + } + return base::MatchPattern(url, url_pattern); +} + } // namespace content diff --git a/chromium/content/browser/devtools/devtools_network_interceptor.h b/chromium/content/browser/devtools/devtools_network_interceptor.h index 59769bf7e01..97c64bc6257 100644 --- a/chromium/content/browser/devtools/devtools_network_interceptor.h +++ b/chromium/content/browser/devtools/devtools_network_interceptor.h @@ -35,6 +35,8 @@ struct InterceptedRequestInfo { class DevToolsNetworkInterceptor { public: + virtual ~DevToolsNetworkInterceptor() = default; + using RequestInterceptedCallback = base::RepeatingCallback<void(std::unique_ptr<InterceptedRequestInfo>)>; using ContinueInterceptedRequestCallback = @@ -43,6 +45,7 @@ class DevToolsNetworkInterceptor { protocol::Network::Backend::GetResponseBodyForInterceptionCallback; struct Modifications { + Modifications(); Modifications(base::Optional<net::Error> error_reason, base::Optional<std::string> raw_response, protocol::Maybe<std::string> modified_url, @@ -73,25 +76,27 @@ class DevToolsNetworkInterceptor { }; enum InterceptionStage { - REQUEST, - RESPONSE, + DONT_INTERCEPT = 0, + REQUEST = (1 << 0), + RESPONSE = (1 << 1), // Note: Both is not sent from front-end. It is used if both Request // and HeadersReceived was found it upgrades it to Both. - BOTH, - DONT_INTERCEPT + BOTH = (REQUEST | RESPONSE), }; struct Pattern { public: - Pattern(); ~Pattern(); Pattern(const Pattern& other); Pattern(const std::string& url_pattern, base::flat_set<ResourceType> resource_types, InterceptionStage interception_stage); + + bool Matches(const std::string& url, ResourceType resource_type) const; + const std::string url_pattern; const base::flat_set<ResourceType> resource_types; - InterceptionStage interception_stage; + const InterceptionStage interception_stage; }; struct FilterEntry { @@ -121,6 +126,13 @@ class DevToolsNetworkInterceptor { std::unique_ptr<ContinueInterceptedRequestCallback> callback) = 0; }; +inline DevToolsNetworkInterceptor::InterceptionStage& operator|=( + DevToolsNetworkInterceptor::InterceptionStage& a, + const DevToolsNetworkInterceptor::InterceptionStage& b) { + a = static_cast<DevToolsNetworkInterceptor::InterceptionStage>(a | b); + return a; +} + } // namespace content #endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_NETWORK_INTERCEPTOR_H_ diff --git a/chromium/content/browser/devtools/devtools_pipe_handler.cc b/chromium/content/browser/devtools/devtools_pipe_handler.cc index dce8d284663..4fbea246102 100644 --- a/chromium/content/browser/devtools/devtools_pipe_handler.cc +++ b/chromium/content/browser/devtools/devtools_pipe_handler.cc @@ -16,7 +16,6 @@ #include <string> #include "base/bind.h" #include "base/files/file_util.h" -#include "base/memory/ptr_util.h" #include "base/memory/ref_counted_memory.h" #include "base/message_loop/message_loop.h" #include "base/sequenced_task_runner.h" diff --git a/chromium/content/browser/devtools/devtools_session.cc b/chromium/content/browser/devtools/devtools_session.cc index 61d9b790cbc..b31ca0ada8b 100644 --- a/chromium/content/browser/devtools/devtools_session.cc +++ b/chromium/content/browser/devtools/devtools_session.cc @@ -24,16 +24,20 @@ bool ShouldSendOnIO(const std::string& method) { method == "Debugger.setBreakpointByUrl" || method == "Debugger.removeBreakpoint" || method == "Debugger.setBreakpointsActive" || - method == "Performance.getMetrics" || method == "Page.crash"; + method == "Performance.getMetrics" || method == "Page.crash" || + method == "Runtime.terminateExecution" || + method == "Emulation.setScriptExecutionDisabled"; } } // namespace DevToolsSession::DevToolsSession(DevToolsAgentHostImpl* agent_host, - DevToolsAgentHostClient* client) + DevToolsAgentHostClient* client, + bool restricted) : binding_(this), agent_host_(agent_host), client_(client), + restricted_(restricted), process_host_id_(ChildProcessHost::kInvalidUniqueID), host_(nullptr), dispatcher_(new protocol::UberDispatcher(this)), @@ -123,12 +127,6 @@ void DevToolsSession::DispatchProtocolMessage(const std::string& message) { if (delegate->HandleCommand(agent_host_, client_, dict_value)) return; - - if (delegate->HandleAsyncCommand(agent_host_, client_, dict_value, - base::Bind(&DevToolsSession::SendResponse, - weak_factory_.GetWeakPtr()))) { - return; - } } int call_id; diff --git a/chromium/content/browser/devtools/devtools_session.h b/chromium/content/browser/devtools/devtools_session.h index d7dd60140c4..ea900e26bb3 100644 --- a/chromium/content/browser/devtools/devtools_session.h +++ b/chromium/content/browser/devtools/devtools_session.h @@ -14,7 +14,7 @@ #include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "mojo/public/cpp/bindings/associated_binding.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" namespace content { @@ -25,9 +25,13 @@ class DevToolsSession : public protocol::FrontendChannel, public blink::mojom::DevToolsSessionHost { public: DevToolsSession(DevToolsAgentHostImpl* agent_host, - DevToolsAgentHostClient* client); + DevToolsAgentHostClient* client, + bool restricted); ~DevToolsSession() override; + bool restricted() { return restricted_; } + DevToolsAgentHostClient* client() { return client_; }; + // Browser-only sessions do not talk to mojom::DevToolsAgent, but instead // handle all protocol messages locally in the browser process. void SetBrowserOnly(bool browser_only); @@ -83,6 +87,7 @@ class DevToolsSession : public protocol::FrontendChannel, blink::mojom::DevToolsSessionPtr io_session_ptr_; DevToolsAgentHostImpl* agent_host_; DevToolsAgentHostClient* client_; + bool restricted_; bool browser_only_ = false; base::flat_map<std::string, std::unique_ptr<protocol::DevToolsDomainHandler>> handlers_; diff --git a/chromium/content/browser/devtools/devtools_target_registry.cc b/chromium/content/browser/devtools/devtools_target_registry.cc index eefc74d8d0a..a426d9e1924 100644 --- a/chromium/content/browser/devtools/devtools_target_registry.cc +++ b/chromium/content/browser/devtools/devtools_target_registry.cc @@ -9,6 +9,7 @@ #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents_observer.h" namespace content { diff --git a/chromium/content/browser/devtools/devtools_traceable_screenshot.h b/chromium/content/browser/devtools/devtools_traceable_screenshot.h index b9f9605379c..bd6afd2271b 100644 --- a/chromium/content/browser/devtools/devtools_traceable_screenshot.h +++ b/chromium/content/browser/devtools/devtools_traceable_screenshot.h @@ -19,14 +19,14 @@ class DevToolsTraceableScreenshot DevToolsTraceableScreenshot(const SkBitmap& bitmap); + ~DevToolsTraceableScreenshot() override; + // base::trace_event::ConvertableToTraceFormat implementation. void AppendAsTraceFormat(std::string* out) const override; private: static base::subtle::Atomic32 number_of_instances_; - ~DevToolsTraceableScreenshot() override; - SkBitmap frame_; DISALLOW_COPY_AND_ASSIGN(DevToolsTraceableScreenshot); diff --git a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc index 91a151c75ea..3c9d31b784b 100644 --- a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc +++ b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.cc @@ -5,7 +5,6 @@ #include "content/browser/devtools/devtools_url_interceptor_request_job.h" #include "base/base64.h" -#include "base/memory/ptr_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "content/browser/devtools/protocol/network_handler.h" @@ -448,6 +447,8 @@ int DevToolsURLInterceptorRequestJob::MockResponseDetails::ReadRawData( namespace { +using DevToolsStatus = ResourceRequestInfoImpl::DevToolsStatus; + void SendPendingBodyRequestsOnUiThread( std::vector<std::unique_ptr< protocol::Network::Backend::GetResponseBodyForInterceptionCallback>> @@ -522,6 +523,14 @@ std::unique_ptr<net::UploadDataStream> GetUploadData(net::URLRequest* request) { std::move(proxy_readers), 0); } +void SetDevToolsStatus(net::URLRequest* request, + DevToolsStatus devtools_status) { + ResourceRequestInfoImpl* resource_request_info = + ResourceRequestInfoImpl::ForRequest(request); + DCHECK(resource_request_info); + resource_request_info->set_devtools_status(devtools_status); +} + } // namespace DevToolsURLInterceptorRequestJob::DevToolsURLInterceptorRequestJob( @@ -660,10 +669,14 @@ DevToolsURLInterceptorRequestJob::GetHttpResponseHeaders() const { bool DevToolsURLInterceptorRequestJob::GetMimeType( std::string* mime_type) const { + if (sub_request_) { + sub_request_->request()->GetMimeType(mime_type); + return true; + } const net::HttpResponseHeaders* response_headers = GetHttpResponseHeaders(); - if (!response_headers) - return false; - return response_headers->GetMimeType(mime_type); + if (response_headers) + return response_headers->GetMimeType(mime_type); + return false; } bool DevToolsURLInterceptorRequestJob::GetCharset(std::string* charset) { @@ -711,7 +724,7 @@ void DevToolsURLInterceptorRequestJob::OnSubRequestAuthRequired( if (stage_to_intercept_ == InterceptionStage::DONT_INTERCEPT) { // This should trigger default auth behavior. - // See comment in ProcessAuthRespose. + // See comment in ProcessAuthResponse. NotifyHeadersComplete(); return; } @@ -756,6 +769,8 @@ void DevToolsURLInterceptorRequestJob::OnSubRequestRedirectReceived( bool* defer_redirect) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(sub_request_); + SetDevToolsStatus(sub_request_->request(), + DevToolsStatus::kCanceledAsRedirect); // If we're not intercepting results or are a response then cancel this // redirect and tell the parent request it was redirected through |redirect_|. @@ -799,8 +814,10 @@ void DevToolsURLInterceptorRequestJob::OnInterceptedRequestResponseStarted( const net::Error& net_error) { DCHECK_NE(waiting_for_user_response_, WaitingForUserResponse::WAITING_FOR_RESPONSE_ACK); - if (stage_to_intercept_ == InterceptionStage::DONT_INTERCEPT) + if (stage_to_intercept_ == InterceptionStage::DONT_INTERCEPT) { + static_cast<InterceptedRequest*>(sub_request_.get())->FetchResponseBody(); return; + } waiting_for_user_response_ = WaitingForUserResponse::WAITING_FOR_RESPONSE_ACK; std::unique_ptr<InterceptedRequestInfo> request_info = BuildRequestInfo(); @@ -873,7 +890,7 @@ void DevToolsURLInterceptorRequestJob::StopIntercepting() { case WaitingForUserResponse::WAITING_FOR_RESPONSE_ACK: // Fallthough. case WaitingForUserResponse::WAITING_FOR_REQUEST_ACK: - ProcessInterceptionRespose( + ProcessInterceptionResponse( std::make_unique<DevToolsNetworkInterceptor::Modifications>( base::nullopt, base::nullopt, protocol::Maybe<std::string>(), protocol::Maybe<std::string>(), protocol::Maybe<std::string>(), @@ -887,7 +904,7 @@ void DevToolsURLInterceptorRequestJob::StopIntercepting() { .SetResponse(protocol::Network::AuthChallengeResponse:: ResponseEnum::Default) .Build(); - ProcessAuthRespose( + ProcessAuthResponse( std::make_unique<DevToolsNetworkInterceptor::Modifications>( base::nullopt, base::nullopt, protocol::Maybe<std::string>(), protocol::Maybe<std::string>(), protocol::Maybe<std::string>(), @@ -928,7 +945,7 @@ void DevToolsURLInterceptorRequestJob::ContinueInterceptedRequest( "authChallengeResponse not expected."))); break; } - ProcessInterceptionRespose(std::move(modifications)); + ProcessInterceptionResponse(std::move(modifications)); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess, @@ -945,7 +962,7 @@ void DevToolsURLInterceptorRequestJob::ContinueInterceptedRequest( "authChallengeResponse required."))); break; } - if (ProcessAuthRespose(std::move(modifications))) { + if (ProcessAuthResponse(std::move(modifications))) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess, @@ -1025,18 +1042,14 @@ DevToolsURLInterceptorRequestJob::BuildRequestInfo() { return result; } -void DevToolsURLInterceptorRequestJob::ProcessInterceptionRespose( +void DevToolsURLInterceptorRequestJob::ProcessInterceptionResponse( std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modifications) { bool is_response_ack = waiting_for_user_response_ == WaitingForUserResponse::WAITING_FOR_RESPONSE_ACK; waiting_for_user_response_ = WaitingForUserResponse::NOT_WAITING; - if (modifications->mark_as_canceled) { - ResourceRequestInfoImpl* resource_request_info = - ResourceRequestInfoImpl::ForRequest(request()); - DCHECK(resource_request_info); - resource_request_info->set_canceled_by_devtools(true); - } + if (modifications->mark_as_canceled) + SetDevToolsStatus(request(), DevToolsStatus::kCanceled); if (modifications->error_reason) { if (sub_request_) { @@ -1061,6 +1074,10 @@ void DevToolsURLInterceptorRequestJob::ProcessInterceptionRespose( sub_request_->Cancel(); sub_request_.reset(); } + if (response_headers_callback_) { + response_headers_callback_.Run( + mock_response_details_->response_headers()); + } NotifyHeadersComplete(); return; } @@ -1118,11 +1135,17 @@ void DevToolsURLInterceptorRequestJob::ProcessInterceptionRespose( // The reason we start a sub request is because we are in full control of it // and can choose to ignore it if, for example, the fetch encounters a // redirect that the user chooses to replace with a mock response. - sub_request_.reset(new SubRequest(request_details_, this, interceptor_)); + DCHECK(stage_to_intercept_ != InterceptionStage::RESPONSE); + if (stage_to_intercept_ == InterceptionStage::BOTH) { + sub_request_.reset( + new InterceptedRequest(request_details_, this, interceptor_)); + } else { + sub_request_.reset(new SubRequest(request_details_, this, interceptor_)); + } } } -bool DevToolsURLInterceptorRequestJob::ProcessAuthRespose( +bool DevToolsURLInterceptorRequestJob::ProcessAuthResponse( std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modifications) { waiting_for_user_response_ = WaitingForUserResponse::NOT_WAITING; @@ -1150,8 +1173,8 @@ bool DevToolsURLInterceptorRequestJob::ProcessAuthRespose( protocol::Network::AuthChallengeResponse::ResponseEnum:: ProvideCredentials) { SetAuth(net::AuthCredentials( - base::UTF8ToUTF16(auth_challenge_response->GetUsername("").c_str()), - base::UTF8ToUTF16(auth_challenge_response->GetPassword("").c_str()))); + base::UTF8ToUTF16(auth_challenge_response->GetUsername("")), + base::UTF8ToUTF16(auth_challenge_response->GetPassword("")))); return true; } @@ -1160,12 +1183,12 @@ bool DevToolsURLInterceptorRequestJob::ProcessAuthRespose( void DevToolsURLInterceptorRequestJob::SetRequestHeadersCallback( net::RequestHeadersCallback callback) { - request_headers_callback_ = callback; + request_headers_callback_ = std::move(callback); } void DevToolsURLInterceptorRequestJob::SetResponseHeadersCallback( net::ResponseHeadersCallback callback) { - response_headers_callback_ = callback; + response_headers_callback_ = std::move(callback); } DevToolsURLInterceptorRequestJob::RequestDetails::RequestDetails( diff --git a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.h b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.h index 961cf901c6b..48e7a7bde39 100644 --- a/chromium/content/browser/devtools/devtools_url_interceptor_request_job.h +++ b/chromium/content/browser/devtools/devtools_url_interceptor_request_job.h @@ -128,10 +128,10 @@ class DevToolsURLInterceptorRequestJob : public net::URLRequestJob { const net::HttpResponseHeaders* GetHttpResponseHeaders() const; void ProcessRedirect(int status_code, const std::string& new_url); - void ProcessInterceptionRespose( + void ProcessInterceptionResponse( std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modification); - bool ProcessAuthRespose( + bool ProcessAuthResponse( std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modification); enum class WaitingForUserResponse { diff --git a/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc b/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc new file mode 100644 index 00000000000..ced25eb9169 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_url_loader_interceptor.cc @@ -0,0 +1,1170 @@ +// Copyright 2018 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/devtools/devtools_url_loader_interceptor.h" +#include "base/base64.h" +#include "base/no_destructor.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "base/unguessable_token.h" +#include "content/browser/devtools/protocol/network_handler.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/public/browser/browser_thread.h" +#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/system/data_pipe_drainer.h" +#include "net/base/mime_sniffer.h" +#include "net/http/http_util.h" +#include "net/url_request/url_request.h" +#include "services/network/public/cpp/resource_request_body.h" + +namespace content { + +namespace { + +using RequestInterceptedCallback = + DevToolsNetworkInterceptor::RequestInterceptedCallback; +using ContinueInterceptedRequestCallback = + DevToolsNetworkInterceptor::ContinueInterceptedRequestCallback; +using GetResponseBodyForInterceptionCallback = + DevToolsNetworkInterceptor::GetResponseBodyForInterceptionCallback; +using Modifications = DevToolsNetworkInterceptor::Modifications; +using InterceptionStage = DevToolsNetworkInterceptor::InterceptionStage; +using protocol::Response; +using protocol::Network::AuthChallengeResponse; +using GlobalRequestId = std::tuple<int32_t, int32_t, int32_t>; + +struct CreateLoaderParameters { + CreateLoaderParameters( + int32_t routing_id, + int32_t request_id, + uint32_t options, + network::ResourceRequest request, + net::MutableNetworkTrafficAnnotationTag traffic_annotation) + : routing_id(routing_id), + request_id(request_id), + options(options), + request(request), + traffic_annotation(traffic_annotation) {} + + const int32_t routing_id; + const int32_t request_id; + const uint32_t options; + network::ResourceRequest request; + const net::MutableNetworkTrafficAnnotationTag traffic_annotation; +}; + +class BodyReader : public mojo::DataPipeDrainer::Client { + public: + explicit BodyReader(base::OnceClosure download_complete_callback) + : download_complete_callback_(std::move(download_complete_callback)) {} + + void StartReading(mojo::ScopedDataPipeConsumerHandle body); + + void AddCallback( + std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) { + callbacks_.push_back(std::move(callback)); + if (data_complete_) { + DCHECK_EQ(1UL, callbacks_.size()); + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&BodyReader::DispatchBodyOnUI, std::move(callbacks_), + encoded_body_)); + } + } + + bool data_complete() const { return data_complete_; } + const std::string& body() const { return body_; } + + void CancelWithError(std::string error) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&BodyReader::DispatchErrorOnUI, std::move(callbacks_), + std::move(error))); + } + + private: + using CallbackVector = + std::vector<std::unique_ptr<GetResponseBodyForInterceptionCallback>>; + static void DispatchBodyOnUI(const CallbackVector& callbacks, + const std::string& body); + static void DispatchErrorOnUI(const CallbackVector& callbacks, + const std::string& error); + + void OnDataAvailable(const void* data, size_t num_bytes) override { + DCHECK(!data_complete_); + body_.append(std::string(static_cast<const char*>(data), num_bytes)); + } + + void OnDataComplete() override; + + std::unique_ptr<mojo::DataPipeDrainer> body_pipe_drainer_; + CallbackVector callbacks_; + base::OnceClosure download_complete_callback_; + std::string body_; + std::string encoded_body_; + bool data_complete_ = false; +}; + +void BodyReader::StartReading(mojo::ScopedDataPipeConsumerHandle body) { + DCHECK(!callbacks_.empty()); + DCHECK(!body_pipe_drainer_); + DCHECK(!data_complete_); + + body_pipe_drainer_.reset(new mojo::DataPipeDrainer(this, std::move(body))); +} + +void BodyReader::OnDataComplete() { + DCHECK(!data_complete_); + data_complete_ = true; + body_pipe_drainer_.reset(); + // TODO(caseq): only encode if necessary. + base::Base64Encode(body_, &encoded_body_); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::BindOnce(&BodyReader::DispatchBodyOnUI, + std::move(callbacks_), encoded_body_)); + std::move(download_complete_callback_).Run(); +} + +// static +void BodyReader::DispatchBodyOnUI(const CallbackVector& callbacks, + const std::string& encoded_body) { + for (const auto& cb : callbacks) + cb->sendSuccess(encoded_body, true); +} + +// static +void BodyReader::DispatchErrorOnUI(const CallbackVector& callbacks, + const std::string& error) { + for (const auto& cb : callbacks) + cb->sendFailure(Response::Error(error)); +} + +struct ResponseMetadata { + ResponseMetadata() = default; + explicit ResponseMetadata(const network::ResourceResponseHead& head) + : head(head) {} + + network::ResourceResponseHead head; + std::unique_ptr<net::RedirectInfo> redirect_info; + network::mojom::DownloadedTempFilePtr downloaded_file; + std::vector<uint8_t> cached_metadata; + size_t encoded_length = 0; + size_t transfer_size = 0; + network::URLLoaderCompletionStatus status; +}; + +class InterceptionJob : public network::mojom::URLLoaderClient, + public network::mojom::URLLoader { + public: + static InterceptionJob* FindByRequestId( + const GlobalRequestId& global_req_id) { + const auto& map = GetInterceptionJobMap(); + auto it = map.find(global_req_id); + return it == map.end() ? nullptr : it->second; + } + + InterceptionJob(DevToolsURLLoaderInterceptor::Impl* interceptor, + const std::string& id, + const base::UnguessableToken& frame_token, + int32_t process_id, + std::unique_ptr<CreateLoaderParameters> create_loader_params, + network::mojom::URLLoaderRequest loader_request, + network::mojom::URLLoaderClientPtr client, + network::mojom::URLLoaderFactoryPtr target_factory); + + void GetResponseBody( + std::unique_ptr<GetResponseBodyForInterceptionCallback> callback); + void ContinueInterceptedRequest( + std::unique_ptr<Modifications> modifications, + std::unique_ptr<ContinueInterceptedRequestCallback> callback); + void Detach(); + + void OnAuthRequest( + const scoped_refptr<net::AuthChallengeInfo>& auth_info, + DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback); + + private: + static std::map<GlobalRequestId, InterceptionJob*>& GetInterceptionJobMap() { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + static base::NoDestructor<std::map<GlobalRequestId, InterceptionJob*>> inst; + return *inst; + } + + ~InterceptionJob() override { + size_t erased = GetInterceptionJobMap().erase(global_req_id_); + DCHECK_EQ(1lu, erased); + } + + Response InnerContinueRequest(std::unique_ptr<Modifications> modifications); + Response ProcessAuthResponse(AuthChallengeResponse* auth_challenge_response); + Response ProcessResponseOverride(const std::string& response); + Response ProcessRedirectByClient(const std::string& location); + void SendResponse(const base::StringPiece& body); + void ApplyModificationsToRequest( + std::unique_ptr<Modifications> modifications); + + void StartRequest(); + void CancelRequest(); + void Shutdown(); + + std::unique_ptr<InterceptedRequestInfo> BuildRequestInfo( + const network::ResourceResponseHead* head); + void NotifyClient(std::unique_ptr<InterceptedRequestInfo> request_info); + + void ResponseBodyComplete(); + + bool ShouldBypassForResponse() const { + DCHECK_EQ(!!response_metadata_, !!body_reader_); + DCHECK_EQ(state_, State::kResponseReceived); + return !response_metadata_; + } + + // network::mojom::URLLoader methods + void FollowRedirect() override; + void ProceedWithResponse() override; + void SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) override; + void PauseReadingBodyFromNet() override; + void ResumeReadingBodyFromNet() override; + + // network::mojom::URLLoaderClient methods + void OnReceiveResponse( + const network::ResourceResponseHead& head, + network::mojom::DownloadedTempFilePtr downloaded_file) override; + void OnReceiveRedirect(const net::RedirectInfo& redirect_info, + const network::ResourceResponseHead& head) override; + void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override; + void OnUploadProgress(int64_t current_position, + int64_t total_size, + OnUploadProgressCallback callback) override; + void OnReceiveCachedMetadata(const std::vector<uint8_t>& data) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override; + void OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) override; + void OnComplete(const network::URLLoaderCompletionStatus& status) override; + + const std::string id_; + const GlobalRequestId global_req_id_; + const base::UnguessableToken frame_token_; + const base::TimeTicks start_ticks_; + const base::Time start_time_; + const bool report_upload_; + + DevToolsURLLoaderInterceptor::Impl* interceptor_; + InterceptionStage stage_; + + std::unique_ptr<CreateLoaderParameters> create_loader_params_; + + mojo::Binding<network::mojom::URLLoaderClient> client_binding_; + mojo::Binding<network::mojom::URLLoader> loader_binding_; + + network::mojom::URLLoaderClientPtr client_; + network::mojom::URLLoaderPtr loader_; + network::mojom::URLLoaderFactoryPtr target_factory_; + + enum State { + kNotStarted, + kRequestSent, + kRedirectReceived, + kAuthRequired, + kResponseReceived, + }; + + State state_; + bool waiting_for_resolution_; + + std::unique_ptr<BodyReader> body_reader_; + std::unique_ptr<ResponseMetadata> response_metadata_; + + base::Optional<std::pair<net::RequestPriority, int32_t>> priority_; + DevToolsURLLoaderInterceptor::HandleAuthRequestCallback + pending_auth_callback_; + + DISALLOW_COPY_AND_ASSIGN(InterceptionJob); +}; + +} // namespace + +class DevToolsURLLoaderInterceptor::Impl + : public base::SupportsWeakPtr<DevToolsURLLoaderInterceptor::Impl> { + public: + explicit Impl(RequestInterceptedCallback callback) + : request_intercepted_callback_(callback) {} + ~Impl() { + for (auto const& entry : jobs_) + entry.second->Detach(); + } + + void CreateJob(const base::UnguessableToken& frame_token, + int32_t process_id, + std::unique_ptr<CreateLoaderParameters> create_params, + network::mojom::URLLoaderRequest loader_request, + network::mojom::URLLoaderClientPtr client, + network::mojom::URLLoaderFactoryPtr target_factory) { + DCHECK(!frame_token.is_empty()); + + static int last_id = 0; + + std::string id = base::StringPrintf("interception-job-%d", ++last_id); + InterceptionJob* job = + new InterceptionJob(this, id, frame_token, process_id, + std::move(create_params), std::move(loader_request), + std::move(client), std::move(target_factory)); + jobs_.emplace(std::move(id), job); + } + + void SetPatterns(std::vector<DevToolsNetworkInterceptor::Pattern> patterns) { + patterns_ = std::move(patterns); + } + + InterceptionStage GetInterceptionStage(const GURL& url, + ResourceType resource_type) const { + InterceptionStage stage = InterceptionStage::DONT_INTERCEPT; + std::string url_str = protocol::NetworkHandler::ClearUrlRef(url).spec(); + for (const auto& pattern : patterns_) { + if (pattern.Matches(url_str, resource_type)) + stage |= pattern.interception_stage; + } + return stage; + } + + void GetResponseBody( + const std::string& interception_id, + std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) { + if (InterceptionJob* job = FindJob(interception_id, &callback)) + job->GetResponseBody(std::move(callback)); + } + + void ContinueInterceptedRequest( + const std::string& interception_id, + std::unique_ptr<Modifications> modifications, + std::unique_ptr<ContinueInterceptedRequestCallback> callback) { + if (InterceptionJob* job = FindJob(interception_id, &callback)) { + job->ContinueInterceptedRequest(std::move(modifications), + std::move(callback)); + } + } + + private: + friend class content::InterceptionJob; + + template <typename Callback> + InterceptionJob* FindJob(const std::string& id, + std::unique_ptr<Callback>* callback) { + auto it = jobs_.find(id); + if (it != jobs_.end()) + return it->second; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce( + &Callback::sendFailure, std::move(*callback), + protocol::Response::InvalidParams("Invalid InterceptionId."))); + return nullptr; + } + + void RemoveJob(const std::string& id) { jobs_.erase(id); } + + std::map<std::string, InterceptionJob*> jobs_; + RequestInterceptedCallback request_intercepted_callback_; + std::vector<DevToolsNetworkInterceptor::Pattern> patterns_; + + DISALLOW_COPY_AND_ASSIGN(Impl); +}; + +class DevToolsURLLoaderFactoryProxy : public network::mojom::URLLoaderFactory { + public: + DevToolsURLLoaderFactoryProxy( + const base::UnguessableToken& frame_token, + int32_t process_id, + network::mojom::URLLoaderFactoryRequest loader_request, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info, + base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor); + ~DevToolsURLLoaderFactoryProxy() override; + + private: + // network::mojom::URLLoaderFactory implementation + void CreateLoaderAndStart(network::mojom::URLLoaderRequest loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtr client, + const net::MutableNetworkTrafficAnnotationTag& + traffic_annotation) override; + void Clone(network::mojom::URLLoaderFactoryRequest request) override; + + void StartOnIO(network::mojom::URLLoaderFactoryRequest loader_request, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info); + void OnProxyBindingError(); + void OnTargetFactoryError(); + + const base::UnguessableToken frame_token_; + const int32_t process_id_; + + network::mojom::URLLoaderFactoryPtr target_factory_; + base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor_; + mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +DevToolsURLLoaderFactoryProxy::DevToolsURLLoaderFactoryProxy( + const base::UnguessableToken& frame_token, + int32_t process_id, + network::mojom::URLLoaderFactoryRequest loader_request, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info, + base::WeakPtr<DevToolsURLLoaderInterceptor::Impl> interceptor) + : frame_token_(frame_token), + process_id_(process_id), + interceptor_(std::move(interceptor)) { + DETACH_FROM_SEQUENCE(sequence_checker_); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&DevToolsURLLoaderFactoryProxy::StartOnIO, + base::Unretained(this), std::move(loader_request), + std::move(target_factory_info))); +} + +DevToolsURLLoaderFactoryProxy::~DevToolsURLLoaderFactoryProxy() {} + +void DevToolsURLLoaderFactoryProxy::CreateLoaderAndStart( + network::mojom::URLLoaderRequest loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + network::mojom::URLLoaderClientPtr client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + DevToolsURLLoaderInterceptor::Impl* interceptor = interceptor_.get(); + if (!interceptor_) { + target_factory_->CreateLoaderAndStart( + std::move(loader), routing_id, request_id, options, request, + std::move(client), traffic_annotation); + return; + } + auto creation_params = std::make_unique<CreateLoaderParameters>( + routing_id, request_id, options, request, traffic_annotation); + network::mojom::URLLoaderFactoryPtr factory_clone; + target_factory_->Clone(MakeRequest(&factory_clone)); + interceptor->CreateJob(frame_token_, process_id_, std::move(creation_params), + std::move(loader), std::move(client), + std::move(factory_clone)); +} + +void DevToolsURLLoaderFactoryProxy::StartOnIO( + network::mojom::URLLoaderFactoryRequest loader_request, + network::mojom::URLLoaderFactoryPtrInfo target_factory_info) { + target_factory_.Bind(std::move(target_factory_info)); + target_factory_.set_connection_error_handler( + base::BindOnce(&DevToolsURLLoaderFactoryProxy::OnTargetFactoryError, + base::Unretained(this))); + + bindings_.AddBinding(this, std::move(loader_request)); + bindings_.set_connection_error_handler( + base::BindRepeating(&DevToolsURLLoaderFactoryProxy::OnProxyBindingError, + base::Unretained(this))); +} + +void DevToolsURLLoaderFactoryProxy::Clone( + network::mojom::URLLoaderFactoryRequest request) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + bindings_.AddBinding(this, std::move(request)); +} + +void DevToolsURLLoaderFactoryProxy::OnTargetFactoryError() { + DCHECK(!target_factory_.is_bound()); + delete this; +} + +void DevToolsURLLoaderFactoryProxy::OnProxyBindingError() { + if (bindings_.empty()) + delete this; +} + +// static +void DevToolsURLLoaderInterceptor::HandleAuthRequest( + int32_t process_id, + int32_t routing_id, + int32_t request_id, + const scoped_refptr<net::AuthChallengeInfo>& auth_info, + HandleAuthRequestCallback callback) { + GlobalRequestId req_id = std::make_tuple(process_id, routing_id, request_id); + if (auto* job = InterceptionJob::FindByRequestId(req_id)) + job->OnAuthRequest(auth_info, std::move(callback)); + else + std::move(callback).Run(true, base::nullopt); +} + +DevToolsURLLoaderInterceptor::DevToolsURLLoaderInterceptor( + FrameTreeNode* const local_root, + RequestInterceptedCallback callback) + : local_root_(local_root), + enabled_(false), + impl_(new DevToolsURLLoaderInterceptor::Impl(std::move(callback)), + base::OnTaskRunnerDeleter( + BrowserThread::GetTaskRunnerForThread(BrowserThread::IO))), + weak_impl_(impl_->AsWeakPtr()) {} + +DevToolsURLLoaderInterceptor::~DevToolsURLLoaderInterceptor() { + UpdateSubresourceLoaderFactories(); +}; + +void DevToolsURLLoaderInterceptor::UpdateSubresourceLoaderFactories() { + base::queue<FrameTreeNode*> queue; + queue.push(local_root_); + while (!queue.empty()) { + FrameTreeNode* node = queue.front(); + queue.pop(); + RenderFrameHostImpl* host = node->current_frame_host(); + if (node != local_root_ && host->IsCrossProcessSubframe()) + continue; + host->UpdateSubresourceLoaderFactories(); + for (size_t i = 0; i < node->child_count(); ++i) + queue.push(node->child_at(i)); + } +} + +void DevToolsURLLoaderInterceptor::SetPatterns( + std::vector<DevToolsNetworkInterceptor::Pattern> patterns) { + if (enabled_ != !!patterns.size()) { + enabled_ = !!patterns.size(); + UpdateSubresourceLoaderFactories(); + } + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&Impl::SetPatterns, base::Unretained(impl_.get()), + std::move(patterns))); +} + +void DevToolsURLLoaderInterceptor::GetResponseBody( + const std::string& interception_id, + std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&Impl::GetResponseBody, base::Unretained(impl_.get()), + interception_id, std::move(callback))); +} + +void DevToolsURLLoaderInterceptor::ContinueInterceptedRequest( + const std::string& interception_id, + std::unique_ptr<Modifications> modifications, + std::unique_ptr<ContinueInterceptedRequestCallback> callback) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce(&Impl::ContinueInterceptedRequest, + base::Unretained(impl_.get()), interception_id, + std::move(modifications), std::move(callback))); +} + +bool DevToolsURLLoaderInterceptor::CreateProxyForInterception( + const base::UnguessableToken frame_token, + int process_id, + network::mojom::URLLoaderFactoryRequest* request) const { + if (!enabled_) + return false; + network::mojom::URLLoaderFactoryRequest original_request = + std::move(*request); + network::mojom::URLLoaderFactoryPtrInfo target_ptr_info; + *request = MakeRequest(&target_ptr_info); + + new DevToolsURLLoaderFactoryProxy(frame_token, process_id, + std::move(original_request), + std::move(target_ptr_info), weak_impl_); + return true; +} + +InterceptionJob::InterceptionJob( + DevToolsURLLoaderInterceptor::Impl* interceptor, + const std::string& id, + const base::UnguessableToken& frame_token, + int process_id, + std::unique_ptr<CreateLoaderParameters> create_loader_params, + network::mojom::URLLoaderRequest loader_request, + network::mojom::URLLoaderClientPtr client, + network::mojom::URLLoaderFactoryPtr target_factory) + : id_(std::move(id)), + global_req_id_( + std::make_tuple(process_id, + create_loader_params->request.render_frame_id, + create_loader_params->request_id)), + frame_token_(frame_token), + start_ticks_(base::TimeTicks::Now()), + start_time_(base::Time::Now()), + report_upload_(!!create_loader_params->request.request_body), + interceptor_(interceptor), + create_loader_params_(std::move(create_loader_params)), + client_binding_(this), + loader_binding_(this), + client_(std::move(client)), + target_factory_(std::move(target_factory)), + state_(kNotStarted), + waiting_for_resolution_(false) { + const network::ResourceRequest& request = create_loader_params_->request; + stage_ = interceptor_->GetInterceptionStage( + request.url, static_cast<ResourceType>(request.resource_type)); + + loader_binding_.Bind(std::move(loader_request)); + loader_binding_.set_connection_error_handler( + base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this))); + + auto& job_map = GetInterceptionJobMap(); + bool inserted = job_map.emplace(global_req_id_, this).second; + DCHECK(inserted); + + if (stage_ & InterceptionStage::REQUEST) { + NotifyClient(BuildRequestInfo(nullptr)); + return; + } + + StartRequest(); +} + +void InterceptionJob::GetResponseBody( + std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) { + std::string error_reason; + + if (!(stage_ & InterceptionStage::RESPONSE)) { + error_reason = + "Can only get response body on HeadersReceived pattern matched " + "requests."; + } else if (state_ != kResponseReceived) { + DCHECK(waiting_for_resolution_); + error_reason = + "Can only get response body on requests captured after headers " + "received."; + } + if (!error_reason.empty()) { + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&GetResponseBodyForInterceptionCallback::sendFailure, + std::move(callback), + Response::Error(std::move(error_reason)))); + return; + } + + if (!body_reader_) { + body_reader_ = std::make_unique<BodyReader>(base::BindOnce( + &InterceptionJob::ResponseBodyComplete, base::Unretained(this))); + client_binding_.ResumeIncomingMethodCallProcessing(); + loader_->ResumeReadingBodyFromNet(); + } + body_reader_->AddCallback(std::move(callback)); +} + +void InterceptionJob::ContinueInterceptedRequest( + std::unique_ptr<Modifications> modifications, + std::unique_ptr<ContinueInterceptedRequestCallback> callback) { + Response response = InnerContinueRequest(std::move(modifications)); + // |this| may be destroyed at this pont. + bool success = response.isSuccess(); + base::OnceClosure task = + success ? base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess, + std::move(callback)) + : base::BindOnce(&ContinueInterceptedRequestCallback::sendFailure, + std::move(callback), std::move(response)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(task)); +} + +void InterceptionJob::Detach() { + stage_ = InterceptionStage::DONT_INTERCEPT; + interceptor_ = nullptr; + if (!waiting_for_resolution_) + return; + if (state_ == State::kAuthRequired) { + state_ = State::kRequestSent; + waiting_for_resolution_ = false; + std::move(pending_auth_callback_).Run(true, base::nullopt); + return; + } + InnerContinueRequest(std::make_unique<Modifications>()); +} + +Response InterceptionJob::InnerContinueRequest( + std::unique_ptr<Modifications> modifications) { + if (!waiting_for_resolution_) + return Response::Error("Invalid state for continueInterceptedRequest"); + waiting_for_resolution_ = false; + + if (state_ == State::kAuthRequired) { + if (!modifications->auth_challenge_response.isJust()) + return Response::InvalidParams("authChallengeResponse required."); + return ProcessAuthResponse( + modifications->auth_challenge_response.fromJust()); + } else if (modifications->auth_challenge_response.isJust()) { + return Response::InvalidParams("authChallengeResponse not expected."); + } + + if (modifications->mark_as_canceled || modifications->error_reason) { + int error = modifications->error_reason + ? *modifications->error_reason + : (modifications->mark_as_canceled ? net::ERR_ABORTED + : net::ERR_FAILED); + network::URLLoaderCompletionStatus status(error); + status.completion_time = base::TimeTicks::Now(); + client_->OnComplete(status); + Shutdown(); + return Response::OK(); + } + + if (modifications->raw_response) + return ProcessResponseOverride(*modifications->raw_response); + + if (state_ == State::kRedirectReceived) { + // TODO(caseq): report error if other modifications are present. + if (modifications->modified_url.isJust()) { + std::string location = modifications->modified_url.fromJust(); + CancelRequest(); + auto* headers = response_metadata_->head.headers.get(); + headers->RemoveHeader("location"); + headers->AddHeader("location: " + location); + return ProcessRedirectByClient(location); + } + client_->OnReceiveRedirect(*response_metadata_->redirect_info, + response_metadata_->head); + return Response::OK(); + } + + if (body_reader_) { + if (body_reader_->data_complete()) + SendResponse(body_reader_->body()); + + // There are read callbacks pending, so let the reader do its job and come + // back when it's done. + return Response::OK(); + } + + if (response_metadata_) { + // TODO(caseq): report error if other modifications are present. + DCHECK_EQ(State::kResponseReceived, state_); + DCHECK(!body_reader_); + client_->OnReceiveResponse(response_metadata_->head, + std::move(response_metadata_->downloaded_file)); + response_metadata_.reset(); + loader_->ResumeReadingBodyFromNet(); + client_binding_.ResumeIncomingMethodCallProcessing(); + return Response::OK(); + } + + DCHECK_EQ(State::kNotStarted, state_); + ApplyModificationsToRequest(std::move(modifications)); + StartRequest(); + return Response::OK(); +} + +void InterceptionJob::ApplyModificationsToRequest( + std::unique_ptr<Modifications> modifications) { + network::ResourceRequest* request = &create_loader_params_->request; + + // Note this redirect is not visible to the page by design. If they want a + // visible redirect they can mock a response with a 302. + if (modifications->modified_url.isJust()) + request->url = GURL(modifications->modified_url.fromJust()); + + if (modifications->modified_method.isJust()) + request->method = modifications->modified_method.fromJust(); + + if (modifications->modified_post_data.isJust()) { + const std::string& post_data = modifications->modified_post_data.fromJust(); + request->request_body = network::ResourceRequestBody::CreateFromBytes( + post_data.data(), post_data.size()); + } + + if (modifications->modified_headers.isJust()) { + request->headers.Clear(); + std::unique_ptr<protocol::DictionaryValue> headers = + modifications->modified_headers.fromJust()->toValue(); + for (size_t i = 0; i < headers->size(); i++) { + protocol::DictionaryValue::Entry entry = headers->at(i); + std::string value; + if (!entry.second->asString(&value)) + continue; + if (base::EqualsCaseInsensitiveASCII(entry.first, + net::HttpRequestHeaders::kReferer)) { + request->referrer = GURL(value); + request->referrer_policy = net::URLRequest::NEVER_CLEAR_REFERRER; + } else { + request->headers.SetHeader(entry.first, value); + } + } + } +} + +Response InterceptionJob::ProcessAuthResponse( + AuthChallengeResponse* auth_challenge_response) { + std::string response = auth_challenge_response->GetResponse(); + state_ = State::kRequestSent; + if (response == AuthChallengeResponse::ResponseEnum::Default) { + std::move(pending_auth_callback_).Run(true, base::nullopt); + } else if (response == AuthChallengeResponse::ResponseEnum::CancelAuth) { + std::move(pending_auth_callback_).Run(false, base::nullopt); + } else if (response == + AuthChallengeResponse::ResponseEnum::ProvideCredentials) { + net::AuthCredentials credentials( + base::UTF8ToUTF16(auth_challenge_response->GetUsername("")), + base::UTF8ToUTF16(auth_challenge_response->GetPassword(""))); + std::move(pending_auth_callback_).Run(false, std::move(credentials)); + } else { + return Response::InvalidParams("Unrecognized authChallengeResponse."); + } + + return Response::OK(); +} + +Response InterceptionJob::ProcessResponseOverride(const std::string& response) { + CancelRequest(); + + std::string raw_headers; + int header_size = + net::HttpUtil::LocateEndOfHeaders(response.c_str(), response.size()); + if (header_size == -1) { + LOG(WARNING) << "Can't find headers in result"; + header_size = 0; + } else { + raw_headers = + net::HttpUtil::AssembleRawHeaders(response.c_str(), header_size); + } + CHECK_LE(static_cast<size_t>(header_size), response.size()); + size_t body_size = response.size() - header_size; + + response_metadata_ = std::make_unique<ResponseMetadata>(); + network::ResourceResponseHead* head = &response_metadata_->head; + + head->request_time = start_time_; + head->response_time = base::Time::Now(); + head->headers = new net::HttpResponseHeaders(std::move(raw_headers)); + head->headers->GetMimeTypeAndCharset(&head->mime_type, &head->charset); + if (head->mime_type.empty()) { + net::SniffMimeType(response.data() + header_size, body_size, + create_loader_params_->request.url, "", + net::ForceSniffFileUrlsForHtml::kDisabled, + &head->mime_type); + } + head->content_length = body_size; + head->encoded_data_length = header_size; + head->encoded_body_length = 0; + head->request_start = start_ticks_; + head->response_start = base::TimeTicks::Now(); + + std::string location_url; + if (head->headers->IsRedirect(&location_url)) + return ProcessRedirectByClient(location_url); + + response_metadata_->transfer_size = body_size; + + response_metadata_->status.completion_time = base::TimeTicks::Now(); + response_metadata_->status.encoded_data_length = response.size(); + response_metadata_->status.encoded_body_length = body_size; + response_metadata_->status.decoded_body_length = body_size; + + SendResponse(base::StringPiece(response.data() + header_size, body_size)); + return Response::OK(); +} + +Response InterceptionJob::ProcessRedirectByClient(const std::string& location) { + GURL redirect_url = create_loader_params_->request.url.Resolve(location); + + if (!redirect_url.is_valid()) + return Response::Error("Invalid redirect URL in overriden headers"); + + const net::HttpResponseHeaders& headers = *response_metadata_->head.headers; + const network::ResourceRequest& request = create_loader_params_->request; + + auto first_party_url_policy = + request.update_first_party_url_on_redirect + ? net::URLRequest::FirstPartyURLPolicy:: + UPDATE_FIRST_PARTY_URL_ON_REDIRECT + : net::URLRequest::FirstPartyURLPolicy::NEVER_CHANGE_FIRST_PARTY_URL; + + response_metadata_->redirect_info = std::make_unique<net::RedirectInfo>( + net::RedirectInfo::ComputeRedirectInfo( + request.method, request.url, request.site_for_cookies, + first_party_url_policy, request.referrer_policy, + request.referrer.spec(), &headers, headers.response_code(), + redirect_url, false /* token_binding_negotiated */, + false /* copy_fragment */)); + + client_->OnReceiveRedirect(*response_metadata_->redirect_info, + response_metadata_->head); + return Response::OK(); +} + +void InterceptionJob::SendResponse(const base::StringPiece& body) { + client_->OnReceiveResponse(response_metadata_->head, + std::move(response_metadata_->downloaded_file)); + + // We shouldn't be able to transfer a string that big over the protocol, + // but just in case... + DCHECK_LE(body.size(), UINT32_MAX) + << "Response bodies larger than " << UINT32_MAX << " are not supported"; + mojo::DataPipe pipe(body.size()); + uint32_t num_bytes = body.size(); + MojoResult res = pipe.producer_handle->WriteData(body.data(), &num_bytes, + MOJO_WRITE_DATA_FLAG_NONE); + DCHECK_EQ(0u, res); + DCHECK_EQ(num_bytes, body.size()); + + if (!response_metadata_->cached_metadata.empty()) + client_->OnReceiveCachedMetadata(response_metadata_->cached_metadata); + client_->OnStartLoadingResponseBody(std::move(pipe.consumer_handle)); + if (response_metadata_->transfer_size) + client_->OnTransferSizeUpdated(response_metadata_->transfer_size); + + client_->OnComplete(response_metadata_->status); + Shutdown(); +} + +void InterceptionJob::ResponseBodyComplete() { + if (waiting_for_resolution_) + return; + // We're here only if client has already told us to proceed with unmodified + // response. + SendResponse(body_reader_->body()); +} + +void InterceptionJob::StartRequest() { + DCHECK_EQ(State::kNotStarted, state_); + DCHECK(!response_metadata_); + + state_ = State::kRequestSent; + + network::mojom::URLLoaderClientPtr loader_client; + client_binding_.Bind(MakeRequest(&loader_client)); + client_binding_.set_connection_error_handler( + base::BindOnce(&InterceptionJob::Shutdown, base::Unretained(this))); + + target_factory_->CreateLoaderAndStart( + MakeRequest(&loader_), create_loader_params_->routing_id, + create_loader_params_->request_id, create_loader_params_->options, + create_loader_params_->request, std::move(loader_client), + create_loader_params_->traffic_annotation); + + if (priority_) + loader_->SetPriority(priority_->first, priority_->second); +} + +void InterceptionJob::CancelRequest() { + if (state_ == State::kNotStarted) + return; + client_binding_.Close(); + loader_.reset(); + if (body_reader_) { + body_reader_->CancelWithError( + "Another command has cancelled the fetch request"); + body_reader_.reset(); + } + state_ = State::kNotStarted; +} + +std::unique_ptr<InterceptedRequestInfo> InterceptionJob::BuildRequestInfo( + const network::ResourceResponseHead* head) { + auto result = std::make_unique<InterceptedRequestInfo>(); + result->interception_id = id_; + result->network_request = + protocol::NetworkHandler::CreateRequestFromResourceRequest( + create_loader_params_->request); + result->frame_id = frame_token_; + ResourceType resource_type = + static_cast<ResourceType>(create_loader_params_->request.resource_type); + result->resource_type = resource_type; + result->is_navigation = resource_type == RESOURCE_TYPE_MAIN_FRAME || + resource_type == RESOURCE_TYPE_SUB_FRAME; + + // TODO(caseq): merge with NetworkHandler::BuildResponse() + if (head && head->headers) { + result->http_response_status_code = head->headers->response_code(); + auto headers_dict = protocol::DictionaryValue::create(); + size_t iter = 0; + std::string name; + std::string value; + while (head->headers->EnumerateHeaderLines(&iter, &name, &value)) { + std::string old_value; + bool merge_with_another = headers_dict->getString(name, &old_value); + headers_dict->setString( + name, merge_with_another ? old_value + '\n' + value : value); + } + result->response_headers = + protocol::Object::fromValue(headers_dict.get(), nullptr); + } + return result; +} + +void InterceptionJob::NotifyClient( + std::unique_ptr<InterceptedRequestInfo> request_info) { + waiting_for_resolution_ = true; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(interceptor_->request_intercepted_callback_, + std::move(request_info))); +} + +void InterceptionJob::Shutdown() { + if (interceptor_) + interceptor_->RemoveJob(id_); + delete this; +} + +// URLLoader methods +void InterceptionJob::FollowRedirect() { + DCHECK(!waiting_for_resolution_); + + network::ResourceRequest* request = &create_loader_params_->request; + const net::RedirectInfo& info = *response_metadata_->redirect_info; + request->method = info.new_method; + request->url = info.new_url; + request->site_for_cookies = info.new_site_for_cookies; + request->referrer_policy = info.new_referrer_policy; + request->referrer = GURL(info.new_referrer); + response_metadata_.reset(); + if (interceptor_) { + stage_ = interceptor_->GetInterceptionStage( + request->url, static_cast<ResourceType>(request->resource_type)); + } + if (state_ == State::kRedirectReceived) { + state_ = State::kRequestSent; + loader_->FollowRedirect(); + return; + } + + DCHECK_EQ(State::kNotStarted, state_); + StartRequest(); +} + +void InterceptionJob::ProceedWithResponse() { + NOTREACHED(); +} + +void InterceptionJob::SetPriority(net::RequestPriority priority, + int32_t intra_priority_value) { + priority_ = std::make_pair(priority, intra_priority_value); + + if (state_ != State::kNotStarted) + loader_->SetPriority(priority, intra_priority_value); +} + +void InterceptionJob::PauseReadingBodyFromNet() { + if (state_ != State::kNotStarted && !body_reader_) + loader_->PauseReadingBodyFromNet(); +} + +void InterceptionJob::ResumeReadingBodyFromNet() { + if (state_ != State::kNotStarted && !body_reader_) + loader_->ResumeReadingBodyFromNet(); +} + +// URLLoaderClient methods +void InterceptionJob::OnReceiveResponse( + const network::ResourceResponseHead& head, + network::mojom::DownloadedTempFilePtr downloaded_file) { + state_ = State::kResponseReceived; + DCHECK(!response_metadata_); + if (!(stage_ & InterceptionStage::RESPONSE)) { + client_->OnReceiveResponse(head, std::move(downloaded_file)); + return; + } + loader_->PauseReadingBodyFromNet(); + client_binding_.PauseIncomingMethodCallProcessing(); + + response_metadata_ = std::make_unique<ResponseMetadata>(head); + response_metadata_->downloaded_file = std::move(downloaded_file); + + NotifyClient(BuildRequestInfo(&head)); +} + +void InterceptionJob::OnReceiveRedirect( + const net::RedirectInfo& redirect_info, + const network::ResourceResponseHead& head) { + DCHECK_EQ(State::kRequestSent, state_); + state_ = State::kRedirectReceived; + response_metadata_ = std::make_unique<ResponseMetadata>(head); + response_metadata_->redirect_info = + std::make_unique<net::RedirectInfo>(redirect_info); + + if (!(stage_ & InterceptionStage::REQUEST)) { + client_->OnReceiveRedirect(redirect_info, head); + return; + } + + std::unique_ptr<InterceptedRequestInfo> request_info = + BuildRequestInfo(&head); + request_info->http_response_status_code = redirect_info.status_code; + request_info->redirect_url = redirect_info.new_url.spec(); + NotifyClient(std::move(request_info)); +} + +void InterceptionJob::OnDataDownloaded(int64_t data_length, + int64_t encoded_length) { + if (ShouldBypassForResponse()) + client_->OnDataDownloaded(data_length, encoded_length); +} + +void InterceptionJob::OnUploadProgress(int64_t current_position, + int64_t total_size, + OnUploadProgressCallback callback) { + if (!report_upload_) + return; + client_->OnUploadProgress(current_position, total_size, std::move(callback)); +} + +void InterceptionJob::OnReceiveCachedMetadata( + const std::vector<uint8_t>& data) { + if (ShouldBypassForResponse()) + client_->OnReceiveCachedMetadata(data); + else + response_metadata_->cached_metadata = data; +} + +void InterceptionJob::OnTransferSizeUpdated(int32_t transfer_size_diff) { + if (ShouldBypassForResponse()) + client_->OnTransferSizeUpdated(transfer_size_diff); + else + response_metadata_->transfer_size += transfer_size_diff; +} + +void InterceptionJob::OnStartLoadingResponseBody( + mojo::ScopedDataPipeConsumerHandle body) { + if (ShouldBypassForResponse()) + client_->OnStartLoadingResponseBody(std::move(body)); + else + body_reader_->StartReading(std::move(body)); +} + +void InterceptionJob::OnComplete( + const network::URLLoaderCompletionStatus& status) { + if (ShouldBypassForResponse()) { + client_->OnComplete(status); + Shutdown(); + return; + } + response_metadata_->status = status; +} + +void InterceptionJob::OnAuthRequest( + const scoped_refptr<net::AuthChallengeInfo>& auth_info, + DevToolsURLLoaderInterceptor::HandleAuthRequestCallback callback) { + DCHECK_EQ(kRequestSent, state_); + DCHECK(pending_auth_callback_.is_null()); + DCHECK(!waiting_for_resolution_); + + if (!(stage_ & InterceptionStage::REQUEST)) { + std::move(callback).Run(true, base::nullopt); + return; + } + state_ = State::kAuthRequired; + auto request_info = BuildRequestInfo(nullptr); + request_info->auth_challenge = + protocol::Network::AuthChallenge::Create() + .SetSource(auth_info->is_proxy + ? protocol::Network::AuthChallenge::SourceEnum::Proxy + : protocol::Network::AuthChallenge::SourceEnum::Server) + .SetOrigin(auth_info->challenger.Serialize()) + .SetScheme(auth_info->scheme) + .SetRealm(auth_info->realm) + .Build(); + pending_auth_callback_ = std::move(callback); + NotifyClient(std::move(request_info)); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_url_loader_interceptor.h b/chromium/content/browser/devtools/devtools_url_loader_interceptor.h new file mode 100644 index 00000000000..a05c8bc1f56 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_url_loader_interceptor.h @@ -0,0 +1,68 @@ +// Copyright 2018 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_DEVTOOLS_DEVTOOLS_URL_LOADER_INTERCEPTOR_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_URL_LOADER_INTERCEPTOR_H_ + +#include "base/memory/weak_ptr.h" +#include "content/browser/devtools/devtools_network_interceptor.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +namespace content { + +class FrameTreeNode; + +class DevToolsURLLoaderInterceptor { + public: + class Impl; + + using HandleAuthRequestCallback = + base::OnceCallback<void(bool use_fallback, + const base::Optional<net::AuthCredentials>&)>; + // Can only be called on the IO thread. + static void HandleAuthRequest( + int32_t process_id, + int32_t routing_id, + int32_t request_id, + const scoped_refptr<net::AuthChallengeInfo>& auth_info, + HandleAuthRequestCallback callback); + + DevToolsURLLoaderInterceptor( + FrameTreeNode* local_root, + DevToolsNetworkInterceptor::RequestInterceptedCallback callback); + ~DevToolsURLLoaderInterceptor(); + + void SetPatterns(std::vector<DevToolsNetworkInterceptor::Pattern> patterns); + + void GetResponseBody( + const std::string& interception_id, + std::unique_ptr< + DevToolsNetworkInterceptor::GetResponseBodyForInterceptionCallback> + callback); + void ContinueInterceptedRequest( + const std::string& interception_id, + std::unique_ptr<DevToolsNetworkInterceptor::Modifications> modifications, + std::unique_ptr< + DevToolsNetworkInterceptor::ContinueInterceptedRequestCallback> + callback); + + bool CreateProxyForInterception( + const base::UnguessableToken frame_token, + int process_id, // 0 for navigation + network::mojom::URLLoaderFactoryRequest* request) const; + + private: + void UpdateSubresourceLoaderFactories(); + + FrameTreeNode* const local_root_; + bool enabled_; + std::unique_ptr<Impl, base::OnTaskRunnerDeleter> impl_; + base::WeakPtr<Impl> weak_impl_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsURLLoaderInterceptor); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_URL_LOADER_INTERCEPTOR_H_ diff --git a/chromium/content/browser/devtools/devtools_url_request_interceptor.cc b/chromium/content/browser/devtools/devtools_url_request_interceptor.cc index 3f361e8e0c5..260a5206a1f 100644 --- a/chromium/content/browser/devtools/devtools_url_request_interceptor.cc +++ b/chromium/content/browser/devtools/devtools_url_request_interceptor.cc @@ -4,7 +4,6 @@ #include "content/browser/devtools/devtools_url_request_interceptor.h" -#include "base/memory/ptr_util.h" #include "base/strings/pattern.h" #include "base/strings/stringprintf.h" #include "content/browser/devtools/devtools_interceptor_controller.h" @@ -42,20 +41,6 @@ DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() { // DevToolsURLRequestInterceptorUserData explicitly. } -DevToolsURLRequestInterceptor::Pattern::Pattern() = default; - -DevToolsURLRequestInterceptor::Pattern::~Pattern() = default; - -DevToolsURLRequestInterceptor::Pattern::Pattern(const Pattern& other) = default; - -DevToolsURLRequestInterceptor::Pattern::Pattern( - const std::string& url_pattern, - base::flat_set<ResourceType> resource_types, - InterceptionStage interception_stage) - : url_pattern(url_pattern), - resource_types(std::move(resource_types)), - interception_stage(interception_stage) {} - const DevToolsTargetRegistry::TargetInfo* DevToolsURLRequestInterceptor::TargetInfoForRequestInfo( const ResourceRequestInfo* request_info) const { @@ -318,25 +303,4 @@ void DevToolsURLRequestInterceptor::JobFinished( controller_, interception_id)); } -DevToolsURLRequestInterceptor::Modifications::Modifications( - base::Optional<net::Error> error_reason, - base::Optional<std::string> raw_response, - protocol::Maybe<std::string> modified_url, - protocol::Maybe<std::string> modified_method, - protocol::Maybe<std::string> modified_post_data, - protocol::Maybe<protocol::Network::Headers> modified_headers, - protocol::Maybe<protocol::Network::AuthChallengeResponse> - auth_challenge_response, - bool mark_as_canceled) - : error_reason(std::move(error_reason)), - raw_response(std::move(raw_response)), - modified_url(std::move(modified_url)), - modified_method(std::move(modified_method)), - modified_post_data(std::move(modified_post_data)), - modified_headers(std::move(modified_headers)), - auth_challenge_response(std::move(auth_challenge_response)), - mark_as_canceled(mark_as_canceled) {} - -DevToolsURLRequestInterceptor::Modifications::~Modifications() {} - } // namespace content diff --git a/chromium/content/browser/devtools/devtools_video_consumer.cc b/chromium/content/browser/devtools/devtools_video_consumer.cc new file mode 100644 index 00000000000..8c9bdd44ade --- /dev/null +++ b/chromium/content/browser/devtools/devtools_video_consumer.cc @@ -0,0 +1,169 @@ +// Copyright 2018 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/devtools/devtools_video_consumer.h" + +#include "cc/paint/skia_paint_canvas.h" +#include "components/viz/host/host_frame_sink_manager.h" +#include "components/viz/service/frame_sinks/video_capture/frame_sink_video_capturer_impl.h" +#include "content/browser/compositor/surface_utils.h" +#include "media/base/limits.h" +#include "media/renderers/paint_canvas_video_renderer.h" + +namespace content { + +namespace { + +// Frame capture period is 10 frames per second by default. +constexpr base::TimeDelta kDefaultMinCapturePeriod = + base::TimeDelta::FromMilliseconds(100); + +// Frame size can change every frame. +constexpr base::TimeDelta kDefaultMinPeriod = base::TimeDelta(); + +// Allow variable aspect ratio. +const bool kDefaultUseFixedAspectRatio = false; + +} // namespace + +// static +constexpr gfx::Size DevToolsVideoConsumer::kDefaultMinFrameSize; + +// static +constexpr gfx::Size DevToolsVideoConsumer::kDefaultMaxFrameSize; + +DevToolsVideoConsumer::DevToolsVideoConsumer(OnFrameCapturedCallback callback) + : callback_(std::move(callback)), + min_capture_period_(kDefaultMinCapturePeriod), + min_frame_size_(kDefaultMinFrameSize), + max_frame_size_(kDefaultMaxFrameSize), + binding_(this) {} + +DevToolsVideoConsumer::~DevToolsVideoConsumer() = default; + +// static +SkBitmap DevToolsVideoConsumer::GetSkBitmapFromFrame( + scoped_refptr<media::VideoFrame> frame) { + media::PaintCanvasVideoRenderer renderer; + SkBitmap skbitmap; + skbitmap.allocN32Pixels(frame->visible_rect().width(), + frame->visible_rect().height()); + cc::SkiaPaintCanvas canvas(skbitmap); + renderer.Copy(frame, &canvas, media::Context3D()); + return skbitmap; +} + +void DevToolsVideoConsumer::StartCapture() { + if (capturer_) + return; + InnerStartCapture(CreateCapturer()); +} + +void DevToolsVideoConsumer::StopCapture() { + if (!capturer_) + return; + binding_.Close(); + capturer_->Stop(); + capturer_.reset(); +} + +void DevToolsVideoConsumer::SetFrameSinkId( + const viz::FrameSinkId& frame_sink_id) { + frame_sink_id_ = frame_sink_id; + if (capturer_) + capturer_->ChangeTarget(frame_sink_id_); +} + +void DevToolsVideoConsumer::SetMinCapturePeriod( + base::TimeDelta min_capture_period) { + min_capture_period_ = min_capture_period; + if (capturer_) + capturer_->SetMinCapturePeriod(min_capture_period_); +} + +void DevToolsVideoConsumer::SetMinAndMaxFrameSize(gfx::Size min_frame_size, + gfx::Size max_frame_size) { + DCHECK(IsValidMinAndMaxFrameSize(min_frame_size, max_frame_size)); + min_frame_size_ = min_frame_size; + max_frame_size_ = max_frame_size; + if (capturer_) { + capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_, + kDefaultUseFixedAspectRatio); + } +} + +viz::mojom::FrameSinkVideoCapturerPtrInfo +DevToolsVideoConsumer::CreateCapturer() { + viz::HostFrameSinkManager* const manager = GetHostFrameSinkManager(); + viz::mojom::FrameSinkVideoCapturerPtr capturer; + manager->CreateVideoCapturer(mojo::MakeRequest(&capturer)); + return capturer.PassInterface(); +} + +void DevToolsVideoConsumer::InnerStartCapture( + viz::mojom::FrameSinkVideoCapturerPtrInfo capturer_info) { + capturer_.Bind(std::move(capturer_info)); + + // Give |capturer_| the capture parameters. + capturer_->SetMinCapturePeriod(min_capture_period_); + capturer_->SetMinSizeChangePeriod(kDefaultMinPeriod); + capturer_->SetResolutionConstraints(min_frame_size_, max_frame_size_, + kDefaultUseFixedAspectRatio); + capturer_->ChangeTarget(frame_sink_id_); + + viz::mojom::FrameSinkVideoConsumerPtr consumer; + binding_.Bind(mojo::MakeRequest(&consumer)); + capturer_->Start(std::move(consumer)); +} + +bool DevToolsVideoConsumer::IsValidMinAndMaxFrameSize( + gfx::Size min_frame_size, + gfx::Size max_frame_size) { + // Returns true if + // 0 < |min_frame_size| <= |max_frame_size| <= media::limits::kMaxDimension. + return 0 < min_frame_size.width() && 0 < min_frame_size.height() && + min_frame_size.width() <= max_frame_size.width() && + min_frame_size.height() <= max_frame_size.height() && + max_frame_size.width() <= media::limits::kMaxDimension && + max_frame_size.height() <= media::limits::kMaxDimension; +} + +void DevToolsVideoConsumer::OnFrameCaptured( + mojo::ScopedSharedBufferHandle buffer, + uint32_t buffer_size, + ::media::mojom::VideoFrameInfoPtr info, + const gfx::Rect& update_rect, + const gfx::Rect& content_rect, + viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) { + if (!buffer.is_valid()) + return; + + mojo::ScopedSharedBufferMapping mapping = buffer->Map(buffer_size); + if (!mapping) { + DLOG(ERROR) << "Shared memory mapping failed."; + return; + } + + scoped_refptr<media::VideoFrame> frame; + // Setting |frame|'s visible rect equal to |content_rect| so that only the + // portion of the frame that contain content are used. + frame = media::VideoFrame::WrapExternalData( + info->pixel_format, info->coded_size, info->visible_rect, + info->visible_rect.size(), static_cast<uint8_t*>(mapping.get()), + buffer_size, info->timestamp); + if (!frame) + return; + frame->AddDestructionObserver(base::BindOnce( + [](mojo::ScopedSharedBufferMapping mapping) {}, std::move(mapping))); + frame->metadata()->MergeInternalValuesFrom(info->metadata); + + callback_.Run(std::move(frame)); +} + +void DevToolsVideoConsumer::OnTargetLost( + const viz::FrameSinkId& frame_sink_id) {} + +void DevToolsVideoConsumer::OnStopped() {} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_video_consumer.h b/chromium/content/browser/devtools/devtools_video_consumer.h new file mode 100644 index 00000000000..fc9bbdba359 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_video_consumer.h @@ -0,0 +1,104 @@ +// Copyright 2018 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_DEVTOOLS_DEVTOOLS_VIDEO_CONSUMER_H_ +#define CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_VIDEO_CONSUMER_H_ + +#include "base/time/time.h" +#include "content/common/content_export.h" +#include "mojo/public/cpp/bindings/binding.h" +#include "services/viz/privileged/interfaces/compositing/frame_sink_video_capture.mojom.h" +#include "ui/gfx/geometry/size.h" + +class SkBitmap; + +namespace content { + +// This class is the video consumer to FrameSinkVideoCapturerImpl. This class, +// in turn sends video frames to its host via the OnFrameCapturedCallback. Used +// when the VizDisplayCompositor feature is enabled. +// TODO(https://crbug.com/813929): Use this class everywhere even if viz is not +// enabled. +class CONTENT_EXPORT DevToolsVideoConsumer + : public viz::mojom::FrameSinkVideoConsumer { + public: + using OnFrameCapturedCallback = + base::RepeatingCallback<void(scoped_refptr<media::VideoFrame> frame)>; + + explicit DevToolsVideoConsumer(OnFrameCapturedCallback callback); + ~DevToolsVideoConsumer() override; + + // Copies |frame| onto a SkBitmap and returns it. + static SkBitmap GetSkBitmapFromFrame(scoped_refptr<media::VideoFrame> frame); + + // If not currently capturing, this creates the capturer and starts capturing. + void StartCapture(); + + // Closes |binding_|. Stops capturing and resets |capturer_|. + void StopCapture(); + + // These functions cache the values passed to them and if we're currently + // capturing, they call the corresponding |capturer_| functions. + // TODO(samans): Add a SetFormat function here so that ARGB pixel format can + // be used. + void SetFrameSinkId(const viz::FrameSinkId& frame_sink_id); + void SetMinCapturePeriod(base::TimeDelta min_capture_period); + void SetMinAndMaxFrameSize(gfx::Size min_frame_size, + gfx::Size max_frame_size); + + private: + friend class DevToolsVideoConsumerTest; + // Sets up a mojo message pipe and requests the HostFrameSinkManager create a + // new capturer instance bound to it. Returns the client-side interface. + viz::mojom::FrameSinkVideoCapturerPtrInfo CreateCapturer(); + + // Binds |capturer_info| to the |capturer_|, sets capture parameters, and + // starts capture. Normally, CreateCapturer produces the |capturer_info|, but + // unittests can provide a mock. + void InnerStartCapture( + viz::mojom::FrameSinkVideoCapturerPtrInfo capturer_info); + + // Checks that |min_frame_size| and |max_frame_size| are in the expected + // range. Limits are specified in media::limits. + bool IsValidMinAndMaxFrameSize(gfx::Size min_frame_size, + gfx::Size max_frame_size); + + // viz::mojom::FrameSinkVideoConsumer: + void OnFrameCaptured( + mojo::ScopedSharedBufferHandle buffer, + uint32_t buffer_size, + ::media::mojom::VideoFrameInfoPtr info, + const gfx::Rect& update_rect, + const gfx::Rect& content_rect, + viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks) override; + void OnTargetLost(const viz::FrameSinkId& frame_sink_id) override; + void OnStopped() override; + + // Default min frame size is 1x1, as otherwise, nothing would be captured. + static constexpr gfx::Size kDefaultMinFrameSize = gfx::Size(1, 1); + + // Using an arbitrary default max frame size of 500x500. + static constexpr gfx::Size kDefaultMaxFrameSize = gfx::Size(500, 500); + + // Callback that is run when a frame is received. + const OnFrameCapturedCallback callback_; + + // Capture parameters. + base::TimeDelta min_capture_period_; + gfx::Size min_frame_size_; + gfx::Size max_frame_size_; + viz::FrameSinkId frame_sink_id_; + + // Mojo pointer to the viz::FrameSinkVideoCapturer instance. If |capturer_| + // is alive, then we are currently capturing. + viz::mojom::FrameSinkVideoCapturerPtr capturer_; + + mojo::Binding<viz::mojom::FrameSinkVideoConsumer> binding_; + + DISALLOW_COPY_AND_ASSIGN(DevToolsVideoConsumer); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_DEVTOOLS_VIDEO_CONSUMER_H_ diff --git a/chromium/content/browser/devtools/devtools_video_consumer_browsertest.cc b/chromium/content/browser/devtools/devtools_video_consumer_browsertest.cc new file mode 100644 index 00000000000..789594ed607 --- /dev/null +++ b/chromium/content/browser/devtools/devtools_video_consumer_browsertest.cc @@ -0,0 +1,128 @@ +// Copyright 2018 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 "content/browser/devtools/devtools_video_consumer.h" +#include "content/browser/web_contents/web_contents_impl.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/controllable_http_response.h" + +namespace { + +// Helper to verify that |frame| dimensions are in the expected ranges. +bool VerifyFrameSize(scoped_refptr<media::VideoFrame> frame, + gfx::Size min_frame_size, + gfx::Size max_frame_size) { + // Returns true if |min_frame_size| <= |frame| <= |max_frame_size|. + return min_frame_size.width() <= frame->visible_rect().width() && + min_frame_size.height() <= frame->visible_rect().height() && + frame->visible_rect().width() <= max_frame_size.width() && + frame->visible_rect().height() <= max_frame_size.height(); +} + +// The default video pixel format. +// TODO(sundarrajs): Add |FrameSinkVideoCapturerImpl::kDefaultPixelFormat| to +// frame_sink_video_capture.mojom and use that instead of +// |kVideoCapturerDefaultPixelFormat|. +constexpr media::VideoPixelFormat kVideoCapturerDefaultPixelFormat = + media::PIXEL_FORMAT_I420; + +} // namespace + +namespace content { + +class DevToolsVideoConsumerTest : public ContentBrowserTest { + public: + DevToolsVideoConsumerTest() {} + + void SetUpOnMainThread() override { + consumer_ = std::make_unique<DevToolsVideoConsumer>(base::BindRepeating( + &DevToolsVideoConsumerTest::OnFrameFromVideoConsumer, + base::Unretained(this))); + consumer_->SetFrameSinkId( + static_cast<content::WebContentsImpl*>(shell()->web_contents()) + ->GetRenderViewHost() + ->GetWidget() + ->GetFrameSinkId()); + } + + scoped_refptr<media::VideoFrame> GetFrame(int i) { return frames_[i]; } + + size_t NumberOfFramesReceived() { return frames_.size(); } + + void OnFrameFromVideoConsumer(scoped_refptr<media::VideoFrame> frame) { + run_loop_->Quit(); + frames_.push_back(std::move(frame)); + } + + void WaitUntilFrameReceived() { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } + + static gfx::Size GetVideoConsumerDefaultMinFrameSize() { + return DevToolsVideoConsumer::kDefaultMinFrameSize; + } + + static gfx::Size GetVideoConsumerDefaultMaxFrameSize() { + return DevToolsVideoConsumer::kDefaultMaxFrameSize; + } + + protected: + std::unique_ptr<DevToolsVideoConsumer> consumer_; + + private: + std::vector<scoped_refptr<media::VideoFrame>> frames_; + std::unique_ptr<base::RunLoop> run_loop_; +}; + +// Tests that setting new frame dimensions via SetMinAndMaxFrameSizes +// produces frames of the new dimensions. +IN_PROC_BROWSER_TEST_F(DevToolsVideoConsumerTest, + SetMinAndMaxFramesChangesDimensions) { + ASSERT_TRUE(embedded_test_server()->Start()); + + // Complete navigation to a page and then start capture. Since navigation is + // complete before capturing, we only expect the refresh frame from the + // StartCapture call. Wait for this refresh frame and verify its info. + NavigateToURLBlockUntilNavigationsComplete( + shell(), embedded_test_server()->GetURL("/devtools/navigation.html"), 1); + consumer_->StartCapture(); + WaitUntilFrameReceived(); + size_t cur_frame_index = 0; + size_t num_frames_received = 1; + EXPECT_EQ(num_frames_received, NumberOfFramesReceived()); + scoped_refptr<media::VideoFrame> frame = GetFrame(cur_frame_index); + EXPECT_EQ(frame->format(), kVideoCapturerDefaultPixelFormat); + EXPECT_TRUE(VerifyFrameSize(frame, GetVideoConsumerDefaultMinFrameSize(), + GetVideoConsumerDefaultMaxFrameSize())); + + // Setting |kNewMinFrameSize| > |kDefaultMaxFrameSize| to disambiguate + // frames with older vs. newer dimensions. + const gfx::Size kNewMinFrameSize = + gfx::Size(GetVideoConsumerDefaultMaxFrameSize().width() + 1, + GetVideoConsumerDefaultMaxFrameSize().height() + 1); + // |kNewMaxFrameSize| only needs to be >= |kNewMinFrameSize|. + const gfx::Size kNewMaxFrameSize = gfx::Size(kNewMinFrameSize.width() + 500, + kNewMinFrameSize.height() + 500); + // Set the min and max frame sizes. This will call + // FrameSinkVideoCapturer::SetResolutionConstraints which triggers sending a + // refresh frame. Wait for this refresh frame and verify it's info. + consumer_->SetMinAndMaxFrameSize(kNewMinFrameSize, kNewMaxFrameSize); + WaitUntilFrameReceived(); + EXPECT_EQ(++num_frames_received, NumberOfFramesReceived()); + frame = GetFrame(++cur_frame_index); + EXPECT_EQ(frame->format(), kVideoCapturerDefaultPixelFormat); + EXPECT_TRUE(VerifyFrameSize(frame, kNewMinFrameSize, kNewMaxFrameSize)); + + // Stop capturing. + consumer_->StopCapture(); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc b/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc new file mode 100644 index 00000000000..adb95d4bbab --- /dev/null +++ b/chromium/content/browser/devtools/devtools_video_consumer_unittest.cc @@ -0,0 +1,394 @@ +// Copyright 2018 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 "base/message_loop/message_loop.h" +#include "content/browser/devtools/devtools_video_consumer.h" +#include "content/public/test/test_utils.h" +#include "media/base/limits.h" +#include "testing/gmock/include/gmock/gmock.h" + +using testing::_; + +namespace content { +namespace { + +// Capture parameters. +constexpr gfx::Size kResolution = gfx::Size(320, 180); // Arbitrarily chosen. +constexpr media::VideoPixelFormat kFormat = media::PIXEL_FORMAT_I420; +constexpr media::VideoPixelStorage kStorage = media::VideoPixelStorage::CPU; + +// A non-zero FrameSinkId to prevent validation errors when +// DevToolsVideoConsumer::ChangeTarget(viz::FrameSinkId) is called +// (which eventually fails in FrameSinkVideoCapturerStubDispatch::Accept). +constexpr viz::FrameSinkId kInitialFrameSinkId = viz::FrameSinkId(1, 1); + +} // namespace + +// Mock for the FrameSinkVideoCapturer running in the VIZ process. +class MockFrameSinkVideoCapturer : public viz::mojom::FrameSinkVideoCapturer { + public: + MockFrameSinkVideoCapturer() : binding_(this) {} + + bool is_bound() const { return binding_.is_bound(); } + + void Bind(viz::mojom::FrameSinkVideoCapturerRequest request) { + DCHECK(!binding_.is_bound()); + binding_.Bind(std::move(request)); + } + + // This is never called. + MOCK_METHOD2(SetFormat, + void(media::VideoPixelFormat format, + media::ColorSpace color_space)); + void SetMinCapturePeriod(base::TimeDelta min_capture_period) final { + min_capture_period_ = min_capture_period; + MockSetMinCapturePeriod(min_capture_period_); + } + MOCK_METHOD1(MockSetMinCapturePeriod, + void(base::TimeDelta min_capture_period)); + void SetMinSizeChangePeriod(base::TimeDelta min_period) final { + min_period_ = min_period; + MockSetMinSizeChangePeriod(min_period_); + } + MOCK_METHOD1(MockSetMinSizeChangePeriod, void(base::TimeDelta min_period)); + void SetResolutionConstraints(const gfx::Size& min_frame_size, + const gfx::Size& max_frame_size, + bool use_fixed_aspect_ratio) final { + min_frame_size_ = min_frame_size; + max_frame_size_ = max_frame_size; + MockSetResolutionConstraints(min_frame_size_, max_frame_size_, true); + } + MOCK_METHOD3(MockSetResolutionConstraints, + void(const gfx::Size& min_frame_size, + const gfx::Size& max_frame_size, + bool use_fixed_aspect_ratio)); + // This is never called. + MOCK_METHOD1(SetAutoThrottlingEnabled, void(bool)); + void ChangeTarget(const viz::FrameSinkId& frame_sink_id) final { + frame_sink_id_ = frame_sink_id; + MockChangeTarget(frame_sink_id_); + } + MOCK_METHOD1(MockChangeTarget, void(const viz::FrameSinkId& frame_sink_id)); + void Start(viz::mojom::FrameSinkVideoConsumerPtr consumer) final { + DCHECK(!consumer_); + consumer_ = std::move(consumer); + MockStart(consumer_.get()); + } + MOCK_METHOD1(MockStart, void(viz::mojom::FrameSinkVideoConsumer* consumer)); + void Stop() final { + binding_.Close(); + consumer_.reset(); + MockStop(); + } + MOCK_METHOD0(MockStop, void()); + MOCK_METHOD0(RequestRefreshFrame, void()); + + // Const accessors to get the cached variables. + base::TimeDelta min_capture_period() const { return min_capture_period_; } + base::TimeDelta min_period() const { return min_period_; } + gfx::Size min_frame_size() const { return min_frame_size_; } + gfx::Size max_frame_size() const { return max_frame_size_; } + viz::FrameSinkId frame_sink_id() const { return frame_sink_id_; } + + private: + // These variables are cached when they are received from + // DevToolsVideoConsumer. + base::TimeDelta min_capture_period_; + base::TimeDelta min_period_; + gfx::Size min_frame_size_; + gfx::Size max_frame_size_; + viz::FrameSinkId frame_sink_id_; + viz::mojom::FrameSinkVideoConsumerPtr consumer_; + + mojo::Binding<viz::mojom::FrameSinkVideoCapturer> binding_; +}; + +// Represents the FrameSinkVideoConsumerFrameCallbacks instance in the VIZ +// process. +class MockFrameSinkVideoConsumerFrameCallbacks + : public viz::mojom::FrameSinkVideoConsumerFrameCallbacks { + public: + MockFrameSinkVideoConsumerFrameCallbacks() : binding_(this) {} + + void Bind(viz::mojom::FrameSinkVideoConsumerFrameCallbacksRequest request) { + binding_.Bind(std::move(request)); + } + + MOCK_METHOD0(Done, void()); + MOCK_METHOD1(ProvideFeedback, void(double utilization)); + + private: + mojo::Binding<viz::mojom::FrameSinkVideoConsumerFrameCallbacks> binding_; +}; + +// Mock for the classes like TracingHandler that receive frames from +// DevToolsVideoConsumer via the OnFrameCapturedCallback. +class MockDevToolsVideoFrameReceiver { + public: + MOCK_METHOD1(OnFrameFromVideoConsumerMock, + void(scoped_refptr<media::VideoFrame> frame)); + + MockDevToolsVideoFrameReceiver() : weak_factory_(this) {} + + scoped_refptr<media::VideoFrame> TakeFrameAt(int i) { + return std::move(frames_[i]); + } + + void OnFrameFromVideoConsumer(scoped_refptr<media::VideoFrame> frame) { + OnFrameFromVideoConsumerMock(frame); + frames_.push_back(std::move(frame)); + } + + std::unique_ptr<DevToolsVideoConsumer> CreateDevToolsVideoConsumer() { + return std::make_unique<DevToolsVideoConsumer>(base::BindRepeating( + &MockDevToolsVideoFrameReceiver::OnFrameFromVideoConsumer, + weak_factory_.GetWeakPtr())); + } + + private: + std::vector<scoped_refptr<media::VideoFrame>> frames_; + base::WeakPtrFactory<MockDevToolsVideoFrameReceiver> weak_factory_; +}; + +class DevToolsVideoConsumerTest : public testing::Test { + public: + DevToolsVideoConsumerTest() : weak_factory_(this) {} + + void SetUp() override { + consumer_ = receiver_.CreateDevToolsVideoConsumer(); + + consumer_->SetFrameSinkId(kInitialFrameSinkId); + } + + void SimulateFrameCapture(mojo::ScopedSharedBufferHandle buffer, + uint32_t buffer_size) { + viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr callbacks_ptr; + callbacks.Bind(mojo::MakeRequest(&callbacks_ptr)); + + media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New( + base::TimeDelta(), base::Value(base::Value::Type::DICTIONARY), kFormat, + kStorage, kResolution, gfx::Rect(kResolution)); + + consumer_->OnFrameCaptured(std::move(buffer), buffer_size, std::move(info), + gfx::Rect(kResolution), gfx::Rect(kResolution), + std::move(callbacks_ptr)); + } + + void StartCaptureWithMockCapturer() { + consumer_->InnerStartCapture(BindMockCapturer()); + } + + bool IsValidMinAndMaxFrameSize(gfx::Size min_frame_size, + gfx::Size max_frame_size) { + return consumer_->IsValidMinAndMaxFrameSize(min_frame_size, max_frame_size); + } + + static gfx::Size GetVideoConsumerDefaultMinFrameSize() { + return DevToolsVideoConsumer::kDefaultMinFrameSize; + } + + static gfx::Size GetVideoConsumerDefaultMaxFrameSize() { + return DevToolsVideoConsumer::kDefaultMaxFrameSize; + } + + // Getters for |consumer_|'s private variables. + base::TimeDelta GetMinCapturePeriod() const { + return consumer_->min_capture_period_; + } + gfx::Size GetMinFrameSize() const { return consumer_->min_frame_size_; } + gfx::Size GetMaxFrameSize() const { return consumer_->max_frame_size_; } + viz::FrameSinkId GetFrameSinkId() const { return consumer_->frame_sink_id_; } + + protected: + MockFrameSinkVideoCapturer capturer_; + MockFrameSinkVideoConsumerFrameCallbacks callbacks; + MockDevToolsVideoFrameReceiver receiver_; + std::unique_ptr<DevToolsVideoConsumer> consumer_; + + private: + viz::mojom::FrameSinkVideoCapturerPtrInfo BindMockCapturer() { + viz::mojom::FrameSinkVideoCapturerPtr capturer_ptr; + capturer_.Bind(mojo::MakeRequest(&capturer_ptr)); + return capturer_ptr.PassInterface(); + } + + base::MessageLoop message_loop_; + base::WeakPtrFactory<DevToolsVideoConsumerTest> weak_factory_; +}; + +// Tests that the OnFrameFromVideoConsumer callbacks is called when +// OnFrameCaptured is passed a valid buffer with valid mapping. +TEST_F(DevToolsVideoConsumerTest, CallbacksAreCalledWhenBufferValid) { + // Create a valid buffer. + const size_t buffer_size = + media::VideoFrame::AllocationSize(kFormat, kResolution); + mojo::ScopedSharedBufferHandle buffer = + mojo::SharedBufferHandle::Create(buffer_size); + + // On valid buffer the |receiver_| gets a frame via OnFrameFromVideoConsumer. + EXPECT_CALL(receiver_, OnFrameFromVideoConsumerMock(_)).Times(1); + + SimulateFrameCapture(std::move(buffer), buffer_size); + base::RunLoop().RunUntilIdle(); +} + +// Tests that only the OnFrameFromVideoConsumer callback is not called when +// OnFrameCaptured is passed an invalid buffer. +TEST_F(DevToolsVideoConsumerTest, OnFrameCapturedExitEarlyOnInvalidBuffer) { + // Create an invalid buffer. + const size_t buffer_size = 0; + mojo::ScopedSharedBufferHandle buffer = + mojo::SharedBufferHandle::Create(buffer_size); + + // On invalid buffer, the |receiver_| doesn't get a frame. + EXPECT_CALL(receiver_, OnFrameFromVideoConsumerMock(_)).Times(0); + + SimulateFrameCapture(std::move(buffer), buffer_size); + base::RunLoop().RunUntilIdle(); +} + +// Tests that the OnFrameFromVideoConsumer callback is not called when +// OnFrameCaptured is passed a buffer with invalid mapping. +TEST_F(DevToolsVideoConsumerTest, OnFrameCapturedExitsOnInvalidMapping) { + // Create a valid buffer, but change buffer_size to simulate an invalid + // mapping. + size_t buffer_size = media::VideoFrame::AllocationSize(kFormat, kResolution); + mojo::ScopedSharedBufferHandle buffer = + mojo::SharedBufferHandle::Create(buffer_size); + buffer_size = 0; + + // On invalid mapping, the |receiver_| doesn't get a frame. + EXPECT_CALL(receiver_, OnFrameFromVideoConsumerMock(_)).Times(0); + + SimulateFrameCapture(std::move(buffer), buffer_size); + base::RunLoop().RunUntilIdle(); +} + +// Tests that starting capture calls |capturer_| functions, and capture can be +// restarted. This test is important as it ensures that when restarting capture, +// a FrameSinkVideoCapturerPtrInfo is bound to |capturer_| and it verifies that +// resources used in the previous StartCapture aren't reused. +TEST_F(DevToolsVideoConsumerTest, StartCaptureCallsSetFunctions) { + // Starting capture should call these |capturer_| functions once. + EXPECT_CALL(capturer_, MockSetMinCapturePeriod(_)); + EXPECT_CALL(capturer_, MockSetMinSizeChangePeriod(_)); + EXPECT_CALL(capturer_, MockSetResolutionConstraints(_, _, _)); + EXPECT_CALL(capturer_, MockChangeTarget(_)); + EXPECT_CALL(capturer_, MockStart(_)); + StartCaptureWithMockCapturer(); + base::RunLoop().RunUntilIdle(); + + // Stop capturing. + EXPECT_CALL(capturer_, MockStop()); + consumer_->StopCapture(); + base::RunLoop().RunUntilIdle(); + + // Start capturing again, and expect that these |capturer_| functions are + // called once. This will re-bind the |capturer_| and ensures that destroyed + // resources aren't being reused. + EXPECT_CALL(capturer_, MockSetMinCapturePeriod(_)); + EXPECT_CALL(capturer_, MockSetMinSizeChangePeriod(_)); + EXPECT_CALL(capturer_, MockSetResolutionConstraints(_, _, _)); + EXPECT_CALL(capturer_, MockChangeTarget(_)); + EXPECT_CALL(capturer_, MockStart(_)); + StartCaptureWithMockCapturer(); + base::RunLoop().RunUntilIdle(); +} + +// Tests that calling 'Set' functions in DevToolsVideoConsumer before +// |capturer_| is initialized results in the passed values being cached. +// When capture is later started (and |capturer_| initialized), these cached +// values should be used and sent to the |capturer_|. +TEST_F(DevToolsVideoConsumerTest, CapturerIsPassedCachedValues) { + // These values are chosen so that they are valid, and different from + // the default values in DevToolsVideoConsumer. + constexpr base::TimeDelta kNewMinCapturePeriod = base::TimeDelta(); + const gfx::Size kNewMinFrameSize = + gfx::Size(GetVideoConsumerDefaultMinFrameSize().width() + 1, + GetVideoConsumerDefaultMinFrameSize().height() + 1); + const gfx::Size kNewMaxFrameSize = + gfx::Size(GetVideoConsumerDefaultMaxFrameSize().width() + 1, + GetVideoConsumerDefaultMaxFrameSize().width() + 1); + constexpr viz::FrameSinkId kNewFrameSinkId = viz::FrameSinkId(2, 2); + + // Right now, |capturer_| has not been created via StartCapture, so + // calling these functions should not call the |capturer_|, but the + // values that are passed in should be cached. + EXPECT_CALL(capturer_, MockSetMinCapturePeriod(_)).Times(0); + EXPECT_CALL(capturer_, MockSetMinSizeChangePeriod(_)).Times(0); + EXPECT_CALL(capturer_, MockSetResolutionConstraints(_, _, _)).Times(0); + EXPECT_CALL(capturer_, MockChangeTarget(_)).Times(0); + EXPECT_CALL(capturer_, MockStart(_)).Times(0); + consumer_->SetMinCapturePeriod(kNewMinCapturePeriod); + consumer_->SetMinAndMaxFrameSize(kNewMinFrameSize, kNewMaxFrameSize); + consumer_->SetFrameSinkId(kNewFrameSinkId); + base::RunLoop().RunUntilIdle(); + // Verify that new values are cached. + EXPECT_EQ(GetMinCapturePeriod(), kNewMinCapturePeriod); + EXPECT_EQ(GetMinFrameSize(), kNewMinFrameSize); + EXPECT_EQ(GetMaxFrameSize(), kNewMaxFrameSize); + EXPECT_EQ(GetFrameSinkId(), kNewFrameSinkId); + + // Starting capture now, will result in the cached values being sent to + // |capturer_|. So, expect that these calls are made and verify the values. + EXPECT_CALL(capturer_, MockSetMinCapturePeriod(_)); + EXPECT_CALL(capturer_, MockSetMinSizeChangePeriod(_)); + EXPECT_CALL(capturer_, MockSetResolutionConstraints(_, _, _)); + EXPECT_CALL(capturer_, MockChangeTarget(_)); + EXPECT_CALL(capturer_, MockStart(_)); + StartCaptureWithMockCapturer(); + base::RunLoop().RunUntilIdle(); + // Verify that the previously cached values are sent to |capturer_|. + EXPECT_EQ(capturer_.min_capture_period(), kNewMinCapturePeriod); + EXPECT_EQ(capturer_.min_frame_size(), kNewMinFrameSize); + EXPECT_EQ(capturer_.max_frame_size(), kNewMaxFrameSize); + EXPECT_EQ(capturer_.frame_sink_id(), kNewFrameSinkId); +} + +// Tests that DevToolsVideoConsumer::IsValidMinAndMaxFrameSize adheres to the +// limits set by media::limits::kMaxDimension +TEST_F(DevToolsVideoConsumerTest, IsValidMinAndMaxFrameSize) { + // Choosing valid frame sizes with + // kNormalMinSize.height() > kNormalMaxSize.width() so that width + // and height are not interchanged in this test. + constexpr gfx::Size kNormalMinSize = gfx::Size(50, 150); + constexpr gfx::Size kNormalMaxSize = gfx::Size(100, 200); + + // Testing success cases. + EXPECT_TRUE(IsValidMinAndMaxFrameSize(kNormalMinSize, kNormalMaxSize)); + // Non-zero frames that are equal should pass. + EXPECT_TRUE(IsValidMinAndMaxFrameSize(kNormalMinSize, kNormalMaxSize)); + // Swapping width and height of frames should pass. + EXPECT_TRUE(IsValidMinAndMaxFrameSize( + gfx::Size(kNormalMinSize.height(), kNormalMinSize.width()), + gfx::Size(kNormalMaxSize.height(), kNormalMaxSize.width()))); + + // Testing failure cases. + // |min_frame_size|.width() should be > 0 + EXPECT_FALSE(IsValidMinAndMaxFrameSize(gfx::Size(0, kNormalMinSize.height()), + kNormalMaxSize)); + // |min_frame_size|.height() should be > 0 + EXPECT_FALSE(IsValidMinAndMaxFrameSize(gfx::Size(kNormalMinSize.width(), 0), + kNormalMaxSize)); + // |min_frame_size|.width() should be <= |max_frame_size|.width() + EXPECT_FALSE(IsValidMinAndMaxFrameSize( + gfx::Size(kNormalMaxSize.width() + 1, kNormalMinSize.height()), + kNormalMaxSize)); + // |max_frame_size|.height() should be <= |max_frame_size|.height() + EXPECT_FALSE(IsValidMinAndMaxFrameSize( + gfx::Size(kNormalMinSize.width(), kNormalMaxSize.height() + 1), + kNormalMaxSize)); + // |max_frame_size|.height() should be <= media::limits::kMaxDimension + EXPECT_FALSE(IsValidMinAndMaxFrameSize( + kNormalMinSize, + gfx::Size(kNormalMaxSize.width(), media::limits::kMaxDimension + 1))); + // |max_frame_size|.width() should be <= media::limits::kMaxDimension + EXPECT_FALSE(IsValidMinAndMaxFrameSize( + kNormalMinSize, + gfx::Size(media::limits::kMaxDimension + 1, kNormalMaxSize.height()))); +} + +} // namespace content diff --git a/chromium/content/browser/devtools/protocol/browser_handler.cc b/chromium/content/browser/devtools/protocol/browser_handler.cc index aed9964e47b..7e03482fde2 100644 --- a/chromium/content/browser/devtools/protocol/browser_handler.cc +++ b/chromium/content/browser/devtools/protocol/browser_handler.cc @@ -112,7 +112,7 @@ Response BrowserHandler::GetHistogram( return Response::OK(); } -Response BrowserHandler::GetCommandLine( +Response BrowserHandler::GetBrowserCommandLine( std::unique_ptr<protocol::Array<String>>* arguments) { *arguments = protocol::Array<String>::create(); base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); diff --git a/chromium/content/browser/devtools/protocol/browser_handler.h b/chromium/content/browser/devtools/protocol/browser_handler.h index 8af1d28e08a..b4f3e05eccc 100644 --- a/chromium/content/browser/devtools/protocol/browser_handler.h +++ b/chromium/content/browser/devtools/protocol/browser_handler.h @@ -34,7 +34,7 @@ class BrowserHandler : public DevToolsDomainHandler, public Browser::Backend { const std::string& in_name, std::unique_ptr<Browser::Histogram>* out_histogram) override; - Response GetCommandLine( + Response GetBrowserCommandLine( std::unique_ptr<protocol::Array<String>>* arguments) override; private: diff --git a/chromium/content/browser/devtools/protocol/devtools_download_manager_delegate.cc b/chromium/content/browser/devtools/protocol/devtools_download_manager_delegate.cc index 687a744304b..f71a5bf4ab3 100644 --- a/chromium/content/browser/devtools/protocol/devtools_download_manager_delegate.cc +++ b/chromium/content/browser/devtools/protocol/devtools_download_manager_delegate.cc @@ -121,7 +121,7 @@ bool DevToolsDownloadManagerDelegate::DetermineDownloadTarget( base::BindOnce(&DevToolsDownloadManagerDelegate::GenerateFilename, item->GetURL(), item->GetContentDisposition(), item->GetSuggestedFilename(), item->GetMimeType(), - download_path, filename_determined_callback)); + download_path, std::move(filename_determined_callback))); return true; } diff --git a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc index d8eba18ceb5..5a364cef4ed 100644 --- a/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc +++ b/chromium/content/browser/devtools/protocol/devtools_protocol_browsertest.cc @@ -21,15 +21,16 @@ #include "base/sys_info.h" #include "base/values.h" #include "build/build_config.h" +#include "components/download/public/common/download_file_factory.h" +#include "components/download/public/common/download_file_impl.h" #include "components/download/public/common/download_task_runner.h" #include "content/browser/devtools/protocol/devtools_download_manager_delegate.h" -#include "content/browser/download/download_file_factory.h" -#include "content/browser/download/download_file_impl.h" #include "content/browser/download/download_manager_impl.h" #include "content/browser/frame_host/interstitial_page_impl.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/devtools_agent_host.h" #include "content/public/browser/download_manager.h" #include "content/public/browser/interstitial_page_delegate.h" @@ -49,6 +50,7 @@ #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/public/test/download_test_observer.h" +#include "content/public/test/slow_download_http_response.h" #include "content/public/test/test_navigation_observer.h" #include "content/shell/browser/shell.h" #include "content/shell/browser/shell_browser_context.h" @@ -58,9 +60,7 @@ #include "net/dns/mock_host_resolver.h" #include "net/test/cert_test_util.h" #include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/test/test_data_directory.h" -#include "net/test/url_request/url_request_mock_http_job.h" -#include "net/test/url_request/url_request_slow_download_job.h" +#include "services/network/public/cpp/features.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" @@ -2243,6 +2243,24 @@ IN_PROC_BROWSER_TEST_F(DevToolsProtocolDeviceEmulationTest, MAYBE_DeviceSize) { EXPECT_EQ(original_size, GetViewSize()); } +// Setting frame size (through RWHV) is not supported on Android. +#if defined(OS_ANDROID) +#define MAYBE_RenderKillDoesNotCrashBrowser \ + DISABLED_RenderKillDoesNotCrashBrowser +#else +#define MAYBE_RenderKillDoesNotCrashBrowser RenderKillDoesNotCrashBrowser +#endif +IN_PROC_BROWSER_TEST_F(DevToolsProtocolDeviceEmulationTest, + MAYBE_RenderKillDoesNotCrashBrowser) { + NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); + Attach(); + EmulateDeviceSize(gfx::Size(200, 200)); + NavigateToURLBlockUntilNavigationsComplete( + shell(), GURL(content::kChromeUICrashURL), 1); + SendCommand("Emulation.clearDeviceMetricsOverride", nullptr); + // Should not crash at this point. +} + class DevToolsProtocolTouchTest : public DevToolsProtocolTest { public: ~DevToolsProtocolTouchTest() override {} @@ -2385,32 +2403,34 @@ static void RemoveShellDelegate(Shell* shell) { DownloadManagerForShell(shell)->SetDelegate(nullptr); } -class CountingDownloadFile : public DownloadFileImpl { +class CountingDownloadFile : public download::DownloadFileImpl { public: - CountingDownloadFile(std::unique_ptr<download::DownloadSaveInfo> save_info, - const base::FilePath& default_downloads_directory, - std::unique_ptr<DownloadManager::InputStream> stream, - uint32_t download_id, - base::WeakPtr<DownloadDestinationObserver> observer) - : DownloadFileImpl(std::move(save_info), - default_downloads_directory, - std::move(stream), - download_id, - observer) {} + CountingDownloadFile( + std::unique_ptr<download::DownloadSaveInfo> save_info, + const base::FilePath& default_downloads_directory, + std::unique_ptr<download::InputStream> stream, + uint32_t download_id, + base::WeakPtr<download::DownloadDestinationObserver> observer) + : download::DownloadFileImpl(std::move(save_info), + default_downloads_directory, + std::move(stream), + download_id, + observer) {} ~CountingDownloadFile() override { DCHECK(download::GetDownloadTaskRunner()->RunsTasksInCurrentSequence()); active_files_--; } - void Initialize(const InitializeCallback& callback, + void Initialize(InitializeCallback callback, const CancelRequestCallback& cancel_request_callback, const download::DownloadItem::ReceivedSlices& received_slices, bool is_parallelizable) override { DCHECK(download::GetDownloadTaskRunner()->RunsTasksInCurrentSequence()); active_files_++; - DownloadFileImpl::Initialize(callback, cancel_request_callback, - received_slices, is_parallelizable); + download::DownloadFileImpl::Initialize(std::move(callback), + cancel_request_callback, + received_slices, is_parallelizable); } static void GetNumberActiveFiles(int* result) { @@ -2437,18 +2457,18 @@ class CountingDownloadFile : public DownloadFileImpl { int CountingDownloadFile::active_files_ = 0; -class CountingDownloadFileFactory : public DownloadFileFactory { +class CountingDownloadFileFactory : public download::DownloadFileFactory { public: CountingDownloadFileFactory() {} ~CountingDownloadFileFactory() override {} // DownloadFileFactory interface. - DownloadFile* CreateFile( + download::DownloadFile* CreateFile( std::unique_ptr<download::DownloadSaveInfo> save_info, const base::FilePath& default_downloads_directory, - std::unique_ptr<DownloadManager::InputStream> stream, + std::unique_ptr<download::InputStream> stream, uint32_t download_id, - base::WeakPtr<DownloadDestinationObserver> observer) override { + base::WeakPtr<download::DownloadDestinationObserver> observer) override { return new CountingDownloadFile(std::move(save_info), default_downloads_directory, std::move(stream), download_id, observer); @@ -2488,7 +2508,7 @@ class TestShellDownloadManagerDelegate : public ShellDownloadManagerDelegate { class DownloadCreateObserver : DownloadManager::Observer { public: explicit DownloadCreateObserver(DownloadManager* manager) - : manager_(manager), item_(nullptr) { + : manager_(manager), item_(nullptr), received_item_response_(false) { manager_->AddObserver(this); } @@ -2506,16 +2526,26 @@ class DownloadCreateObserver : DownloadManager::Observer { void OnDownloadCreated(DownloadManager* manager, download::DownloadItem* download) override { + received_item_response_ = true; + if (!item_) item_ = download; - if (!completion_closure_.is_null()) - base::ResetAndReturn(&completion_closure_).Run(); + if (completion_closure_) + std::move(completion_closure_).Run(); + } + + void OnDownloadDropped(DownloadManager* manager) override { + received_item_response_ = true; + + item_ = nullptr; + if (completion_closure_) + std::move(completion_closure_).Run(); } download::DownloadItem* WaitForFinished() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!item_) { + if (!received_item_response_) { base::RunLoop run_loop; completion_closure_ = run_loop.QuitClosure(); run_loop.Run(); @@ -2526,6 +2556,7 @@ class DownloadCreateObserver : DownloadManager::Observer { private: DownloadManager* manager_; download::DownloadItem* item_; + bool received_item_response_; base::Closure completion_closure_; }; @@ -2549,14 +2580,8 @@ class DevToolsDownloadContentTest : public DevToolsProtocolTest { manager->SetDelegate(test_delegate_.get()); test_delegate_->SetDownloadManager(manager); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&net::URLRequestSlowDownloadJob::AddUrlHandler)); - base::FilePath mock_base(GetTestFilePath("download", "")); - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&net::URLRequestMockHTTPJob::AddUrlHandlers, mock_base)); + embedded_test_server()->RegisterRequestHandler(base::BindRepeating( + &content::SlowDownloadHttpResponse::HandleSlowDownloadRequest)); ASSERT_TRUE(embedded_test_server()->Start()); } @@ -2590,7 +2615,7 @@ class DevToolsDownloadContentTest : public DevToolsProtocolTest { // Note: Cannot be used with other alternative DownloadFileFactorys void SetupEnsureNoPendingDownloads() { DownloadManagerForShell(shell())->SetDownloadFileFactoryForTesting( - std::unique_ptr<DownloadFileFactory>( + std::unique_ptr<download::DownloadFileFactory>( new CountingDownloadFileFactory())); } @@ -2602,13 +2627,7 @@ class DevToolsDownloadContentTest : public DevToolsProtocolTest { } bool EnsureNoPendingDownloads() { - bool result = true; - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&EnsureNoPendingDownloadJobsOnIO, &result)); - base::RunLoop().Run(); - return result && - (CountingDownloadFile::GetNumberActiveFilesFromFileThread() == 0); + return CountingDownloadFile::GetNumberActiveFilesFromFileThread() == 0; } // Checks that |path| is has |file_size| bytes, and matches the |value| @@ -2652,14 +2671,6 @@ class DevToolsDownloadContentTest : public DevToolsProtocolTest { } private: - static void EnsureNoPendingDownloadJobsOnIO(bool* result) { - if (net::URLRequestSlowDownloadJob::NumberOutstandingRequests()) - *result = false; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::MessageLoop::current()->QuitWhenIdleClosure()); - } - // Location of the downloads directory for these tests base::ScopedTempDir downloads_directory_; std::unique_ptr<TestShellDownloadManagerDelegate> test_delegate_; @@ -2678,8 +2689,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, SingleDownload) { // Create a download, wait until it's started, and confirm // we're in the expected state. download::DownloadItem* download = StartDownloadAndReturnItem( - shell(), - GURL(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib"))); + shell(), embedded_test_server()->GetURL("/download/download-test.lib")); ASSERT_EQ(download::DownloadItem::IN_PROGRESS, download->GetState()); WaitForCompletion(download); @@ -2697,7 +2707,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, DownloadCancelled) { // Create a download, wait until it's started, and confirm // we're in the expected state. download::DownloadItem* download = StartDownloadAndReturnItem( - shell(), GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl)); + shell(), embedded_test_server()->GetURL( + content::SlowDownloadHttpResponse::kUnknownSizeUrl)); ASSERT_EQ(download::DownloadItem::IN_PROGRESS, download->GetState()); // Cancel the download and wait for download system quiesce. @@ -2719,9 +2730,10 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, DeniedDownload) { SetDownloadBehavior("deny"); // Create a download, wait and confirm it was cancelled. download::DownloadItem* download = StartDownloadAndReturnItem( - shell(), - GURL(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib"))); - EnsureNoPendingDownloads(); + shell(), embedded_test_server()->GetURL("/download/download-test.lib")); + DownloadTestFlushObserver flush_observer(DownloadManagerForShell(shell())); + flush_observer.WaitForFlush(); + EXPECT_TRUE(EnsureNoPendingDownloads()); ASSERT_EQ(download::DownloadItem::CANCELLED, download->GetState()); } @@ -2736,7 +2748,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, DefaultDownload) { // Create a download, wait until it's started, and confirm // we're in the expected state. download::DownloadItem* download = StartDownloadAndReturnItem( - shell(), GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl)); + shell(), embedded_test_server()->GetURL( + content::SlowDownloadHttpResponse::kUnknownSizeUrl)); ASSERT_EQ(download::DownloadItem::IN_PROGRESS, download->GetState()); // Cancel the download and wait for download system quiesce. @@ -2760,10 +2773,17 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, DefaultDownloadHeadless) { SetDownloadBehavior("default"); // Create a download, wait and confirm it was cancelled. download::DownloadItem* download = StartDownloadAndReturnItem( - shell(), - GURL(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib"))); - EnsureNoPendingDownloads(); - ASSERT_EQ(download::DownloadItem::CANCELLED, download->GetState()); + shell(), embedded_test_server()->GetURL("/download/download-test.lib")); + DownloadTestFlushObserver flush_observer(DownloadManagerForShell(shell())); + flush_observer.WaitForFlush(); + EXPECT_TRUE(EnsureNoPendingDownloads()); + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + // The download manager will intercept the download navigation and drop it + // since we have set the delegate to null. + EXPECT_EQ(nullptr, download); + } else { + EXPECT_EQ(download::DownloadItem::CANCELLED, download->GetState()); + } } // Check that download logic is reset when creating a new target. @@ -2780,7 +2800,7 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, ResetDownloadState) { // Create a download, wait and confirm it wasn't cancelled. download::DownloadItem* download = StartDownloadAndReturnItem( new_window, - GURL(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib"))); + embedded_test_server()->GetURL("/download/download-test.lib")); WaitForCompletion(download); ASSERT_EQ(download::DownloadItem::COMPLETE, download->GetState()); } @@ -2797,13 +2817,14 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, MultiDownload) { // Create a download, wait until it's started, and confirm // we're in the expected state. download::DownloadItem* download1 = StartDownloadAndReturnItem( - shell(), GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl)); + shell(), embedded_test_server()->GetURL( + content::SlowDownloadHttpResponse::kUnknownSizeUrl)); ASSERT_EQ(download::DownloadItem::IN_PROGRESS, download1->GetState()); NavigateToURLBlockUntilNavigationsComplete(shell(), GURL("about:blank"), 1); SetDownloadBehavior("allow", "download2"); // Start the second download and wait until it's done. - GURL url(net::URLRequestMockHTTPJob::GetMockUrl("download-test.lib")); + GURL url(embedded_test_server()->GetURL("/download/download-test.lib")); download::DownloadItem* download2 = StartDownloadAndReturnItem(shell(), url); WaitForCompletion(download2); @@ -2813,7 +2834,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, MultiDownload) { // Allow the first request to finish. std::unique_ptr<DownloadTestObserver> observer2(CreateWaiter(shell(), 1)); NavigateToURL(shell(), - GURL(net::URLRequestSlowDownloadJob::kFinishDownloadUrl)); + embedded_test_server()->GetURL( + content::SlowDownloadHttpResponse::kFinishDownloadUrl)); observer2->WaitForFinished(); // Wait for the third request. EXPECT_EQ( 1u, observer2->NumDownloadsSeenInState(download::DownloadItem::COMPLETE)); @@ -2827,8 +2849,8 @@ IN_PROC_BROWSER_TEST_F(DevToolsDownloadContentTest, MultiDownload) { // source file. base::FilePath file1(download1->GetTargetFilePath()); ASSERT_EQ(file1.DirName().MaybeAsASCII(), "download1"); - size_t file_size1 = net::URLRequestSlowDownloadJob::kFirstDownloadSize + - net::URLRequestSlowDownloadJob::kSecondDownloadSize; + size_t file_size1 = content::SlowDownloadHttpResponse::kFirstDownloadSize + + content::SlowDownloadHttpResponse::kSecondDownloadSize; std::string expected_contents(file_size1, '*'); ASSERT_TRUE(VerifyFile(file1, expected_contents, file_size1)); diff --git a/chromium/content/browser/devtools/protocol/emulation_handler.cc b/chromium/content/browser/devtools/protocol/emulation_handler.cc index 25ffa90042d..f61f93491cc 100644 --- a/chromium/content/browser/devtools/protocol/emulation_handler.cc +++ b/chromium/content/browser/devtools/protocol/emulation_handler.cc @@ -265,8 +265,12 @@ Response EmulationHandler::SetDeviceMetricsOverride( device_emulation_enabled_ = true; device_emulation_params_ = params; UpdateDeviceEmulationState(); + // Renderer should answer after emulation params were updated, so that the // response is only sent to the client once updates were applied. + // Unless the renderer has crashed. + if (GetWebContents() && GetWebContents()->IsCrashed()) + return Response::OK(); return Response::FallThrough(); } @@ -282,6 +286,9 @@ Response EmulationHandler::ClearDeviceMetricsOverride() { UpdateDeviceEmulationState(); // Renderer should answer after emulation was disabled, so that the response // is only sent to the client once updates were applied. + // Unless the renderer has crashed. + if (GetWebContents() && GetWebContents()->IsCrashed()) + return Response::OK(); return Response::FallThrough(); } @@ -316,16 +323,16 @@ WebContentsImpl* EmulationHandler::GetWebContents() { } void EmulationHandler::UpdateTouchEventEmulationState() { - RenderWidgetHostImpl* widget_host = - host_ ? host_->GetRenderWidgetHost() : nullptr; - if (!widget_host) + if (!host_ || !host_->GetRenderWidgetHost()) + return; + if (host_->GetParent() && !host_->IsCrossProcessSubframe()) return; if (touch_emulation_enabled_) { - widget_host->GetTouchEmulator()->Enable( + host_->GetRenderWidgetHost()->GetTouchEmulator()->Enable( TouchEmulator::Mode::kEmulatingTouchFromMouse, TouchEmulationConfigurationToType(touch_emulation_configuration_)); } else { - widget_host->GetTouchEmulator()->Disable(); + host_->GetRenderWidgetHost()->GetTouchEmulator()->Disable(); } if (GetWebContents()) { GetWebContents()->SetForceDisableOverscrollContent( @@ -334,9 +341,9 @@ void EmulationHandler::UpdateTouchEventEmulationState() { } void EmulationHandler::UpdateDeviceEmulationState() { - RenderWidgetHostImpl* widget_host = - host_ ? host_->GetRenderWidgetHost() : nullptr; - if (!widget_host) + if (!host_ || !host_->GetRenderWidgetHost()) + return; + if (host_->GetParent() && !host_->IsCrossProcessSubframe()) return; // TODO(eseckler): Once we change this to mojo, we should wait for an ack to // these messages from the renderer. The renderer should send the ack once the @@ -346,11 +353,12 @@ void EmulationHandler::UpdateDeviceEmulationState() { // ViewMsg and acknowledgment, as well as plump the acknowledgment back to the // EmulationHandler somehow. Mojo callbacks should make this much simpler. if (device_emulation_enabled_) { - widget_host->Send(new ViewMsg_EnableDeviceEmulation( - widget_host->GetRoutingID(), device_emulation_params_)); + host_->GetRenderWidgetHost()->Send(new ViewMsg_EnableDeviceEmulation( + host_->GetRenderWidgetHost()->GetRoutingID(), + device_emulation_params_)); } else { - widget_host->Send(new ViewMsg_DisableDeviceEmulation( - widget_host->GetRoutingID())); + host_->GetRenderWidgetHost()->Send(new ViewMsg_DisableDeviceEmulation( + host_->GetRenderWidgetHost()->GetRoutingID())); } } diff --git a/chromium/content/browser/devtools/protocol/emulation_handler.h b/chromium/content/browser/devtools/protocol/emulation_handler.h index f322802b648..1503b2e70da 100644 --- a/chromium/content/browser/devtools/protocol/emulation_handler.h +++ b/chromium/content/browser/devtools/protocol/emulation_handler.h @@ -8,7 +8,7 @@ #include "base/macros.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "content/browser/devtools/protocol/emulation.h" -#include "third_party/WebKit/public/web/WebDeviceEmulationParams.h" +#include "third_party/blink/public/web/web_device_emulation_params.h" namespace content { diff --git a/chromium/content/browser/devtools/protocol/input_handler.cc b/chromium/content/browser/devtools/protocol/input_handler.cc index 689dc38a92d..3556f33c0fb 100644 --- a/chromium/content/browser/devtools/protocol/input_handler.cc +++ b/chromium/content/browser/devtools/protocol/input_handler.cc @@ -10,7 +10,6 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/trace_event.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" #include "content/browser/devtools/devtools_session.h" #include "content/browser/devtools/protocol/native_input_event_builder.h" #include "content/browser/frame_host/render_frame_host_impl.h" @@ -441,9 +440,8 @@ void InputHandler::Wire(UberDispatcher* dispatcher) { Input::Dispatcher::wire(dispatcher, this); } -void InputHandler::OnSwapCompositorFrame( - const viz::CompositorFrameMetadata& frame_metadata) { - page_scale_factor_ = frame_metadata.page_scale_factor; +void InputHandler::OnPageScaleFactorChanged(float page_scale_factor) { + page_scale_factor_ = page_scale_factor; } Response InputHandler::Disable() { diff --git a/chromium/content/browser/devtools/protocol/input_handler.h b/chromium/content/browser/devtools/protocol/input_handler.h index 9dce4844708..ae8fd55904c 100644 --- a/chromium/content/browser/devtools/protocol/input_handler.h +++ b/chromium/content/browser/devtools/protocol/input_handler.h @@ -16,11 +16,7 @@ #include "content/browser/renderer_host/input/synthetic_gesture.h" #include "content/common/input/synthetic_smooth_scroll_gesture_params.h" #include "content/public/browser/render_widget_host.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" - -namespace viz { -class CompositorFrameMetadata; -} +#include "third_party/blink/public/platform/web_input_event.h" namespace content { class DevToolsAgentHostImpl; @@ -40,8 +36,7 @@ class InputHandler : public DevToolsDomainHandler, public Input::Backend { void SetRenderer(int process_host_id, RenderFrameHostImpl* frame_host) override; - void OnSwapCompositorFrame( - const viz::CompositorFrameMetadata& frame_metadata); + void OnPageScaleFactorChanged(float page_scale_factor); Response Disable() override; void DispatchKeyEvent( diff --git a/chromium/content/browser/devtools/protocol/native_input_event_builder_mac.mm b/chromium/content/browser/devtools/protocol/native_input_event_builder_mac.mm index 601ec197b5b..3faae032e20 100644 --- a/chromium/content/browser/devtools/protocol/native_input_event_builder_mac.mm +++ b/chromium/content/browser/devtools/protocol/native_input_event_builder_mac.mm @@ -5,7 +5,7 @@ #include <Cocoa/Cocoa.h> #include "base/strings/sys_string_conversions.h" #include "content/browser/devtools/protocol/native_input_event_builder.h" -#include "third_party/WebKit/public/platform/WebInputEvent.h" +#include "third_party/blink/public/platform/web_input_event.h" namespace content { namespace protocol { diff --git a/chromium/content/browser/devtools/protocol/network_handler.cc b/chromium/content/browser/devtools/protocol/network_handler.cc index 06c84ec3703..6d62f72deae 100644 --- a/chromium/content/browser/devtools/protocol/network_handler.cc +++ b/chromium/content/browser/devtools/protocol/network_handler.cc @@ -19,6 +19,7 @@ #include "base/time/time.h" #include "content/browser/devtools/devtools_interceptor_controller.h" #include "content/browser/devtools/devtools_session.h" +#include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/protocol/page.h" #include "content/browser/devtools/protocol/security.h" #include "content/browser/frame_host/frame_tree_node.h" @@ -38,10 +39,12 @@ #include "content/public/browser/site_instance.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/browser_side_navigation_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" #include "net/base/net_errors.h" #include "net/base/upload_bytes_element_reader.h" +#include "net/cert/ct_policy_status.h" #include "net/cert/ct_sct_to_string.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_store.h" @@ -53,6 +56,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/data_element.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/http_raw_request_response_info.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_response.h" @@ -76,125 +80,203 @@ using ClearBrowserCookiesCallback = const char kDevToolsEmulateNetworkConditionsClientId[] = "X-DevTools-Emulate-Network-Conditions-Client-Id"; +Network::CertificateTransparencyCompliance SerializeCTPolicyCompliance( + net::ct::CTPolicyCompliance ct_compliance) { + switch (ct_compliance) { + case net::ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS: + return Network::CertificateTransparencyComplianceEnum::Compliant; + case net::ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS: + case net::ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS: + return Network::CertificateTransparencyComplianceEnum::NotCompliant; + case net::ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY: + case net::ct::CTPolicyCompliance:: + CT_POLICY_COMPLIANCE_DETAILS_NOT_AVAILABLE: + return Network::CertificateTransparencyComplianceEnum::Unknown; + case net::ct::CTPolicyCompliance::CT_POLICY_MAX: + NOTREACHED(); + return Network::CertificateTransparencyComplianceEnum::Unknown; + } + NOTREACHED(); + return Network::CertificateTransparencyComplianceEnum::Unknown; +} + +std::unique_ptr<Network::Cookie> BuildCookie( + const net::CanonicalCookie& cookie) { + std::unique_ptr<Network::Cookie> devtools_cookie = + Network::Cookie::Create() + .SetName(cookie.Name()) + .SetValue(cookie.Value()) + .SetDomain(cookie.Domain()) + .SetPath(cookie.Path()) + .SetExpires(cookie.ExpiryDate().is_null() + ? -1 + : cookie.ExpiryDate().ToDoubleT()) + .SetSize(cookie.Name().length() + cookie.Value().length()) + .SetHttpOnly(cookie.IsHttpOnly()) + .SetSecure(cookie.IsSecure()) + .SetSession(!cookie.IsPersistent()) + .Build(); + + switch (cookie.SameSite()) { + case net::CookieSameSite::STRICT_MODE: + devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Strict); + break; + case net::CookieSameSite::LAX_MODE: + devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Lax); + break; + case net::CookieSameSite::NO_RESTRICTION: + break; + } + return devtools_cookie; +} + +std::unique_ptr<ProtocolCookieArray> BuildCookieArray( + const std::vector<net::CanonicalCookie>& cookie_list) { + auto cookies = std::make_unique<ProtocolCookieArray>(); + + for (const net::CanonicalCookie& cookie : cookie_list) + cookies->addItem(BuildCookie(cookie)); + + return cookies; +} + class CookieRetriever : public base::RefCountedThreadSafe<CookieRetriever> { - public: - CookieRetriever(std::unique_ptr<GetCookiesCallback> callback) - : callback_(std::move(callback)), - all_callback_(nullptr) {} - - CookieRetriever(std::unique_ptr<GetAllCookiesCallback> callback) - : callback_(nullptr), - all_callback_(std::move(callback)) {} - - void RetrieveCookiesOnIO( - net::URLRequestContextGetter* context_getter, - const std::vector<GURL>& urls) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback_count_ = urls.size(); - - if (callback_count_ == 0) { - GotAllCookies(); - return; - } + public: + CookieRetriever(std::unique_ptr<GetCookiesCallback> callback) + : callback_(std::move(callback)), all_callback_(nullptr) { + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); + } - for (const GURL& url : urls) { - net::URLRequestContext* request_context = - context_getter->GetURLRequestContext(); - request_context->cookie_store()->GetAllCookiesForURLAsync( - url, base::BindOnce(&CookieRetriever::GotCookies, this)); - } - } + CookieRetriever(std::unique_ptr<GetAllCookiesCallback> callback) + : callback_(nullptr), all_callback_(std::move(callback)) {} - void RetrieveAllCookiesOnIO( - net::URLRequestContextGetter* context_getter) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - callback_count_ = 1; + void RetrieveCookiesOnIO(net::URLRequestContextGetter* context_getter, + const std::vector<GURL>& urls) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + callback_count_ = urls.size(); + if (callback_count_ == 0) { + GotAllCookies(); + return; + } + + for (const GURL& url : urls) { net::URLRequestContext* request_context = context_getter->GetURLRequestContext(); - request_context->cookie_store()->GetAllCookiesAsync( - base::BindOnce(&CookieRetriever::GotCookies, this)); + request_context->cookie_store()->GetAllCookiesForURLAsync( + url, base::BindOnce(&CookieRetriever::GotCookies, this)); + } + } + + void RetrieveAllCookiesOnIO(net::URLRequestContextGetter* context_getter) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + callback_count_ = 1; + + net::URLRequestContext* request_context = + context_getter->GetURLRequestContext(); + request_context->cookie_store()->GetAllCookiesAsync( + base::BindOnce(&CookieRetriever::GotCookies, this)); + } + + protected: + virtual ~CookieRetriever() {} + + void GotCookies(const net::CookieList& cookie_list) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + for (const net::CanonicalCookie& cookie : cookie_list) { + std::string key = base::StringPrintf( + "%s::%s::%s::%d", cookie.Name().c_str(), cookie.Domain().c_str(), + cookie.Path().c_str(), cookie.IsSecure()); + cookies_[key] = cookie; } - protected: - virtual ~CookieRetriever() {} - - void GotCookies(const net::CookieList& cookie_list) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - for (const net::CanonicalCookie& cookie : cookie_list) { - std::string key = base::StringPrintf( - "%s::%s::%s::%d", cookie.Name().c_str(), cookie.Domain().c_str(), - cookie.Path().c_str(), cookie.IsSecure()); - cookies_[key] = cookie; - } - --callback_count_; - if (callback_count_ == 0) - GotAllCookies(); + --callback_count_; + if (callback_count_ == 0) + GotAllCookies(); + } + + void GotAllCookies() { + net::CookieList master_cookie_list; + for (const auto& pair : cookies_) + master_cookie_list.push_back(pair.second); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::BindOnce(&CookieRetriever::SendCookiesResponseOnUI, this, + master_cookie_list)); + } + + void SendCookiesResponseOnUI(const net::CookieList& cookie_list) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + std::unique_ptr<ProtocolCookieArray> cookies = + BuildCookieArray(cookie_list); + + if (callback_) { + callback_->sendSuccess(std::move(cookies)); + } else { + DCHECK(all_callback_); + all_callback_->sendSuccess(std::move(cookies)); } + } + + std::unique_ptr<GetCookiesCallback> callback_; + std::unique_ptr<GetAllCookiesCallback> all_callback_; + int callback_count_ = 0; + std::unordered_map<std::string, net::CanonicalCookie> cookies_; - void GotAllCookies() { - net::CookieList master_cookie_list; - for (const auto& pair : cookies_) - master_cookie_list.push_back(pair.second); + private: + friend class base::RefCountedThreadSafe<CookieRetriever>; +}; - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::BindOnce(&CookieRetriever::SendCookiesResponseOnUI, this, - master_cookie_list)); +class CookieRetrieverNetworkService + : public base::RefCounted<CookieRetrieverNetworkService> { + public: + static void Retrieve(network::mojom::CookieManager* cookie_manager, + const std::vector<GURL> urls, + std::unique_ptr<GetCookiesCallback> callback) { + scoped_refptr<CookieRetrieverNetworkService> self = + new CookieRetrieverNetworkService(std::move(callback)); + net::CookieOptions cookie_options; + cookie_options.set_include_httponly(); + cookie_options.set_same_site_cookie_mode( + net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX); + cookie_options.set_do_not_update_access_time(); + for (const auto& url : urls) { + cookie_manager->GetCookieList( + url, cookie_options, + base::BindOnce(&CookieRetrieverNetworkService::GotCookies, self)); } + } - void SendCookiesResponseOnUI(const net::CookieList& cookie_list) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - std::unique_ptr<ProtocolCookieArray> cookies = - ProtocolCookieArray::create(); - - for (const net::CanonicalCookie& cookie : cookie_list) { - std::unique_ptr<Network::Cookie> devtools_cookie = - Network::Cookie::Create() - .SetName(cookie.Name()) - .SetValue(cookie.Value()) - .SetDomain(cookie.Domain()) - .SetPath(cookie.Path()) - .SetExpires(cookie.ExpiryDate().is_null() ? -1 : cookie.ExpiryDate().ToDoubleT()) - .SetSize(cookie.Name().length() + cookie.Value().length()) - .SetHttpOnly(cookie.IsHttpOnly()) - .SetSecure(cookie.IsSecure()) - .SetSession(!cookie.IsPersistent()) - .Build(); + private: + friend class base::RefCounted<CookieRetrieverNetworkService>; - switch (cookie.SameSite()) { - case net::CookieSameSite::STRICT_MODE: - devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Strict); - break; - case net::CookieSameSite::LAX_MODE: - devtools_cookie->SetSameSite(Network::CookieSameSiteEnum::Lax); - break; - case net::CookieSameSite::NO_RESTRICTION: - break; - } - - cookies->addItem(std::move(devtools_cookie)); - } + CookieRetrieverNetworkService(std::unique_ptr<GetCookiesCallback> callback) + : callback_(std::move(callback)) {} - if (callback_) { - callback_->sendSuccess(std::move(cookies)); - } else { - DCHECK(all_callback_); - all_callback_->sendSuccess(std::move(cookies)); - } + void GotCookies(const std::vector<net::CanonicalCookie>& cookies) { + for (const auto& cookie : cookies) { + std::string key = base::StringPrintf( + "%s::%s::%s::%d", cookie.Name().c_str(), cookie.Domain().c_str(), + cookie.Path().c_str(), cookie.IsSecure()); + all_cookies_.emplace(std::move(key), cookie); } + } - std::unique_ptr<GetCookiesCallback> callback_; - std::unique_ptr<GetAllCookiesCallback> all_callback_; - int callback_count_ = 0; - std::unordered_map<std::string, net::CanonicalCookie> cookies_; + ~CookieRetrieverNetworkService() { + auto cookies = std::make_unique<ProtocolCookieArray>(); + for (const auto& entry : all_cookies_) + cookies->addItem(BuildCookie(entry.second)); + callback_->sendSuccess(std::move(cookies)); + } - private: - friend class base::RefCountedThreadSafe<CookieRetriever>; + std::unique_ptr<GetCookiesCallback> callback_; + std::unordered_map<std::string, net::CanonicalCookie> all_cookies_; }; void ClearedCookiesOnIO(std::unique_ptr<ClearBrowserCookiesCallback> callback, uint32_t num_deleted) { + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -204,6 +286,7 @@ void ClearedCookiesOnIO(std::unique_ptr<ClearBrowserCookiesCallback> callback, void ClearCookiesOnIO(net::URLRequestContextGetter* context_getter, std::unique_ptr<ClearBrowserCookiesCallback> callback) { + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); net::URLRequestContext* request_context = context_getter->GetURLRequestContext(); @@ -216,36 +299,38 @@ void DeletedCookiesOnIO(base::OnceClosure callback, uint32_t num_deleted) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, std::move(callback)); } -void DeleteSelectedCookiesOnIO(net::URLRequestContextGetter* context_getter, - const std::string& name, - const std::string& url_spec, - const std::string& domain, - const std::string& path, - base::OnceClosure callback, - const net::CookieList& cookie_list) { - net::URLRequestContext* request_context = - context_getter->GetURLRequestContext(); - std::string normalized_domain = domain; - if (normalized_domain.empty()) { - GURL url(url_spec); - if (!url.SchemeIsHTTPOrHTTPS()) { - std::move(callback).Run(); - return; - } - normalized_domain = url.host(); - } +std::vector<net::CanonicalCookie> FilterCookies( + const std::vector<net::CanonicalCookie>& cookies, + const std::string& name, + const std::string& normalized_domain, + const std::string& path) { + std::vector<net::CanonicalCookie> result; - net::CookieList filtered_list; - for (const auto& cookie : cookie_list) { + for (const auto& cookie : cookies) { if (cookie.Name() != name) continue; if (cookie.Domain() != normalized_domain) continue; if (!path.empty() && cookie.Path() != path) continue; - filtered_list.push_back(cookie); + result.push_back(cookie); } + return result; +} + +void DeleteSelectedCookiesOnIO(net::URLRequestContextGetter* context_getter, + const std::string& name, + const std::string& normalized_domain, + const std::string& path, + base::OnceClosure callback, + const net::CookieList& cookie_list) { + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); + + net::URLRequestContext* request_context = + context_getter->GetURLRequestContext(); + net::CookieList filtered_list = + FilterCookies(cookie_list, name, normalized_domain, path); for (size_t i = 0; i < filtered_list.size(); ++i) { const auto& cookie = filtered_list[i]; base::OnceCallback<void(uint32_t)> once_callback; @@ -255,22 +340,22 @@ void DeleteSelectedCookiesOnIO(net::URLRequestContextGetter* context_getter, cookie, std::move(once_callback)); } if (!filtered_list.size()) - std::move(callback).Run(); + DeletedCookiesOnIO(std::move(callback), 0); } void DeleteCookiesOnIO(net::URLRequestContextGetter* context_getter, const std::string& name, - const std::string& url, - const std::string& domain, + const std::string& normalized_domain, const std::string& path, base::OnceClosure callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); net::URLRequestContext* request_context = context_getter->GetURLRequestContext(); request_context->cookie_store()->GetAllCookiesAsync(base::BindOnce( - &DeleteSelectedCookiesOnIO, base::Unretained(context_getter), name, url, - domain, path, std::move(callback))); + &DeleteSelectedCookiesOnIO, base::Unretained(context_getter), name, + normalized_domain, path, std::move(callback))); } void CookieSetOnIO(std::unique_ptr<SetCookieCallback> callback, bool success) { @@ -280,33 +365,55 @@ void CookieSetOnIO(std::unique_ptr<SetCookieCallback> callback, bool success) { std::move(callback), success)); } -void SetCookieOnIO(net::URLRequestContextGetter* context_getter, - const std::string& name, - const std::string& value, - const std::string& url_spec, - const std::string& domain, - const std::string& path, - bool secure, - bool http_only, - const std::string& same_site, - double expires, - base::OnceCallback<void(bool)> callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - net::URLRequestContext* request_context = - context_getter->GetURLRequestContext(); +void DeleteFilteredCookies(network::mojom::CookieManager* cookie_manager, + const std::string& name, + const std::string& normalized_domain, + const std::string& path, + std::unique_ptr<DeleteCookiesCallback> callback, + const std::vector<net::CanonicalCookie>& cookies) { + base::Time yesterday(base::Time::Now() - base::TimeDelta::FromDays(1)); - if (url_spec.empty() && domain.empty()) { - std::move(callback).Run(false); - return; + std::vector<net::CanonicalCookie> filtered_list = + FilterCookies(cookies, name, normalized_domain, path); + + base::RepeatingClosure barrier_closure = base::BarrierClosure( + filtered_list.size(), + base::BindOnce(&DeleteCookiesCallback::sendSuccess, std::move(callback))); + + for (auto& cookie : filtered_list) { + // Delete a single cookie by setting its expiration time into the past. + cookie_manager->SetCanonicalCookie( + net::CanonicalCookie(cookie.Name(), cookie.Value(), cookie.Domain(), + cookie.Path(), cookie.CreationDate(), yesterday, + cookie.LastAccessDate(), cookie.IsSecure(), + cookie.IsHttpOnly(), cookie.SameSite(), + cookie.Priority()), + true /* secure_source */, true /* modify_http_only */, + base::BindOnce( + [](base::RepeatingClosure callback, bool) { callback.Run(); }, + barrier_closure)); } +} +std::unique_ptr<net::CanonicalCookie> MakeCookieFromProtocolValues( + const std::string& name, + const std::string& value, + const std::string& url_spec, + const std::string& domain, + const std::string& path, + bool secure, + bool http_only, + const std::string& same_site, + double expires) { std::string normalized_domain = domain; + + if (url_spec.empty() && domain.empty()) + return nullptr; + if (!url_spec.empty()) { GURL source_url = GURL(url_spec); - if (!source_url.SchemeIsHTTPOrHTTPS()) { - std::move(callback).Run(false); - return; - } + if (!source_url.SchemeIsHTTPOrHTTPS()) + return nullptr; secure = secure || source_url.SchemeIsCryptographic(); if (normalized_domain.empty()) @@ -329,45 +436,44 @@ void SetCookieOnIO(net::URLRequestContextGetter* context_getter, if (same_site == Network::CookieSameSiteEnum::Strict) css = net::CookieSameSite::STRICT_MODE; - std::unique_ptr<net::CanonicalCookie> cc( - net::CanonicalCookie::CreateSanitizedCookie( - url, name, value, normalized_domain, path, base::Time(), - expiration_date, base::Time(), secure, http_only, css, - net::COOKIE_PRIORITY_DEFAULT)); - if (!cc) { - std::move(callback).Run(false); - return; - } + return net::CanonicalCookie::CreateSanitizedCookie( + url, name, value, normalized_domain, path, base::Time(), expiration_date, + base::Time(), secure, http_only, css, net::COOKIE_PRIORITY_DEFAULT); +} + +void SetCookieOnIO(net::URLRequestContextGetter* context_getter, + std::unique_ptr<net::CanonicalCookie> cookie, + base::OnceCallback<void(bool)> callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); + net::URLRequestContext* request_context = + context_getter->GetURLRequestContext(); + request_context->cookie_store()->SetCanonicalCookieAsync( - std::move(cc), secure, true /*modify_http_only*/, std::move(callback)); + std::move(cookie), true /* secure_source */, true /*modify_http_only*/, + std::move(callback)); } -void CookiesSetOnIO(std::unique_ptr<SetCookiesCallback> callback, - bool success) { +void CookiesSetOnIO(std::unique_ptr<SetCookiesCallback> callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::BindOnce(&SetCookiesCallback::sendSuccess, std::move(callback))); } -void SetCookiesOnIO( - net::URLRequestContextGetter* context_getter, - std::unique_ptr<protocol::Array<Network::CookieParam>> cookies, - base::OnceCallback<void(bool)> callback) { +void SetCookiesOnIO(net::URLRequestContextGetter* context_getter, + std::vector<std::unique_ptr<net::CanonicalCookie>> cookies, + base::OnceClosure callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); - for (size_t i = 0; i < cookies->length(); i++) { - Network::CookieParam* cookie = cookies->get(i); - - base::OnceCallback<void(bool)> once_callback; - if (i == cookies->length() - 1) - once_callback = std::move(callback); - - SetCookieOnIO(context_getter, cookie->GetName(), cookie->GetValue(), - cookie->GetUrl(""), cookie->GetDomain(""), - cookie->GetPath(""), cookie->GetSecure(false), - cookie->GetHttpOnly(false), cookie->GetSameSite(""), - cookie->GetExpires(-1), std::move(once_callback)); + base::RepeatingClosure barrier_closure = + base::BarrierClosure(cookies.size(), std::move(callback)); + for (auto& cookie : cookies) { + SetCookieOnIO(context_getter, std::move(cookie), + base::BindOnce([](base::RepeatingClosure callback, + bool) { callback.Run(); }, + barrier_closure)); } } @@ -809,6 +915,7 @@ Response NetworkHandler::Disable() { enabled_ = false; user_agent_ = std::string(); interception_handle_.reset(); + url_loader_interceptor_.reset(); SetNetworkConditions(nullptr); extra_headers_.clear(); return Response::FallThrough(); @@ -863,30 +970,47 @@ void NetworkHandler::ClearBrowserCookies( return; } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &ClearCookiesOnIO, - base::Unretained(storage_partition_->GetURLRequestContext()), - std::move(callback))); + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &ClearCookiesOnIO, + base::Unretained(storage_partition_->GetURLRequestContext()), + std::move(callback))); + return; + } + + storage_partition_->GetCookieManagerForBrowserProcess()->DeleteCookies( + network::mojom::CookieDeletionFilter::New(), + base::BindOnce([](std::unique_ptr<ClearBrowserCookiesCallback> callback, + uint32_t) { callback->sendSuccess(); }, + std::move(callback))); } void NetworkHandler::GetCookies(Maybe<Array<String>> protocol_urls, std::unique_ptr<GetCookiesCallback> callback) { - if (!host_) { + if (!host_ || !storage_partition_) { callback->sendFailure(Response::InternalError()); return; } - std::vector<GURL> urls = ComputeCookieURLs(host_, protocol_urls); - scoped_refptr<CookieRetriever> retriever = - new CookieRetriever(std::move(callback)); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &CookieRetriever::RetrieveCookiesOnIO, retriever, - base::Unretained(storage_partition_->GetURLRequestContext()), urls)); + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + scoped_refptr<CookieRetriever> retriever = + new CookieRetriever(std::move(callback)); + + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &CookieRetriever::RetrieveCookiesOnIO, retriever, + base::Unretained(storage_partition_->GetURLRequestContext()), + urls)); + return; + } + + CookieRetrieverNetworkService::Retrieve( + storage_partition_->GetCookieManagerForBrowserProcess(), urls, + std::move(callback)); } void NetworkHandler::GetAllCookies( @@ -895,15 +1019,25 @@ void NetworkHandler::GetAllCookies( callback->sendFailure(Response::InternalError()); return; } + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + scoped_refptr<CookieRetriever> retriever = + new CookieRetriever(std::move(callback)); - scoped_refptr<CookieRetriever> retriever = - new CookieRetriever(std::move(callback)); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &CookieRetriever::RetrieveAllCookiesOnIO, retriever, + base::Unretained(storage_partition_->GetURLRequestContext()))); + return; + } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, + storage_partition_->GetCookieManagerForBrowserProcess()->GetAllCookies( base::BindOnce( - &CookieRetriever::RetrieveAllCookiesOnIO, retriever, - base::Unretained(storage_partition_->GetURLRequestContext()))); + [](std::unique_ptr<GetAllCookiesCallback> callback, + const std::vector<net::CanonicalCookie>& cookies) { + callback->sendSuccess(BuildCookieArray(cookies)); + }, + std::move(callback))); } void NetworkHandler::SetCookie(const std::string& name, @@ -926,15 +1060,32 @@ void NetworkHandler::SetCookie(const std::string& name, "At least one of the url and domain needs to be specified")); } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &SetCookieOnIO, - base::Unretained(storage_partition_->GetURLRequestContext()), name, - value, url.fromMaybe(""), domain.fromMaybe(""), path.fromMaybe(""), - secure.fromMaybe(false), http_only.fromMaybe(false), - same_site.fromMaybe(""), expires.fromMaybe(-1), - base::BindOnce(&CookieSetOnIO, std::move(callback)))); + std::unique_ptr<net::CanonicalCookie> cookie = MakeCookieFromProtocolValues( + name, value, url.fromMaybe(""), domain.fromMaybe(""), path.fromMaybe(""), + secure.fromMaybe(false), http_only.fromMaybe(false), + same_site.fromMaybe(""), expires.fromMaybe(-1)); + + if (!cookie) { + // TODO(caseq): Current logic is for compatability only. + // Consider returning protocol error here. + callback->sendSuccess(false); + return; + } + + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &SetCookieOnIO, + base::Unretained(storage_partition_->GetURLRequestContext()), + std::move(cookie), + base::BindOnce(&CookieSetOnIO, std::move(callback)))); + return; + } + + storage_partition_->GetCookieManagerForBrowserProcess()->SetCanonicalCookie( + *cookie, true /* secure_source */, true /* modify_http_only */, + base::BindOnce(&SetCookieCallback::sendSuccess, std::move(callback))); } void NetworkHandler::SetCookies( @@ -945,18 +1096,51 @@ void NetworkHandler::SetCookies( return; } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &SetCookiesOnIO, - base::Unretained(storage_partition_->GetURLRequestContext()), - std::move(cookies), - base::BindOnce(&CookiesSetOnIO, std::move(callback)))); + std::vector<std::unique_ptr<net::CanonicalCookie>> net_cookies; + for (size_t i = 0; i < cookies->length(); i++) { + Network::CookieParam* cookie = cookies->get(i); + std::unique_ptr<net::CanonicalCookie> net_cookie = + MakeCookieFromProtocolValues( + cookie->GetName(), cookie->GetValue(), cookie->GetUrl(""), + cookie->GetDomain(""), cookie->GetPath(""), + cookie->GetSecure(false), cookie->GetHttpOnly(false), + cookie->GetSameSite(""), cookie->GetExpires(-1)); + if (!cookie) { + callback->sendFailure(Response::InvalidParams("Invalid cookie fields")); + return; + } + net_cookies.push_back(std::move(net_cookie)); + } + + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &SetCookiesOnIO, + base::Unretained(storage_partition_->GetURLRequestContext()), + std::move(net_cookies), + base::BindOnce(&CookiesSetOnIO, std::move(callback)))); + return; + } + + base::RepeatingClosure barrier_closure = base::BarrierClosure( + net_cookies.size(), + base::BindOnce(&SetCookiesCallback::sendSuccess, std::move(callback))); + + auto* cookie_manager = + storage_partition_->GetCookieManagerForBrowserProcess(); + for (const auto& cookie : net_cookies) { + cookie_manager->SetCanonicalCookie( + *cookie, true, true, + base::BindOnce( + [](base::RepeatingClosure callback, bool) { callback.Run(); }, + barrier_closure)); + } } void NetworkHandler::DeleteCookies( const std::string& name, - Maybe<std::string> url, + Maybe<std::string> url_spec, Maybe<std::string> domain, Maybe<std::string> path, std::unique_ptr<DeleteCookiesCallback> callback) { @@ -964,19 +1148,40 @@ void NetworkHandler::DeleteCookies( callback->sendFailure(Response::InternalError()); return; } - - if (!url.isJust() && !domain.isJust()) { + if (!url_spec.isJust() && !domain.isJust()) { callback->sendFailure(Response::InvalidParams( "At least one of the url and domain needs to be specified")); } - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce( - &DeleteCookiesOnIO, - base::Unretained(storage_partition_->GetURLRequestContext()), name, - url.fromMaybe(""), domain.fromMaybe(""), path.fromMaybe(""), - base::BindOnce(&DeleteCookiesCallback::sendSuccess, - std::move(callback)))); + + std::string normalized_domain = domain.fromMaybe(""); + if (normalized_domain.empty()) { + GURL url(url_spec.fromMaybe("")); + if (!url.SchemeIsHTTPOrHTTPS()) { + callback->sendFailure(Response::InvalidParams( + "An http or https url URL must be specified")); + return; + } + normalized_domain = url.host(); + } + + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + base::BindOnce( + &DeleteCookiesOnIO, + base::Unretained(storage_partition_->GetURLRequestContext()), name, + normalized_domain, path.fromMaybe(""), + base::BindOnce(&DeleteCookiesCallback::sendSuccess, + std::move(callback)))); + return; + } + + auto* cookie_manager = + storage_partition_->GetCookieManagerForBrowserProcess(); + + cookie_manager->GetAllCookies(base::BindOnce( + &DeleteFilteredCookies, base::Unretained(cookie_manager), name, + normalized_domain, path.fromMaybe(""), std::move(callback))); } Response NetworkHandler::SetUserAgentOverride(const std::string& user_agent) { @@ -1041,20 +1246,14 @@ Response NetworkHandler::SetBypassServiceWorker(bool bypass) { namespace { std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails( - const network::ResourceResponseInfo& info) { - if (info.certificate.empty()) + const net::SSLInfo& ssl_info) { + if (!ssl_info.cert) return nullptr; - scoped_refptr<net::X509Certificate> cert( - net::X509Certificate::CreateFromBytes(info.certificate[0].data(), - info.certificate[0].size())); - if (!cert) - return nullptr; - std::unique_ptr< protocol::Array<protocol::Network::SignedCertificateTimestamp>> signed_certificate_timestamp_list = protocol::Array<Network::SignedCertificateTimestamp>::create(); - for (auto const& sct : info.signed_certificate_timestamps) { + for (auto const& sct : ssl_info.signed_certificate_timestamps) { std::unique_ptr<protocol::Network::SignedCertificateTimestamp> signed_certificate_timestamp = Network::SignedCertificateTimestamp::Create() @@ -1063,8 +1262,8 @@ std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails( .SetLogDescription(sct.sct->log_description) .SetLogId(base::HexEncode(sct.sct->log_id.c_str(), sct.sct->log_id.length())) - .SetTimestamp( - (sct.sct->timestamp - base::Time()).InMillisecondsF()) + .SetTimestamp((sct.sct->timestamp - base::Time::UnixEpoch()) + .InMillisecondsF()) .SetHashAlgorithm(net::ct::HashAlgorithmToString( sct.sct->signature.hash_algorithm)) .SetSignatureAlgorithm(net::ct::SignatureAlgorithmToString( @@ -1078,7 +1277,7 @@ std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails( } std::vector<std::string> san_dns; std::vector<std::string> san_ip; - cert->GetSubjectAltName(&san_dns, &san_ip); + ssl_info.cert->GetSubjectAltName(&san_dns, &san_ip); std::unique_ptr<Array<String>> san_list = Array<String>::create(); for (const std::string& san : san_dns) san_list->addItem(san); @@ -1088,23 +1287,27 @@ std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails( .ToString()); } + const char* protocol = ""; + const char* key_exchange = ""; + const char* cipher = ""; + const char* mac = nullptr; + int ssl_version = - net::SSLConnectionStatusToVersion(info.ssl_connection_status); - const char* protocol; - net::SSLVersionToString(&protocol, ssl_version); - - const char* key_exchange; - const char* cipher; - const char* mac; - bool is_aead; - bool is_tls13; - uint16_t cipher_suite = - net::SSLConnectionStatusToCipherSuite(info.ssl_connection_status); - net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, - &is_tls13, cipher_suite); - if (key_exchange == nullptr) { - DCHECK(is_tls13); - key_exchange = ""; + net::SSLConnectionStatusToVersion(ssl_info.connection_status); + + if (ssl_info.connection_status) { + net::SSLVersionToString(&protocol, ssl_version); + + bool is_aead; + bool is_tls13; + uint16_t cipher_suite = + net::SSLConnectionStatusToCipherSuite(ssl_info.connection_status); + net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead, + &is_tls13, cipher_suite); + if (key_exchange == nullptr) { + DCHECK(is_tls13); + key_exchange = ""; + } } std::unique_ptr<protocol::Network::SecurityDetails> security_details = @@ -1112,19 +1315,21 @@ std::unique_ptr<protocol::Network::SecurityDetails> BuildSecurityDetails( .SetProtocol(protocol) .SetKeyExchange(key_exchange) .SetCipher(cipher) - .SetSubjectName(cert->subject().common_name) + .SetSubjectName(ssl_info.cert->subject().common_name) .SetSanList(std::move(san_list)) - .SetIssuer(cert->issuer().common_name) - .SetValidFrom(cert->valid_start().ToDoubleT()) - .SetValidTo(cert->valid_expiry().ToDoubleT()) + .SetIssuer(ssl_info.cert->issuer().common_name) + .SetValidFrom(ssl_info.cert->valid_start().ToDoubleT()) + .SetValidTo(ssl_info.cert->valid_expiry().ToDoubleT()) .SetCertificateId(0) // Keep this in protocol for compatability. .SetSignedCertificateTimestampList( std::move(signed_certificate_timestamp_list)) + .SetCertificateTransparencyCompliance( + SerializeCTPolicyCompliance(ssl_info.ct_policy_compliance)) .Build(); - if (info.ssl_key_exchange_group != 0) { + if (ssl_info.key_exchange_group != 0) { const char* key_exchange_group = - SSL_get_curve_name(info.ssl_key_exchange_group); + SSL_get_curve_name(ssl_info.key_exchange_group); if (key_exchange_group) security_details->SetKeyExchangeGroup(key_exchange_group); } @@ -1195,7 +1400,8 @@ std::unique_ptr<Network::Response> BuildResponse( response->SetProtocol(GetProtocol(url, info)); response->SetRemoteIPAddress(info.socket_address.HostForURL()); response->SetRemotePort(info.socket_address.port()); - response->SetSecurityDetails(BuildSecurityDetails(info)); + if (info.ssl_info.has_value()) + response->SetSecurityDetails(BuildSecurityDetails(*info.ssl_info)); return response; } @@ -1242,12 +1448,13 @@ void NetworkHandler::NavigationRequestWillBeSent( request->SetMixedContentType(Security::MixedContentTypeEnum::None); std::unique_ptr<Network::Initiator> initiator; - base::DictionaryValue* initiator_value = - nav_request.begin_params()->devtools_initiator.get(); - if (initiator_value) { + const base::Optional<base::Value>& initiator_optional = + nav_request.begin_params()->devtools_initiator; + if (initiator_optional.has_value()) { ErrorSupport ignored_errors; initiator = Network::Initiator::fromValue( - toProtocolValue(initiator_value, 1000).get(), &ignored_errors); + toProtocolValue(&initiator_optional.value(), 1000).get(), + &ignored_errors); } if (!initiator) { initiator = Network::Initiator::Create() @@ -1264,7 +1471,8 @@ void NetworkHandler::NavigationRequestWillBeSent( id, id, StripFragment(common_params.url), std::move(request), current_ticks, current_wall_time, std::move(initiator), std::move(redirect_response), - std::string(Page::ResourceTypeEnum::Document), std::move(frame_token)); + std::string(Page::ResourceTypeEnum::Document), std::move(frame_token), + common_params.has_user_gesture); } void NetworkHandler::RequestSent(const std::string& request_id, @@ -1290,7 +1498,8 @@ void NetworkHandler::RequestSent(const std::string& request_id, base::Time::Now().ToDoubleT(), Network::Initiator::Create().SetType(initiator_type).Build(), std::unique_ptr<Network::Response>(), - std::string(Page::ResourceTypeEnum::Other)); + std::string(Page::ResourceTypeEnum::Other), + Maybe<std::string>() /* frame_id */, request.has_user_gesture); } void NetworkHandler::ResponseReceived(const std::string& request_id, @@ -1369,7 +1578,9 @@ void NetworkHandler::NavigationFailed(NavigationRequest* navigation_request) { .SetType(Network::Initiator::TypeEnum::Parser) .Build(), std::unique_ptr<Network::Response>(), - std::string(Page::ResourceTypeEnum::Document)); + std::string(Page::ResourceTypeEnum::Document), + Maybe<std::string>() /* frame_id */, + navigation_request->common_params().has_user_gesture); frontend_->LoadingFailed( request_id, @@ -1381,18 +1592,9 @@ void NetworkHandler::NavigationFailed(NavigationRequest* navigation_request) { DispatchResponse NetworkHandler::SetRequestInterception( std::unique_ptr<protocol::Array<protocol::Network::RequestPattern>> patterns) { - WebContents* web_contents = WebContents::FromRenderFrameHost(host_); - if (!web_contents) - return Response::InternalError(); - - DevToolsInterceptorController* interceptor = - DevToolsInterceptorController::FromBrowserContext( - web_contents->GetBrowserContext()); - if (!interceptor) - return Response::Error("Interception not supported"); - if (!patterns->length()) { interception_handle_.reset(); + url_loader_interceptor_.reset(); return Response::OK(); } @@ -1412,13 +1614,37 @@ DispatchResponse NetworkHandler::SetRequestInterception( protocol::Network::InterceptionStageEnum::Request)))); } + if (!host_) + return Response::InternalError(); + + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + if (!url_loader_interceptor_) { + url_loader_interceptor_ = std::make_unique<DevToolsURLLoaderInterceptor>( + host_->frame_tree_node(), + base::BindRepeating(&NetworkHandler::RequestIntercepted, + weak_factory_.GetWeakPtr())); + } + url_loader_interceptor_->SetPatterns(interceptor_patterns); + return Response::OK(); + } + + WebContents* web_contents = WebContents::FromRenderFrameHost(host_); + if (!web_contents) + return Response::InternalError(); + + DevToolsInterceptorController* interceptor = + DevToolsInterceptorController::FromBrowserContext( + web_contents->GetBrowserContext()); + if (!interceptor) + return Response::Error("Interception not supported"); + if (interception_handle_) { - interception_handle_->UpdatePatterns(std::move(interceptor_patterns)); + interception_handle_->UpdatePatterns(interceptor_patterns); } else { interception_handle_ = interceptor->StartInterceptingRequests( - host_->frame_tree_node(), std::move(interceptor_patterns), - base::Bind(&NetworkHandler::RequestIntercepted, - weak_factory_.GetWeakPtr())); + host_->frame_tree_node(), interceptor_patterns, + base::BindRepeating(&NetworkHandler::RequestIntercepted, + weak_factory_.GetWeakPtr())); } return Response::OK(); @@ -1434,13 +1660,6 @@ void NetworkHandler::ContinueInterceptedRequest( Maybe<protocol::Network::Headers> headers, Maybe<protocol::Network::AuthChallengeResponse> auth_challenge_response, std::unique_ptr<ContinueInterceptedRequestCallback> callback) { - DevToolsInterceptorController* interceptor = - DevToolsInterceptorController::FromBrowserContext(browser_context_); - if (!interceptor) { - callback->sendFailure(Response::InternalError()); - return; - } - base::Optional<std::string> raw_response; if (base64_raw_response.isJust()) { std::string decoded; @@ -1464,18 +1683,37 @@ void NetworkHandler::ContinueInterceptedRequest( mark_as_canceled = true; } - interceptor->ContinueInterceptedRequest( - interception_id, + auto modifications = std::make_unique<DevToolsNetworkInterceptor::Modifications>( std::move(error), std::move(raw_response), std::move(url), std::move(method), std::move(post_data), std::move(headers), - std::move(auth_challenge_response), mark_as_canceled), - std::move(callback)); + std::move(auth_challenge_response), mark_as_canceled); + + if (url_loader_interceptor_) { + url_loader_interceptor_->ContinueInterceptedRequest( + interception_id, std::move(modifications), std::move(callback)); + return; + } + + DevToolsInterceptorController* interceptor = + DevToolsInterceptorController::FromBrowserContext(browser_context_); + if (!interceptor) { + callback->sendFailure(Response::InternalError()); + return; + } + interceptor->ContinueInterceptedRequest( + interception_id, std::move(modifications), std::move(callback)); } void NetworkHandler::GetResponseBodyForInterception( const String& interception_id, std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) { + if (url_loader_interceptor_) { + url_loader_interceptor_->GetResponseBody(interception_id, + std::move(callback)); + return; + } + DevToolsInterceptorController* interceptor = DevToolsInterceptorController::FromBrowserContext(browser_context_); if (!interceptor) { @@ -1495,6 +1733,31 @@ GURL NetworkHandler::ClearUrlRef(const GURL& url) { } // static +std::unique_ptr<Network::Request> +NetworkHandler::CreateRequestFromResourceRequest( + const network::ResourceRequest& request) { + std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create()); + for (net::HttpRequestHeaders::Iterator it(request.headers); it.GetNext();) + headers_dict->setString(it.name(), it.value()); + if (request.referrer.is_valid()) { + headers_dict->setString(net::HttpRequestHeaders::kReferer, + request.referrer.spec()); + } + std::unique_ptr<protocol::Network::Request> request_object = + Network::Request::Create() + .SetUrl(ClearUrlRef(request.url).spec()) + .SetMethod(request.method) + .SetHeaders(Object::fromValue(headers_dict.get(), nullptr)) + .SetInitialPriority(resourcePriority(request.priority)) + .SetReferrerPolicy(referrerPolicy(request.referrer_policy)) + .Build(); + std::string post_data; + if (request.request_body && GetPostData(*request.request_body, &post_data)) + request_object->SetPostData(std::move(post_data)); + return request_object; +} + +// static std::unique_ptr<Network::Request> NetworkHandler::CreateRequestFromURLRequest( const net::URLRequest* request) { std::unique_ptr<DictionaryValue> headers_dict(DictionaryValue::create()); @@ -1540,6 +1803,15 @@ bool NetworkHandler::ShouldCancelNavigation( return interceptor && interceptor->ShouldCancelNavigation(global_request_id); } +bool NetworkHandler::MaybeCreateProxyForInterception( + const base::UnguessableToken& frame_token, + int process_id, + network::mojom::URLLoaderFactoryRequest* target_factory_request) { + return url_loader_interceptor_ && + url_loader_interceptor_->CreateProxyForInterception( + frame_token, process_id, target_factory_request); +} + void NetworkHandler::ApplyOverrides(net::HttpRequestHeaders* headers, bool* skip_service_worker, bool* disable_cache) { diff --git a/chromium/content/browser/devtools/protocol/network_handler.h b/chromium/content/browser/devtools/protocol/network_handler.h index f28d42d4601..6bf4e7e976a 100644 --- a/chromium/content/browser/devtools/protocol/network_handler.h +++ b/chromium/content/browser/devtools/protocol/network_handler.h @@ -11,12 +11,17 @@ #include "base/containers/flat_set.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" +#include "content/browser/devtools/devtools_url_loader_interceptor.h" #include "content/browser/devtools/protocol/devtools_domain_handler.h" #include "content/browser/devtools/protocol/network.h" #include "net/base/net_errors.h" #include "net/cookies/canonical_cookie.h" #include "services/network/public/mojom/network_service.mojom.h" +namespace base { +class UnguessableToken; +}; + namespace net { class HttpRequestHeaders; class URLRequest; @@ -120,6 +125,11 @@ class NetworkHandler : public DevToolsDomainHandler, std::unique_ptr<GetResponseBodyForInterceptionCallback> callback) override; + bool MaybeCreateProxyForInterception( + const base::UnguessableToken& frame_token, + int process_id, + network::mojom::URLLoaderFactoryRequest* target_factory_request); + void ApplyOverrides(net::HttpRequestHeaders* headers, bool* skip_service_worker, bool* disable_cache); @@ -146,6 +156,8 @@ class NetworkHandler : public DevToolsDomainHandler, Network::Frontend* frontend() const { return frontend_.get(); } static GURL ClearUrlRef(const GURL& url); + static std::unique_ptr<Network::Request> CreateRequestFromResourceRequest( + const network::ResourceRequest& request); static std::unique_ptr<Network::Request> CreateRequestFromURLRequest( const net::URLRequest* request); @@ -169,6 +181,7 @@ class NetworkHandler : public DevToolsDomainHandler, std::vector<std::pair<std::string, std::string>> extra_headers_; std::string host_id_; std::unique_ptr<InterceptionHandle> interception_handle_; + std::unique_ptr<DevToolsURLLoaderInterceptor> url_loader_interceptor_; bool bypass_service_worker_; bool cache_disabled_; base::WeakPtrFactory<NetworkHandler> weak_factory_; diff --git a/chromium/content/browser/devtools/protocol/page_handler.cc b/chromium/content/browser/devtools/protocol/page_handler.cc index a76eb43b702..8dc4689a515 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.cc +++ b/chromium/content/browser/devtools/protocol/page_handler.cc @@ -230,6 +230,7 @@ void PageHandler::DidRunJavaScriptDialog(const GURL& url, const base::string16& message, const base::string16& default_prompt, JavaScriptDialogType dialog_type, + bool has_non_devtools_handlers, JavaScriptDialogCallback callback) { if (!enabled_) return; @@ -241,10 +242,12 @@ void PageHandler::DidRunJavaScriptDialog(const GURL& url, if (dialog_type == JAVASCRIPT_DIALOG_TYPE_PROMPT) type = Page::DialogTypeEnum::Prompt; frontend_->JavascriptDialogOpening(url.spec(), base::UTF16ToUTF8(message), - type, base::UTF16ToUTF8(default_prompt)); + type, has_non_devtools_handlers, + base::UTF16ToUTF8(default_prompt)); } void PageHandler::DidRunBeforeUnloadConfirm(const GURL& url, + bool has_non_devtools_handlers, JavaScriptDialogCallback callback) { if (!enabled_) return; @@ -252,7 +255,7 @@ void PageHandler::DidRunBeforeUnloadConfirm(const GURL& url, pending_dialog_ = std::move(callback); frontend_->JavascriptDialogOpening(url.spec(), std::string(), Page::DialogTypeEnum::Beforeunload, - std::string()); + has_non_devtools_handlers, std::string()); } void PageHandler::DidCloseJavaScriptDialog(bool success, diff --git a/chromium/content/browser/devtools/protocol/page_handler.h b/chromium/content/browser/devtools/protocol/page_handler.h index 9c1b84e68d9..8e45be2d993 100644 --- a/chromium/content/browser/devtools/protocol/page_handler.h +++ b/chromium/content/browser/devtools/protocol/page_handler.h @@ -24,7 +24,7 @@ #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "content/public/common/javascript_dialog_type.h" -#include "third_party/WebKit/public/platform/modules/manifest/manifest_manager.mojom.h" +#include "third_party/blink/public/platform/modules/manifest/manifest_manager.mojom.h" #include "url/gurl.h" class SkBitmap; @@ -78,8 +78,10 @@ class PageHandler : public DevToolsDomainHandler, const base::string16& message, const base::string16& default_prompt, JavaScriptDialogType dialog_type, + bool has_non_devtools_handlers, JavaScriptDialogCallback callback); void DidRunBeforeUnloadConfirm(const GURL& url, + bool has_non_devtools_handlers, JavaScriptDialogCallback callback); void DidCloseJavaScriptDialog(bool success, const base::string16& user_input); void NavigationReset(NavigationRequest* navigation_request); diff --git a/chromium/content/browser/devtools/protocol/security_handler.cc b/chromium/content/browser/devtools/protocol/security_handler.cc index bd4a401fdca..bdc3c63acb8 100644 --- a/chromium/content/browser/devtools/protocol/security_handler.cc +++ b/chromium/content/browser/devtools/protocol/security_handler.cc @@ -20,7 +20,7 @@ #include "content/public/browser/web_contents_delegate.h" #include "net/cert/x509_certificate.h" #include "net/cert/x509_util.h" -#include "third_party/WebKit/public/platform/WebMixedContentContextType.h" +#include "third_party/blink/public/platform/web_mixed_content_context_type.h" namespace content { namespace protocol { @@ -141,6 +141,8 @@ void SecurityHandler::SetRenderer(int process_host_id, void SecurityHandler::DidChangeVisibleSecurityState() { DCHECK(enabled_); + if (!web_contents()->GetDelegate()) + return; SecurityStyleExplanations security_style_explanations; blink::WebSecurityStyle security_style = @@ -206,7 +208,7 @@ bool SecurityHandler::NotifyCertificateError(int cert_error, CertErrorCallback handler) { if (cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll) { if (handler) - handler.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE); + std::move(handler).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE); return true; } @@ -222,7 +224,7 @@ bool SecurityHandler::NotifyCertificateError(int cert_error, return false; } - cert_error_callbacks_[last_cert_error_id_] = handler; + cert_error_callbacks_[last_cert_error_id_] = std::move(handler); return true; } diff --git a/chromium/content/browser/devtools/protocol/service_worker_handler.cc b/chromium/content/browser/devtools/protocol/service_worker_handler.cc index 5501f6ad645..5449b409717 100644 --- a/chromium/content/browser/devtools/protocol/service_worker_handler.cc +++ b/chromium/content/browser/devtools/protocol/service_worker_handler.cc @@ -29,8 +29,8 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/push_event_payload.h" #include "content/public/common/push_messaging_status.mojom.h" -#include "third_party/WebKit/public/mojom/service_worker/service_worker_object.mojom.h" -#include "third_party/WebKit/public/mojom/service_worker/service_worker_provider_type.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" +#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h" #include "url/gurl.h" namespace content { @@ -213,8 +213,7 @@ Response ServiceWorkerHandler::Unregister(const std::string& scope_url) { return CreateDomainNotEnabledErrorResponse(); if (!context_) return CreateContextErrorResponse(); - context_->UnregisterServiceWorker(GURL(scope_url), - base::DoNothing::Repeatedly<bool>()); + context_->UnregisterServiceWorker(GURL(scope_url), base::DoNothing()); return Response::OK(); } @@ -223,8 +222,7 @@ Response ServiceWorkerHandler::StartWorker(const std::string& scope_url) { return CreateDomainNotEnabledErrorResponse(); if (!context_) return CreateContextErrorResponse(); - context_->StartServiceWorker( - GURL(scope_url), base::DoNothing::Repeatedly<ServiceWorkerStatusCode>()); + context_->StartServiceWorker(GURL(scope_url), base::DoNothing()); return Response::OK(); } diff --git a/chromium/content/browser/devtools/protocol/storage_handler.cc b/chromium/content/browser/devtools/protocol/storage_handler.cc index 2c621aa87f0..ad5bb6d37ed 100644 --- a/chromium/content/browser/devtools/protocol/storage_handler.cc +++ b/chromium/content/browser/devtools/protocol/storage_handler.cc @@ -12,11 +12,12 @@ #include "base/strings/string_split.h" #include "content/browser/cache_storage/cache_storage_context_impl.h" #include "content/browser/indexed_db/indexed_db_context_impl.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/storage_partition.h" #include "storage/browser/quota/quota_client.h" #include "storage/browser/quota/quota_manager.h" -#include "third_party/WebKit/public/mojom/quota/quota_types.mojom.h" +#include "third_party/blink/public/mojom/quota/quota_types.mojom.h" #include "url/gurl.h" #include "url/origin.h" @@ -88,8 +89,7 @@ void GetUsageAndQuotaOnIOThread( DCHECK_CURRENTLY_ON(BrowserThread::IO); manager->GetUsageAndQuotaWithBreakdown( url, blink::mojom::StorageType::kTemporary, - base::Bind(&GotUsageAndQuotaDataCallback, - base::Passed(std::move(callback)))); + base::BindOnce(&GotUsageAndQuotaDataCallback, std::move(callback))); } } // namespace diff --git a/chromium/content/browser/devtools/protocol/system_info_handler.cc b/chromium/content/browser/devtools/protocol/system_info_handler.cc index 5372bd781ab..3a90a35db77 100644 --- a/chromium/content/browser/devtools/protocol/system_info_handler.cc +++ b/chromium/content/browser/devtools/protocol/system_info_handler.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/command_line.h" -#include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "content/browser/gpu/compositor_util.h" diff --git a/chromium/content/browser/devtools/protocol/target_auto_attacher.cc b/chromium/content/browser/devtools/protocol/target_auto_attacher.cc index b460c7fa321..3d4b128b294 100644 --- a/chromium/content/browser/devtools/protocol/target_auto_attacher.cc +++ b/chromium/content/browser/devtools/protocol/target_auto_attacher.cc @@ -170,7 +170,10 @@ DevToolsAgentHost* TargetAutoAttacher::AutoAttachToFrame( DCHECK(old_cross_process); auto it = auto_attached_hosts_.find(agent_host); - DCHECK(it != auto_attached_hosts_.end()); + // This should not happen in theory, but error pages are sometimes not + // picked up. See https://crbug.com/836511 and https://crbug.com/817881. + if (it == auto_attached_hosts_.end()) + return nullptr; auto_attached_hosts_.erase(it); detach_callback_.Run(agent_host.get()); return nullptr; diff --git a/chromium/content/browser/devtools/protocol/tethering_handler.cc b/chromium/content/browser/devtools/protocol/tethering_handler.cc index 494aea4d93b..ce6738651b2 100644 --- a/chromium/content/browser/devtools/protocol/tethering_handler.cc +++ b/chromium/content/browser/devtools/protocol/tethering_handler.cc @@ -6,7 +6,6 @@ #include <map> -#include "base/memory/ptr_util.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" #include "net/base/ip_address.h" @@ -295,7 +294,7 @@ void TetheringHandler::TetheringImpl::Bind( BoundSocket::AcceptedCallback accepted = base::Bind( &TetheringHandler::TetheringImpl::Accepted, base::Unretained(this)); std::unique_ptr<BoundSocket> bound_socket = - std::make_unique<BoundSocket>(accepted, socket_callback_); + std::make_unique<BoundSocket>(std::move(accepted), socket_callback_); if (!bound_socket->Listen(port)) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, diff --git a/chromium/content/browser/devtools/protocol/tracing_handler.cc b/chromium/content/browser/devtools/protocol/tracing_handler.cc index e94eb77facf..ea314dff3c0 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler.cc +++ b/chromium/content/browser/devtools/protocol/tracing_handler.cc @@ -25,13 +25,19 @@ #include "base/trace_event/tracing_agent.h" #include "components/tracing/common/trace_config_file.h" #include "components/viz/common/features.h" -#include "content/browser/devtools/devtools_frame_trace_recorder_for_viz.h" +#include "content/browser/devtools/devtools_frame_trace_recorder.h" #include "content/browser/devtools/devtools_io_context.h" #include "content/browser/devtools/devtools_session.h" +#include "content/browser/devtools/devtools_traceable_screenshot.h" +#include "content/browser/devtools/devtools_video_consumer.h" +#include "content/browser/frame_host/frame_tree.h" +#include "content/browser/frame_host/frame_tree_node.h" +#include "content/browser/frame_host/navigation_handle_impl.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/tracing/tracing_controller_impl.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/content_features.h" #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" #include "services/tracing/public/mojom/constants.mojom.h" @@ -44,6 +50,17 @@ const double kMinimumReportingInterval = 250.0; const char kRecordModeParam[] = "record_mode"; +// Settings for |video_consumer_|. +// Tracing requires a 10ms minimum capture period. +constexpr base::TimeDelta kMinCapturePeriod = + base::TimeDelta::FromMilliseconds(10); + +// Frames need to be at least 1x1, otherwise nothing would be captured. +constexpr gfx::Size kMinFrameSize = gfx::Size(1, 1); + +// Frames do not need to be greater than 500x500 for tracing. +constexpr gfx::Size kMaxFrameSize = gfx::Size(500, 500); + // Convert from camel case to separator + lowercase. std::string ConvertFromCamelCase(const std::string& in_str, char separator) { std::string out_str; @@ -143,22 +160,66 @@ class DevToolsStreamEndpoint : public TracingController::TraceDataEndpoint { base::WeakPtr<TracingHandler> tracing_handler_; }; +std::string GetProcessHostHex(RenderProcessHost* host) { + return base::StringPrintf("0x%" PRIxPTR, reinterpret_cast<uintptr_t>(host)); +} + +void SendProcessReadyInBrowserEvent(const base::UnguessableToken& frame_token, + RenderProcessHost* host) { + auto data = std::make_unique<base::trace_event::TracedValue>(); + data->SetString("frame", frame_token.ToString()); + data->SetString("processPseudoId", GetProcessHostHex(host)); + data->SetInteger("processId", + static_cast<int>(base::GetProcId(host->GetHandle()))); + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), + "ProcessReadyInBrowser", TRACE_EVENT_SCOPE_THREAD, + "data", std::move(data)); +} + +void FillFrameData(base::trace_event::TracedValue* data, + FrameTreeNode* node, + RenderFrameHostImpl* frame_host, + const GURL& url) { + url::Replacements<char> strip_fragment; + strip_fragment.ClearRef(); + data->SetString("frame", node->devtools_frame_token().ToString()); + data->SetString("url", url.ReplaceComponents(strip_fragment).spec()); + data->SetString("name", node->frame_name()); + if (node->parent()) + data->SetString("parent", + node->parent()->devtools_frame_token().ToString()); + if (frame_host) { + RenderProcessHost* process_host = frame_host->GetProcess(); + base::ProcessId process_id = base::GetProcId(process_host->GetHandle()); + if (process_id == base::kNullProcessId) { + data->SetString("processPseudoId", GetProcessHostHex(process_host)); + frame_host->GetProcess()->PostTaskWhenProcessIsReady( + base::BindOnce(&SendProcessReadyInBrowserEvent, + node->devtools_frame_token(), process_host)); + } else { + // Cast process id to int to be compatible with tracing. + data->SetInteger("processId", static_cast<int>(process_id)); + } + } +} + } // namespace -TracingHandler::TracingHandler(TracingHandler::Target target, - int frame_tree_node_id, +TracingHandler::TracingHandler(FrameTreeNode* frame_tree_node_, DevToolsIOContext* io_context) : DevToolsDomainHandler(Tracing::Metainfo::domainName), - target_(target), io_context_(io_context), - frame_tree_node_id_(frame_tree_node_id), + frame_tree_node_(frame_tree_node_), did_initiate_recording_(false), return_as_stream_(false), gzip_compression_(false), weak_factory_(this) { - if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) { - frame_trace_recorder_ = - std::make_unique<DevToolsFrameTraceRecorderForViz>(); + if (base::FeatureList::IsEnabled(features::kVizDisplayCompositor) || + base::FeatureList::IsEnabled( + features::kUseVideoCaptureApiForDevToolsSnapshots)) { + video_consumer_ = + std::make_unique<DevToolsVideoConsumer>(base::BindRepeating( + &TracingHandler::OnFrameFromVideoConsumer, base::Unretained(this))); } } @@ -174,9 +235,9 @@ std::vector<TracingHandler*> TracingHandler::ForAgentHost( void TracingHandler::SetRenderer(int process_host_id, RenderFrameHostImpl* frame_host) { - if (!frame_trace_recorder_ || !frame_host) + if (!video_consumer_ || !frame_host) return; - frame_trace_recorder_->SetFrameSinkId( + video_consumer_->SetFrameSinkId( frame_host->GetRenderWidgetHost()->GetFrameSinkId()); } @@ -340,7 +401,7 @@ void TracingHandler::Start(Maybe<std::string> categories, // If inspected target is a render process Tracing.start will be handled by // tracing agent in the renderer. - if (target_ == Renderer) + if (frame_tree_node_) callback->fallThrough(); TracingController::GetInstance()->StartTracing( @@ -377,7 +438,7 @@ void TracingHandler::End(std::unique_ptr<EndCallback> callback) { } // If inspected target is a render process Tracing.end will be handled by // tracing agent in the renderer. - if (target_ == Renderer) + if (frame_tree_node_) callback->fallThrough(); else callback->sendSuccess(); @@ -393,17 +454,21 @@ void TracingHandler::GetCategories( void TracingHandler::OnRecordingEnabled( std::unique_ptr<StartCallback> callback) { - TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), - "TracingStartedInBrowser", TRACE_EVENT_SCOPE_THREAD, - "frameTreeNodeId", frame_tree_node_id_); - if (target_ != Renderer) + EmitFrameTree(); + + if (!frame_tree_node_) callback->sendSuccess(); bool screenshot_enabled; TRACE_EVENT_CATEGORY_GROUP_ENABLED( TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), &screenshot_enabled); - if (frame_trace_recorder_ && screenshot_enabled) - frame_trace_recorder_->StartCapture(); + if (video_consumer_ && screenshot_enabled) { + // Reset number of screenshots received, each time tracing begins. + number_of_screenshots_from_video_consumer_ = 0; + video_consumer_->SetMinCapturePeriod(kMinCapturePeriod); + video_consumer_->SetMinAndMaxFrameSize(kMinFrameSize, kMaxFrameSize); + video_consumer_->StartCapture(); + } } void TracingHandler::OnBufferUsage(float percent_full, @@ -437,7 +502,7 @@ void TracingHandler::RequestMemoryDump( ->RequestGlobalDumpAndAppendToTrace( base::trace_event::MemoryDumpType::EXPLICITLY_TRIGGERED, base::trace_event::MemoryDumpLevelOfDetail::DETAILED, - on_memory_dump_finished); + std::move(on_memory_dump_finished)); } void TracingHandler::OnMemoryDumpFinished( @@ -447,6 +512,27 @@ void TracingHandler::OnMemoryDumpFinished( callback->sendSuccess(base::StringPrintf("0x%" PRIx64, dump_id), success); } +void TracingHandler::OnFrameFromVideoConsumer( + scoped_refptr<media::VideoFrame> frame) { + const SkBitmap skbitmap = DevToolsVideoConsumer::GetSkBitmapFromFrame(frame); + + base::TimeTicks reference_time; + const bool had_reference_time = frame->metadata()->GetTimeTicks( + media::VideoFrameMetadata::REFERENCE_TIME, &reference_time); + DCHECK(had_reference_time); + + TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID_AND_TIMESTAMP( + TRACE_DISABLED_BY_DEFAULT("devtools.screenshot"), "Screenshot", 1, + reference_time, std::make_unique<DevToolsTraceableScreenshot>(skbitmap)); + + ++number_of_screenshots_from_video_consumer_; + DCHECK(video_consumer_); + if (number_of_screenshots_from_video_consumer_ >= + DevToolsFrameTraceRecorder::kMaximumNumberOfScreenshots) { + video_consumer_->StopCapture(); + } +} + Response TracingHandler::RecordClockSyncMarker(const std::string& sync_id) { if (!IsTracing()) return Response::Error("Tracing is not started"); @@ -478,17 +564,63 @@ void TracingHandler::StopTracing( buffer_usage_poll_timer_.reset(); TracingController::GetInstance()->StopTracing(endpoint, agent_label); did_initiate_recording_ = false; - if (frame_trace_recorder_) - frame_trace_recorder_->StopCapture(); + if (video_consumer_) + video_consumer_->StopCapture(); } bool TracingHandler::IsTracing() const { return TracingController::GetInstance()->IsTracing(); } +void TracingHandler::EmitFrameTree() { + auto data = std::make_unique<base::trace_event::TracedValue>(); + if (frame_tree_node_) { + data->SetInteger("frameTreeNodeId", frame_tree_node_->frame_tree_node_id()); + data->SetBoolean("persistentIds", true); + data->BeginArray("frames"); + FrameTree::NodeRange subtree = + frame_tree_node_->frame_tree()->SubtreeNodes(frame_tree_node_); + for (FrameTreeNode* node : subtree) { + data->BeginDictionary(); + FillFrameData(data.get(), node, node->current_frame_host(), + node->current_url()); + data->EndDictionary(); + } + data->EndArray(); + } + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), + "TracingStartedInBrowser", TRACE_EVENT_SCOPE_THREAD, + "data", std::move(data)); +} + +void TracingHandler::ReadyToCommitNavigation( + NavigationHandleImpl* navigation_handle) { + if (!did_initiate_recording_) + return; + auto data = std::make_unique<base::trace_event::TracedValue>(); + FillFrameData(data.get(), navigation_handle->frame_tree_node(), + navigation_handle->GetRenderFrameHost(), + navigation_handle->GetURL()); + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), + "FrameCommittedInBrowser", TRACE_EVENT_SCOPE_THREAD, + "data", std::move(data)); +} + +void TracingHandler::FrameDeleted(RenderFrameHostImpl* frame_host) { + if (!did_initiate_recording_) + return; + auto data = std::make_unique<base::trace_event::TracedValue>(); + data->SetString( + "frame", + frame_host->frame_tree_node()->devtools_frame_token().ToString()); + TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), + "FrameDeletedInBrowser", TRACE_EVENT_SCOPE_THREAD, + "data", std::move(data)); +} + +// static bool TracingHandler::IsStartupTracingActive() { - return ::tracing::TraceConfigFile::GetInstance()->IsEnabled() && - TracingController::GetInstance()->IsTracing(); + return ::tracing::TraceConfigFile::GetInstance()->IsEnabled(); } // static diff --git a/chromium/content/browser/devtools/protocol/tracing_handler.h b/chromium/content/browser/devtools/protocol/tracing_handler.h index 8515e3a3dfa..7f21754cc10 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler.h +++ b/chromium/content/browser/devtools/protocol/tracing_handler.h @@ -26,19 +26,23 @@ namespace base { class Timer; } +namespace media { +class VideoFrame; +} + namespace content { class DevToolsAgentHostImpl; -class DevToolsFrameTraceRecorderForViz; +class DevToolsVideoConsumer; class DevToolsIOContext; +class FrameTreeNode; +class NavigationHandleImpl; namespace protocol { class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend { public: - enum Target { Browser, Renderer }; - CONTENT_EXPORT TracingHandler(Target target, - int frame_tree_node_id, + CONTENT_EXPORT TracingHandler(FrameTreeNode* frame_tree_node, DevToolsIOContext* io_context); CONTENT_EXPORT ~TracingHandler() override; @@ -69,6 +73,8 @@ class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend { Response RecordClockSyncMarker(const std::string& sync_id) override; bool did_initiate_recording() { return did_initiate_recording_; } + void ReadyToCommitNavigation(NavigationHandleImpl* navigation_handle); + void FrameDeleted(RenderFrameHostImpl* frame_host); private: friend class TracingHandlerTest; @@ -90,7 +96,7 @@ class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend { void OnMemoryDumpFinished(std::unique_ptr<RequestMemoryDumpCallback> callback, bool success, uint64_t dump_id); - + void OnFrameFromVideoConsumer(scoped_refptr<media::VideoFrame> frame); // Assuming that the input is a potentially incomplete string representation // of a comma separated list of JSON objects, return the longest prefix that // is a valid list and store the rest to be used in subsequent calls. @@ -102,22 +108,23 @@ class TracingHandler : public DevToolsDomainHandler, public Tracing::Backend { const scoped_refptr<TracingController::TraceDataEndpoint>& endpoint, const std::string& agent_label); bool IsTracing() const; + void EmitFrameTree(); static bool IsStartupTracingActive(); CONTENT_EXPORT static base::trace_event::TraceConfig GetTraceConfigFromDevToolsConfig( const base::DictionaryValue& devtools_config); std::unique_ptr<base::Timer> buffer_usage_poll_timer_; - Target target_; std::unique_ptr<Tracing::Frontend> frontend_; DevToolsIOContext* io_context_; - int frame_tree_node_id_; + FrameTreeNode* frame_tree_node_; bool did_initiate_recording_; bool return_as_stream_; bool gzip_compression_; TraceDataBufferState trace_data_buffer_state_; - std::unique_ptr<DevToolsFrameTraceRecorderForViz> frame_trace_recorder_; + std::unique_ptr<DevToolsVideoConsumer> video_consumer_; + int number_of_screenshots_from_video_consumer_ = 0; base::WeakPtrFactory<TracingHandler> weak_factory_; FRIEND_TEST_ALL_PREFIXES(TracingHandlerTest, diff --git a/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc b/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc index 5af1a8d65d4..a392742902a 100644 --- a/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc +++ b/chromium/content/browser/devtools/protocol/tracing_handler_unittest.cc @@ -72,8 +72,7 @@ const char kCustomTraceConfigStringDevToolsStyle[] = class TracingHandlerTest : public testing::Test { public: void SetUp() override { - tracing_handler_.reset( - new TracingHandler(TracingHandler::Browser, 0, nullptr)); + tracing_handler_.reset(new TracingHandler(nullptr, nullptr)); } void TearDown() override { tracing_handler_.reset(); } diff --git a/chromium/content/browser/devtools/protocol_config.json b/chromium/content/browser/devtools/protocol_config.json index 4e9d341cc03..f40789fe6f4 100644 --- a/chromium/content/browser/devtools/protocol_config.json +++ b/chromium/content/browser/devtools/protocol_config.json @@ -11,7 +11,7 @@ "options": [ { "domain": "Browser", - "include": ["getVersion", "getHistograms", "getHistogram", "getCommandLine"] + "include": ["getVersion", "getHistograms", "getHistogram", "getBrowserCommandLine"] }, { "domain": "DOM", diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc index cdeca684d4f..f6977596a09 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.cc @@ -12,6 +12,7 @@ #include "base/memory/ptr_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "components/viz/common/features.h" #include "content/browser/bad_message.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/devtools/devtools_frame_trace_recorder.h" @@ -42,17 +43,19 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/common/view_messages.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/navigation_handle.h" #include "content/public/browser/network_service_instance.h" #include "content/public/browser/render_widget_host_iterator.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/common/browser_side_navigation_policy.h" +#include "content/public/common/content_features.h" #include "mojo/public/cpp/bindings/associated_binding.h" #include "net/base/load_flags.h" #include "net/http/http_request_headers.h" #include "services/network/public/cpp/features.h" -#include "third_party/WebKit/public/common/associated_interfaces/associated_interface_provider.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" #if defined(OS_ANDROID) #include "content/public/browser/render_widget_host_view.h" @@ -272,6 +275,7 @@ void RenderFrameDevToolsAgentHost::ApplyOverrides( net::HttpRequestHeaders headers; headers.AddHeadersFromString(begin_params->headers); for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) { + // TODO(caseq): consider chaining intercepting proxies from multiple agents. if (!network->enabled()) continue; *report_raw_headers = true; @@ -289,6 +293,27 @@ void RenderFrameDevToolsAgentHost::ApplyOverrides( } // static +bool RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( + RenderFrameHostImpl* rfh, + bool is_navigation, + network::mojom::URLLoaderFactoryRequest* target_factory_request) { + FrameTreeNode* frame_tree_node = rfh->frame_tree_node(); + base::UnguessableToken frame_token = frame_tree_node->devtools_frame_token(); + frame_tree_node = GetFrameTreeNodeAncestor(frame_tree_node); + RenderFrameDevToolsAgentHost* agent_host = FindAgentHost(frame_tree_node); + if (!agent_host) + return false; + int process_id = is_navigation ? 0 : rfh->GetProcess()->GetID(); + for (auto* network : protocol::NetworkHandler::ForAgentHost(agent_host)) { + if (network->MaybeCreateProxyForInterception(frame_token, process_id, + target_factory_request)) { + return true; + } + } + return false; +} + +// static void RenderFrameDevToolsAgentHost::OnNavigationRequestWillBeSent( const NavigationRequest& navigation_request) { DispatchToAgents(navigation_request.frame_tree_node(), @@ -342,7 +367,9 @@ WebContents* RenderFrameDevToolsAgentHost::GetWebContents() { return web_contents(); } -void RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { + if (session->restricted() && !IsFrameHostAllowedForRestrictedSessions()) + return false; session->SetRenderer(frame_host_ ? frame_host_->GetProcess()->GetID() : ChildProcessHost::kInvalidUniqueID, frame_host_); @@ -361,26 +388,34 @@ void RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(base::WrapUnique(new protocol::SchemaHandler())); session->AddHandler(base::WrapUnique(new protocol::ServiceWorkerHandler())); session->AddHandler(base::WrapUnique(new protocol::StorageHandler())); - session->AddHandler( - base::WrapUnique(new protocol::TargetHandler(false /* browser_only */))); - session->AddHandler(base::WrapUnique(new protocol::TracingHandler( - protocol::TracingHandler::Renderer, - frame_tree_node_ ? frame_tree_node_->frame_tree_node_id() : 0, - GetIOContext()))); + if (!session->restricted()) { + session->AddHandler(base::WrapUnique( + new protocol::TargetHandler(false /* browser_only */))); + } session->AddHandler( base::WrapUnique(new protocol::PageHandler(emulation_handler))); session->AddHandler(base::WrapUnique(new protocol::SecurityHandler())); + if (!frame_tree_node_ || !frame_tree_node_->parent()) { + session->AddHandler(base::WrapUnique( + new protocol::TracingHandler(frame_tree_node_, GetIOContext()))); + } if (EnsureAgent()) session->AttachToAgent(agent_ptr_); if (sessions().size() == 1) { - frame_trace_recorder_.reset(new DevToolsFrameTraceRecorder()); + // Taking screenshots using the video capture API is done in TracingHandler. + if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor) && + !base::FeatureList::IsEnabled( + features::kUseVideoCaptureApiForDevToolsSnapshots)) { + frame_trace_recorder_.reset(new DevToolsFrameTraceRecorder()); + } GrantPolicy(); #if defined(OS_ANDROID) GetWakeLock()->RequestWakeLock(); #endif } + return true; } void RenderFrameDevToolsAgentHost::DetachSession(DevToolsSession* session) { @@ -431,6 +466,9 @@ void RenderFrameDevToolsAgentHost::ReadyToCommitNavigation( NavigationHandle* navigation_handle) { NavigationHandleImpl* handle = static_cast<NavigationHandleImpl*>(navigation_handle); + for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) + tracing->ReadyToCommitNavigation(handle); + if (handle->frame_tree_node() != frame_tree_node_) { if (ShouldForceCreation() && handle->GetRenderFrameHost() && handle->GetRenderFrameHost()->IsCrossProcessSubframe()) { @@ -487,13 +525,19 @@ void RenderFrameDevToolsAgentHost::UpdateFrameHost( if (IsAttached()) RevokePolicy(); + frame_host_ = frame_host; agent_ptr_.reset(); + + if (!IsFrameHostAllowedForRestrictedSessions()) + ForceDetachRestrictedSessions(); + if (!render_frame_alive_) { render_frame_alive_ = true; for (auto* inspector : protocol::InspectorHandler::ForAgentHost(this)) inspector->TargetReloadedAfterCrash(); } + if (IsAttached()) { GrantPolicy(); for (DevToolsSession* session : sessions()) { @@ -570,9 +614,12 @@ void RenderFrameDevToolsAgentHost::RenderFrameHostChanged( } void RenderFrameDevToolsAgentHost::FrameDeleted(RenderFrameHost* rfh) { - if (static_cast<RenderFrameHostImpl*>(rfh)->frame_tree_node() == - frame_tree_node_) { - DestroyOnRenderFrameGone(); // |this| may be deleted at this point. + RenderFrameHostImpl* host = static_cast<RenderFrameHostImpl*>(rfh); + for (auto* tracing : protocol::TracingHandler::ForAgentHost(this)) + tracing->FrameDeleted(host); + if (host->frame_tree_node() == frame_tree_node_) { + DestroyOnRenderFrameGone(); + // |this| may be deleted at this point. } } @@ -587,7 +634,7 @@ void RenderFrameDevToolsAgentHost::DestroyOnRenderFrameGone() { scoped_refptr<RenderFrameDevToolsAgentHost> protect(this); if (IsAttached()) RevokePolicy(); - ForceDetachAllClients(); + ForceDetachAllSessions(); frame_host_ = nullptr; agent_ptr_.reset(); SetFrameTreeNode(nullptr); @@ -662,8 +709,6 @@ void RenderFrameDevToolsAgentHost::DidReceiveCompositorFrame() { ->last_frame_metadata(); for (auto* page : protocol::PageHandler::ForAgentHost(this)) page->OnSwapCompositorFrame(metadata.Clone()); - for (auto* input : protocol::InputHandler::ForAgentHost(this)) - input->OnSwapCompositorFrame(metadata); if (!frame_trace_recorder_) return; @@ -674,6 +719,12 @@ void RenderFrameDevToolsAgentHost::DidReceiveCompositorFrame() { frame_trace_recorder_->OnSwapCompositorFrame(frame_host_, metadata); } +void RenderFrameDevToolsAgentHost::OnPageScaleFactorChanged( + float page_scale_factor) { + for (auto* input : protocol::InputHandler::ForAgentHost(this)) + input->OnPageScaleFactorChanged(page_scale_factor); +} + void RenderFrameDevToolsAgentHost::DisconnectWebContents() { navigation_handles_.clear(); SetFrameTreeNode(nullptr); @@ -822,8 +873,6 @@ void RenderFrameDevToolsAgentHost::SynchronousSwapCompositorFrame( viz::CompositorFrameMetadata frame_metadata) { for (auto* page : protocol::PageHandler::ForAgentHost(this)) page->OnSynchronousSwapCompositorFrame(frame_metadata.Clone()); - for (auto* input : protocol::InputHandler::ForAgentHost(this)) - input->OnSwapCompositorFrame(frame_metadata); if (!frame_trace_recorder_) return; @@ -848,4 +897,9 @@ bool RenderFrameDevToolsAgentHost::IsChildFrame() { return frame_tree_node_ && frame_tree_node_->parent(); } +bool RenderFrameDevToolsAgentHost::IsFrameHostAllowedForRestrictedSessions() { + return !frame_host_ || + (!frame_host_->web_ui() && !frame_host_->pending_web_ui()); +} + } // namespace content diff --git a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h index 8877e6c3f18..afd61919b40 100644 --- a/chromium/content/browser/devtools/render_frame_devtools_agent_host.h +++ b/chromium/content/browser/devtools/render_frame_devtools_agent_host.h @@ -18,7 +18,8 @@ #include "content/common/navigation_params.mojom.h" #include "content/public/browser/web_contents_observer.h" #include "net/base/net_errors.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" #if defined(OS_ANDROID) #include "services/device/public/mojom/wake_lock.mojom.h" @@ -69,6 +70,11 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost static void ApplyOverrides(FrameTreeNode* frame_tree_node, mojom::BeginNavigationParams* begin_params, bool* report_raw_headers); + static bool WillCreateURLLoaderFactory( + RenderFrameHostImpl* rfh, + bool is_navigation, + network::mojom::URLLoaderFactoryRequest* loader_factory_request); + static void OnNavigationRequestWillBeSent( const NavigationRequest& navigation_request); static void OnNavigationResponseReceived( @@ -114,7 +120,7 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost ~RenderFrameDevToolsAgentHost() override; // DevToolsAgentHostImpl overrides. - void AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session) override; void DetachSession(DevToolsSession* session) override; void InspectElement(RenderFrameHost* frame_host, int x, int y) override; void DispatchProtocolMessage(DevToolsSession* session, @@ -133,8 +139,10 @@ class CONTENT_EXPORT RenderFrameDevToolsAgentHost void DidDetachInterstitialPage() override; void OnVisibilityChanged(content::Visibility visibility) override; void DidReceiveCompositorFrame() override; + void OnPageScaleFactorChanged(float page_scale_factor) override; bool IsChildFrame(); + bool IsFrameHostAllowedForRestrictedSessions(); void OnSwapCompositorFrame(const IPC::Message& message); void DestroyOnRenderFrameGone(); diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc index 71ea6f0d4e7..b4886cc9754 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.cc @@ -115,7 +115,7 @@ ServiceWorkerDevToolsAgentHost::~ServiceWorkerDevToolsAgentHost() { ServiceWorkerDevToolsManager::GetInstance()->AgentHostDestroyed(this); } -void ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { if (state_ == WORKER_READY) { if (sessions().size() == 1) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, @@ -128,6 +128,7 @@ void ServiceWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(base::WrapUnique(new protocol::InspectorHandler())); session->AddHandler(base::WrapUnique(new protocol::NetworkHandler(GetId()))); session->AddHandler(base::WrapUnique(new protocol::SchemaHandler())); + return true; } void ServiceWorkerDevToolsAgentHost::DetachSession(DevToolsSession* session) { diff --git a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h index 59af14400fa..456e9938e8e 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_agent_host.h +++ b/chromium/content/browser/devtools/service_worker_devtools_agent_host.h @@ -14,7 +14,7 @@ #include "base/unguessable_token.h" #include "content/browser/devtools/devtools_agent_host_impl.h" #include "content/browser/devtools/service_worker_devtools_manager.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" namespace content { @@ -47,7 +47,7 @@ class ServiceWorkerDevToolsAgentHost : public DevToolsAgentHostImpl { bool Close() override; // DevToolsAgentHostImpl overrides. - void AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session) override; void DetachSession(DevToolsSession* session) override; void DispatchProtocolMessage(DevToolsSession* session, const std::string& message) override; diff --git a/chromium/content/browser/devtools/service_worker_devtools_manager.h b/chromium/content/browser/devtools/service_worker_devtools_manager.h index a660f6a1410..1018d40705a 100644 --- a/chromium/content/browser/devtools/service_worker_devtools_manager.h +++ b/chromium/content/browser/devtools/service_worker_devtools_manager.h @@ -16,7 +16,7 @@ #include "base/observer_list.h" #include "base/unguessable_token.h" #include "content/common/content_export.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" #include "url/gurl.h" namespace network { diff --git a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc index e4e0d316406..485edd42a67 100644 --- a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc +++ b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.cc @@ -66,13 +66,14 @@ bool SharedWorkerDevToolsAgentHost::Close() { return true; } -void SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { +bool SharedWorkerDevToolsAgentHost::AttachSession(DevToolsSession* session) { session->AddHandler(std::make_unique<protocol::InspectorHandler>()); session->AddHandler(std::make_unique<protocol::NetworkHandler>(GetId())); session->AddHandler(std::make_unique<protocol::SchemaHandler>()); session->SetRenderer(worker_host_ ? worker_host_->process_id() : -1, nullptr); if (state_ == WORKER_READY) session->AttachToAgent(EnsureAgent()); + return true; } void SharedWorkerDevToolsAgentHost::DetachSession(DevToolsSession* session) { diff --git a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.h b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.h index 15c5de57b59..6a1d800795b 100644 --- a/chromium/content/browser/devtools/shared_worker_devtools_agent_host.h +++ b/chromium/content/browser/devtools/shared_worker_devtools_agent_host.h @@ -8,7 +8,7 @@ #include "base/macros.h" #include "base/unguessable_token.h" #include "content/browser/devtools/devtools_agent_host_impl.h" -#include "third_party/WebKit/public/web/devtools_agent.mojom.h" +#include "third_party/blink/public/web/devtools_agent.mojom.h" namespace content { @@ -33,7 +33,7 @@ class SharedWorkerDevToolsAgentHost : public DevToolsAgentHostImpl { bool Close() override; // DevToolsAgentHostImpl overrides. - void AttachSession(DevToolsSession* session) override; + bool AttachSession(DevToolsSession* session) override; void DetachSession(DevToolsSession* session) override; void DispatchProtocolMessage(DevToolsSession* session, const std::string& message) override; |