| /* |
| * Copyright (C) 2010-2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.h" |
| |
| #include <v8-inspector.h> |
| #include <memory> |
| #include <utility> |
| |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/public/platform/web_float_rect.h" |
| #include "third_party/blink/public/platform/web_rect.h" |
| #include "third_party/blink/public/platform/web_string.h" |
| #include "third_party/blink/public/web/web_settings.h" |
| #include "third_party/blink/public/web/web_view_client.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_controller.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/core/core_initializer.h" |
| #include "third_party/blink/renderer/core/core_probe_sink.h" |
| #include "third_party/blink/renderer/core/events/web_input_event_conversion.h" |
| #include "third_party/blink/renderer/core/exported/web_settings_impl.h" |
| #include "third_party/blink/renderer/core/exported/web_view_impl.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/frame/web_frame_widget_base.h" |
| #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" |
| #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h" |
| #include "third_party/blink/renderer/core/inspector/devtools_agent.h" |
| #include "third_party/blink/renderer/core/inspector/devtools_session.h" |
| #include "third_party/blink/renderer/core/inspector/inspected_frames.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_animation_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_application_cache_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_audits_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_css_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_dom_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_dom_debugger_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_dom_snapshot_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_emulation_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_io_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_layer_tree_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_log_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_memory_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_network_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_overlay_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_page_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_performance_agent.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_resource_container.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_resource_content_loader.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_task_runner.h" |
| #include "third_party/blink/renderer/core/inspector/inspector_testing_agent.h" |
| #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h" |
| #include "third_party/blink/renderer/core/layout/layout_view.h" |
| #include "third_party/blink/renderer/core/page/focus_controller.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/platform/cross_thread_functional.h" |
| #include "third_party/blink/renderer/platform/graphics/graphics_context.h" |
| #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h" |
| #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h" |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" |
| #include "third_party/blink/renderer/platform/wtf/vector.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| bool IsMainFrame(WebLocalFrameImpl* frame) { |
| // TODO(dgozman): sometimes view->mainFrameImpl() does return null, even |
| // though |frame| is meant to be main frame. See http://crbug.com/526162. |
| return frame->ViewImpl() && !frame->Parent(); |
| } |
| |
| } // namespace |
| |
| class ClientMessageLoopAdapter : public MainThreadDebugger::ClientMessageLoop { |
| public: |
| ~ClientMessageLoopAdapter() override { instance_ = nullptr; } |
| |
| static void EnsureMainThreadDebuggerCreated() { |
| if (instance_) |
| return; |
| std::unique_ptr<ClientMessageLoopAdapter> instance( |
| new ClientMessageLoopAdapter( |
| Platform::Current()->CreateNestedMessageLoopRunner())); |
| instance_ = instance.get(); |
| MainThreadDebugger::Instance()->SetClientMessageLoop(std::move(instance)); |
| } |
| |
| static void ContinueProgram() { |
| // Release render thread if necessary. |
| if (instance_) |
| instance_->QuitNow(); |
| } |
| |
| private: |
| ClientMessageLoopAdapter( |
| std::unique_ptr<Platform::NestedMessageLoopRunner> message_loop) |
| : running_for_debug_break_(false), |
| message_loop_(std::move(message_loop)) { |
| DCHECK(message_loop_.get()); |
| } |
| |
| void Run(LocalFrame* frame) override { |
| if (running_for_debug_break_) |
| return; |
| |
| running_for_debug_break_ = true; |
| RunLoop(WebLocalFrameImpl::FromFrame(frame)); |
| } |
| |
| void RunLoop(WebLocalFrameImpl* frame) { |
| // 0. Flush pending frontend messages. |
| WebDevToolsAgentImpl* agent = frame->DevToolsAgentImpl(); |
| agent->FlushProtocolNotifications(); |
| |
| // 1. Disable input events. |
| WebFrameWidgetBase::SetIgnoreInputEvents(true); |
| for (auto* const view : WebViewImpl::AllInstances()) |
| view->GetChromeClient().NotifyPopupOpeningObservers(); |
| |
| // 2. Disable active objects |
| WebView::WillEnterModalLoop(); |
| |
| // 3. Process messages until quitNow is called. |
| message_loop_->Run(); |
| } |
| |
| void QuitNow() override { |
| if (running_for_debug_break_) { |
| running_for_debug_break_ = false; |
| // Undo steps (3), (2) and (1) from above. |
| // NOTE: This code used to be above right after the |mesasge_loop_->Run()| |
| // code, but it is moved here to support browser-side navigation. |
| message_loop_->QuitNow(); |
| WebView::DidExitModalLoop(); |
| WebFrameWidgetBase::SetIgnoreInputEvents(false); |
| } |
| } |
| |
| void RunIfWaitingForDebugger(LocalFrame* frame) override { |
| WebDevToolsAgentImpl* agent = |
| WebLocalFrameImpl::FromFrame(frame)->DevToolsAgentImpl(); |
| if (agent && agent->worker_client_) |
| agent->worker_client_->ResumeStartup(); |
| } |
| |
| bool running_for_debug_break_; |
| std::unique_ptr<Platform::NestedMessageLoopRunner> message_loop_; |
| |
| static ClientMessageLoopAdapter* instance_; |
| }; |
| |
| ClientMessageLoopAdapter* ClientMessageLoopAdapter::instance_ = nullptr; |
| |
| void WebDevToolsAgentImpl::AttachSession(DevToolsSession* session, |
| bool restore) { |
| if (!network_agents_.size()) |
| Thread::Current()->AddTaskObserver(this); |
| |
| ClientMessageLoopAdapter::EnsureMainThreadDebuggerCreated(); |
| MainThreadDebugger* main_thread_debugger = MainThreadDebugger::Instance(); |
| v8::Isolate* isolate = V8PerIsolateData::MainThreadIsolate(); |
| InspectedFrames* inspected_frames = inspected_frames_.Get(); |
| |
| int context_group_id = |
| main_thread_debugger->ContextGroupId(inspected_frames->Root()); |
| session->ConnectToV8(main_thread_debugger->GetV8Inspector(), |
| context_group_id); |
| |
| InspectorDOMAgent* dom_agent = MakeGarbageCollected<InspectorDOMAgent>( |
| isolate, inspected_frames, session->V8Session()); |
| session->Append(dom_agent); |
| |
| InspectorLayerTreeAgent* layer_tree_agent = |
| InspectorLayerTreeAgent::Create(inspected_frames, this); |
| session->Append(layer_tree_agent); |
| |
| InspectorNetworkAgent* network_agent = |
| MakeGarbageCollected<InspectorNetworkAgent>(inspected_frames, nullptr, |
| session->V8Session()); |
| session->Append(network_agent); |
| |
| InspectorCSSAgent* css_agent = InspectorCSSAgent::Create( |
| dom_agent, inspected_frames, network_agent, |
| resource_content_loader_.Get(), resource_container_.Get()); |
| session->Append(css_agent); |
| |
| InspectorDOMDebuggerAgent* dom_debugger_agent = |
| MakeGarbageCollected<InspectorDOMDebuggerAgent>(isolate, dom_agent, |
| session->V8Session()); |
| session->Append(dom_debugger_agent); |
| |
| session->Append( |
| InspectorDOMSnapshotAgent::Create(inspected_frames, dom_debugger_agent)); |
| |
| session->Append(MakeGarbageCollected<InspectorAnimationAgent>( |
| inspected_frames, css_agent, session->V8Session())); |
| |
| session->Append(InspectorMemoryAgent::Create(inspected_frames)); |
| |
| session->Append(InspectorPerformanceAgent::Create(inspected_frames)); |
| |
| session->Append(InspectorApplicationCacheAgent::Create(inspected_frames)); |
| |
| InspectorPageAgent* page_agent = InspectorPageAgent::Create( |
| inspected_frames, this, resource_content_loader_.Get(), |
| session->V8Session()); |
| session->Append(page_agent); |
| |
| session->Append(MakeGarbageCollected<InspectorLogAgent>( |
| &inspected_frames->Root()->GetPage()->GetConsoleMessageStorage(), |
| inspected_frames->Root()->GetPerformanceMonitor(), session->V8Session())); |
| |
| InspectorOverlayAgent* overlay_agent = |
| MakeGarbageCollected<InspectorOverlayAgent>( |
| web_local_frame_impl_.Get(), inspected_frames, session->V8Session(), |
| dom_agent); |
| session->Append(overlay_agent); |
| |
| session->Append( |
| MakeGarbageCollected<InspectorIOAgent>(isolate, session->V8Session())); |
| |
| session->Append(MakeGarbageCollected<InspectorAuditsAgent>(network_agent)); |
| |
| // TODO(dgozman): we should actually pass the view instead of frame, but |
| // during remote->local transition we cannot access mainFrameImpl() yet, so |
| // we have to store the frame which will become the main frame later. |
| session->Append(MakeGarbageCollected<InspectorEmulationAgent>( |
| web_local_frame_impl_.Get())); |
| |
| session->Append( |
| MakeGarbageCollected<InspectorTestingAgent>(inspected_frames)); |
| |
| // Call session init callbacks registered from higher layers. |
| CoreInitializer::GetInstance().InitInspectorAgentSession( |
| session, include_view_agents_, dom_agent, inspected_frames, |
| web_local_frame_impl_->ViewImpl()->GetPage()); |
| |
| if (restore && worker_client_) |
| worker_client_->ResumeStartup(); |
| |
| if (node_to_inspect_) { |
| overlay_agent->Inspect(node_to_inspect_); |
| node_to_inspect_ = nullptr; |
| } |
| |
| network_agents_.insert(session, network_agent); |
| page_agents_.insert(session, page_agent); |
| overlay_agents_.insert(session, overlay_agent); |
| } |
| |
| // static |
| WebDevToolsAgentImpl* WebDevToolsAgentImpl::CreateForFrame( |
| WebLocalFrameImpl* frame) { |
| return MakeGarbageCollected<WebDevToolsAgentImpl>(frame, IsMainFrame(frame), |
| nullptr); |
| } |
| |
| // static |
| WebDevToolsAgentImpl* WebDevToolsAgentImpl::CreateForWorker( |
| WebLocalFrameImpl* frame, |
| WorkerClient* worker_client) { |
| return MakeGarbageCollected<WebDevToolsAgentImpl>(frame, true, worker_client); |
| } |
| |
| WebDevToolsAgentImpl::WebDevToolsAgentImpl( |
| WebLocalFrameImpl* web_local_frame_impl, |
| bool include_view_agents, |
| WorkerClient* worker_client) |
| : worker_client_(worker_client), |
| web_local_frame_impl_(web_local_frame_impl), |
| probe_sink_(web_local_frame_impl_->GetFrame()->GetProbeSink()), |
| resource_content_loader_(InspectorResourceContentLoader::Create( |
| web_local_frame_impl_->GetFrame())), |
| inspected_frames_(MakeGarbageCollected<InspectedFrames>( |
| web_local_frame_impl_->GetFrame())), |
| resource_container_( |
| MakeGarbageCollected<InspectorResourceContainer>(inspected_frames_)), |
| include_view_agents_(include_view_agents) { |
| DCHECK(IsMainThread()); |
| agent_ = MakeGarbageCollected<DevToolsAgent>( |
| this, inspected_frames_.Get(), probe_sink_.Get(), |
| web_local_frame_impl_->GetFrame()->GetInspectorTaskRunner(), |
| Platform::Current()->GetIOTaskRunner()); |
| } |
| |
| WebDevToolsAgentImpl::~WebDevToolsAgentImpl() { |
| DCHECK(!worker_client_); |
| } |
| |
| void WebDevToolsAgentImpl::Trace(blink::Visitor* visitor) { |
| visitor->Trace(agent_); |
| visitor->Trace(network_agents_); |
| visitor->Trace(page_agents_); |
| visitor->Trace(overlay_agents_); |
| visitor->Trace(web_local_frame_impl_); |
| visitor->Trace(probe_sink_); |
| visitor->Trace(resource_content_loader_); |
| visitor->Trace(inspected_frames_); |
| visitor->Trace(resource_container_); |
| visitor->Trace(node_to_inspect_); |
| } |
| |
| void WebDevToolsAgentImpl::WillBeDestroyed() { |
| DCHECK(web_local_frame_impl_->GetFrame()); |
| DCHECK(inspected_frames_->Root()->View()); |
| agent_->Dispose(); |
| resource_content_loader_->Dispose(); |
| worker_client_ = nullptr; |
| } |
| |
| void WebDevToolsAgentImpl::BindRequest( |
| mojom::blink::DevToolsAgentHostAssociatedPtrInfo host_ptr_info, |
| mojom::blink::DevToolsAgentAssociatedRequest request) { |
| agent_->BindRequest( |
| std::move(host_ptr_info), std::move(request), |
| web_local_frame_impl_->GetTaskRunner(TaskType::kInternalInspector)); |
| } |
| |
| void WebDevToolsAgentImpl::DetachSession(DevToolsSession* session) { |
| network_agents_.erase(session); |
| page_agents_.erase(session); |
| overlay_agents_.erase(session); |
| if (!network_agents_.size()) |
| Thread::Current()->RemoveTaskObserver(this); |
| } |
| |
| void WebDevToolsAgentImpl::InspectElement(const WebPoint& point_in_local_root) { |
| WebPoint point = point_in_local_root; |
| // TODO(dgozman): the ViewImpl() check must not be necessary, |
| // but it is required when attaching early to a provisional frame. |
| // We should clean this up once provisional frames are gone. |
| // See https://crbug.com/578349. |
| if (web_local_frame_impl_->ViewImpl() && |
| web_local_frame_impl_->ViewImpl()->Client()) { |
| WebFloatRect rect(point.x, point.y, 0, 0); |
| web_local_frame_impl_->ViewImpl()->WidgetClient()->ConvertWindowToViewport( |
| &rect); |
| point = WebPoint(rect.x, rect.y); |
| } |
| |
| HitTestRequest::HitTestRequestType hit_type = |
| HitTestRequest::kMove | HitTestRequest::kReadOnly | |
| HitTestRequest::kAllowChildFrameContent; |
| HitTestRequest request(hit_type); |
| WebMouseEvent dummy_event(WebInputEvent::kMouseDown, |
| WebInputEvent::kNoModifiers, |
| WTF::CurrentTimeTicks()); |
| dummy_event.SetPositionInWidget(point.x, point.y); |
| IntPoint transformed_point = FlooredIntPoint( |
| TransformWebMouseEvent(web_local_frame_impl_->GetFrameView(), dummy_event) |
| .PositionInRootFrame()); |
| HitTestLocation location( |
| web_local_frame_impl_->GetFrameView()->ConvertFromRootFrame( |
| transformed_point)); |
| HitTestResult result(request, location); |
| web_local_frame_impl_->GetFrame()->ContentLayoutObject()->HitTest(location, |
| result); |
| Node* node = result.InnerNode(); |
| if (!node && web_local_frame_impl_->GetFrame()->GetDocument()) |
| node = web_local_frame_impl_->GetFrame()->GetDocument()->documentElement(); |
| |
| if (!overlay_agents_.IsEmpty()) { |
| for (auto& it : overlay_agents_) |
| it.value->Inspect(node); |
| } else { |
| node_to_inspect_ = node; |
| } |
| } |
| |
| void WebDevToolsAgentImpl::DebuggerTaskStarted() {} |
| |
| void WebDevToolsAgentImpl::DebuggerTaskFinished() {} |
| |
| void WebDevToolsAgentImpl::DidCommitLoadForLocalFrame(LocalFrame* frame) { |
| resource_container_->DidCommitLoadForLocalFrame(frame); |
| resource_content_loader_->DidCommitLoadForLocalFrame(frame); |
| } |
| |
| bool WebDevToolsAgentImpl::ScreencastEnabled() { |
| for (auto& it : page_agents_) { |
| if (it.value->ScreencastEnabled()) |
| return true; |
| } |
| return false; |
| } |
| |
| void WebDevToolsAgentImpl::PageLayoutInvalidated(bool resized) { |
| for (auto& it : overlay_agents_) |
| it.value->PageLayoutInvalidated(resized); |
| } |
| |
| bool WebDevToolsAgentImpl::IsInspectorLayer(GraphicsLayer* layer) { |
| for (auto& it : overlay_agents_) { |
| if (it.value->IsInspectorLayer(layer)) |
| return true; |
| } |
| return false; |
| } |
| |
| String WebDevToolsAgentImpl::EvaluateInOverlayForTesting(const String& script) { |
| String result; |
| for (auto& it : overlay_agents_) |
| result = it.value->EvaluateInOverlayForTest(script); |
| return result; |
| } |
| |
| void WebDevToolsAgentImpl::UpdateOverlays() { |
| for (auto& it : overlay_agents_) |
| it.value->UpdateAllOverlayLifecyclePhases(); |
| } |
| |
| void WebDevToolsAgentImpl::DispatchBufferedTouchEvents() { |
| for (auto& it : overlay_agents_) |
| it.value->DispatchBufferedTouchEvents(); |
| } |
| |
| bool WebDevToolsAgentImpl::HandleInputEvent(const WebInputEvent& event) { |
| for (auto& it : overlay_agents_) { |
| if (it.value->HandleInputEvent(event)) |
| return true; |
| } |
| return false; |
| } |
| |
| String WebDevToolsAgentImpl::NavigationInitiatorInfo(LocalFrame* frame) { |
| for (auto& it : network_agents_) { |
| String initiator = it.value->NavigationInitiatorInfo(frame); |
| if (!initiator.IsNull()) |
| return initiator; |
| } |
| return String(); |
| } |
| |
| void WebDevToolsAgentImpl::FlushProtocolNotifications() { |
| agent_->FlushProtocolNotifications(); |
| } |
| |
| void WebDevToolsAgentImpl::WillProcessTask( |
| const base::PendingTask& pending_task) { |
| if (network_agents_.IsEmpty()) |
| return; |
| ThreadDebugger::IdleFinished(V8PerIsolateData::MainThreadIsolate()); |
| } |
| |
| void WebDevToolsAgentImpl::DidProcessTask( |
| const base::PendingTask& pending_task) { |
| if (network_agents_.IsEmpty()) |
| return; |
| ThreadDebugger::IdleStarted(V8PerIsolateData::MainThreadIsolate()); |
| FlushProtocolNotifications(); |
| } |
| |
| } // namespace blink |