1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "content/browser/devtools/embedded_worker_devtools_agent_host.h" 6 7#include "base/strings/utf_string_conversions.h" 8#include "content/browser/devtools/devtools_protocol.h" 9#include "content/browser/devtools/devtools_protocol_constants.h" 10#include "content/browser/service_worker/service_worker_context_core.h" 11#include "content/browser/service_worker/service_worker_version.h" 12#include "content/browser/shared_worker/shared_worker_service_impl.h" 13#include "content/common/devtools_messages.h" 14#include "content/public/browser/browser_thread.h" 15#include "content/public/browser/render_process_host.h" 16 17namespace content { 18 19namespace { 20 21void TerminateSharedWorkerOnIO( 22 EmbeddedWorkerDevToolsAgentHost::WorkerId worker_id) { 23 SharedWorkerServiceImpl::GetInstance()->TerminateWorker( 24 worker_id.first, worker_id.second); 25} 26 27void StatusNoOp(ServiceWorkerStatusCode status) { 28} 29 30void TerminateServiceWorkerOnIO( 31 base::WeakPtr<ServiceWorkerContextCore> context_weak, 32 int64 version_id) { 33 if (ServiceWorkerContextCore* context = context_weak.get()) { 34 if (ServiceWorkerVersion* version = context->GetLiveVersion(version_id)) 35 version->StopWorker(base::Bind(&StatusNoOp)); 36 } 37} 38 39} 40 41EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost( 42 WorkerId worker_id, 43 const SharedWorkerInstance& shared_worker) 44 : shared_worker_(new SharedWorkerInstance(shared_worker)), 45 state_(WORKER_UNINSPECTED), 46 worker_id_(worker_id) { 47 WorkerCreated(); 48} 49 50EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost( 51 WorkerId worker_id, 52 const ServiceWorkerIdentifier& service_worker, 53 bool debug_service_worker_on_start) 54 : service_worker_(new ServiceWorkerIdentifier(service_worker)), 55 state_(WORKER_UNINSPECTED), 56 worker_id_(worker_id) { 57 if (debug_service_worker_on_start) 58 state_ = WORKER_PAUSED_FOR_DEBUG_ON_START; 59 WorkerCreated(); 60} 61 62bool EmbeddedWorkerDevToolsAgentHost::IsWorker() const { 63 return true; 64} 65 66DevToolsAgentHost::Type EmbeddedWorkerDevToolsAgentHost::GetType() { 67 return shared_worker_ ? TYPE_SHARED_WORKER : TYPE_SERVICE_WORKER; 68} 69 70std::string EmbeddedWorkerDevToolsAgentHost::GetTitle() { 71 if (shared_worker_ && shared_worker_->name().length()) 72 return base::UTF16ToUTF8(shared_worker_->name()); 73 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) { 74 return base::StringPrintf("Worker pid:%d", 75 base::GetProcId(host->GetHandle())); 76 } 77 return ""; 78} 79 80GURL EmbeddedWorkerDevToolsAgentHost::GetURL() { 81 if (shared_worker_) 82 return shared_worker_->url(); 83 if (service_worker_) 84 return service_worker_->url(); 85 return GURL(); 86} 87 88bool EmbeddedWorkerDevToolsAgentHost::Activate() { 89 return false; 90} 91 92bool EmbeddedWorkerDevToolsAgentHost::Close() { 93 if (shared_worker_) { 94 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 95 base::Bind(&TerminateSharedWorkerOnIO, worker_id_)); 96 return true; 97 } 98 if (service_worker_) { 99 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 100 base::Bind(&TerminateServiceWorkerOnIO, 101 service_worker_->context_weak(), 102 service_worker_->version_id())); 103 return true; 104 } 105 return false; 106} 107 108void EmbeddedWorkerDevToolsAgentHost::SendMessageToAgent( 109 IPC::Message* message_raw) { 110 scoped_ptr<IPC::Message> message(message_raw); 111 if (state_ != WORKER_INSPECTED) 112 return; 113 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) { 114 message->set_routing_id(worker_id_.second); 115 host->Send(message.release()); 116 } 117} 118 119void EmbeddedWorkerDevToolsAgentHost::Attach() { 120 if (state_ != WORKER_INSPECTED) { 121 state_ = WORKER_INSPECTED; 122 AttachToWorker(); 123 } 124 IPCDevToolsAgentHost::Attach(); 125} 126 127void EmbeddedWorkerDevToolsAgentHost::OnClientDetached() { 128 if (state_ == WORKER_INSPECTED) { 129 state_ = WORKER_UNINSPECTED; 130 DetachFromWorker(); 131 } else if (state_ == WORKER_PAUSED_FOR_REATTACH) { 132 state_ = WORKER_UNINSPECTED; 133 } 134} 135 136bool EmbeddedWorkerDevToolsAgentHost::OnMessageReceived( 137 const IPC::Message& msg) { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 139 bool handled = true; 140 IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg) 141 IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, 142 OnDispatchOnInspectorFrontend) 143 IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, 144 OnSaveAgentRuntimeState) 145 IPC_MESSAGE_UNHANDLED(handled = false) 146 IPC_END_MESSAGE_MAP() 147 return handled; 148} 149 150void EmbeddedWorkerDevToolsAgentHost::WorkerReadyForInspection() { 151 if (state_ == WORKER_PAUSED_FOR_DEBUG_ON_START) { 152 RenderProcessHost* rph = RenderProcessHost::FromID(worker_id_.first); 153 Inspect(rph->GetBrowserContext()); 154 } else if (state_ == WORKER_PAUSED_FOR_REATTACH) { 155 DCHECK(IsAttached()); 156 state_ = WORKER_INSPECTED; 157 AttachToWorker(); 158 Reattach(saved_agent_state_); 159 } 160} 161 162void EmbeddedWorkerDevToolsAgentHost::WorkerContextStarted() { 163} 164 165void EmbeddedWorkerDevToolsAgentHost::WorkerRestarted(WorkerId worker_id) { 166 DCHECK_EQ(WORKER_TERMINATED, state_); 167 state_ = IsAttached() ? WORKER_PAUSED_FOR_REATTACH : WORKER_UNINSPECTED; 168 worker_id_ = worker_id; 169 WorkerCreated(); 170} 171 172void EmbeddedWorkerDevToolsAgentHost::WorkerDestroyed() { 173 DCHECK_NE(WORKER_TERMINATED, state_); 174 if (state_ == WORKER_INSPECTED) { 175 DCHECK(IsAttached()); 176 // Client host is debugging this worker agent host. 177 std::string notification = 178 DevToolsProtocol::CreateNotification( 179 devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize(); 180 SendMessageToClient(notification); 181 DetachFromWorker(); 182 } 183 state_ = WORKER_TERMINATED; 184 Release(); // Balanced in WorkerCreated() 185} 186 187bool EmbeddedWorkerDevToolsAgentHost::Matches( 188 const SharedWorkerInstance& other) { 189 return shared_worker_ && shared_worker_->Matches(other); 190} 191 192bool EmbeddedWorkerDevToolsAgentHost::Matches( 193 const ServiceWorkerIdentifier& other) { 194 return service_worker_ && service_worker_->Matches(other); 195} 196 197bool EmbeddedWorkerDevToolsAgentHost::IsTerminated() { 198 return state_ == WORKER_TERMINATED; 199} 200 201EmbeddedWorkerDevToolsAgentHost::~EmbeddedWorkerDevToolsAgentHost() { 202 DCHECK_EQ(WORKER_TERMINATED, state_); 203 EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData( 204 worker_id_); 205} 206 207void EmbeddedWorkerDevToolsAgentHost::AttachToWorker() { 208 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) 209 host->AddRoute(worker_id_.second, this); 210} 211 212void EmbeddedWorkerDevToolsAgentHost::DetachFromWorker() { 213 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) 214 host->RemoveRoute(worker_id_.second); 215} 216 217void EmbeddedWorkerDevToolsAgentHost::WorkerCreated() { 218 AddRef(); // Balanced in WorkerDestroyed() 219} 220 221void EmbeddedWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend( 222 const std::string& message) { 223 SendMessageToClient(message); 224} 225 226void EmbeddedWorkerDevToolsAgentHost::OnSaveAgentRuntimeState( 227 const std::string& state) { 228 saved_agent_state_ = state; 229} 230 231} // namespace content 232