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 17 namespace content { 18 19 namespace { 20 21 void TerminateSharedWorkerOnIO( 22 EmbeddedWorkerDevToolsAgentHost::WorkerId worker_id) { 23 SharedWorkerServiceImpl::GetInstance()->TerminateWorker( 24 worker_id.first, worker_id.second); 25 } 26 27 void StatusNoOp(ServiceWorkerStatusCode status) { 28 } 29 30 void 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 41 EmbeddedWorkerDevToolsAgentHost::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 50 EmbeddedWorkerDevToolsAgentHost::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 62 bool EmbeddedWorkerDevToolsAgentHost::IsWorker() const { 63 return true; 64 } 65 66 DevToolsAgentHost::Type EmbeddedWorkerDevToolsAgentHost::GetType() { 67 return shared_worker_ ? TYPE_SHARED_WORKER : TYPE_SERVICE_WORKER; 68 } 69 70 std::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 80 GURL 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 88 bool EmbeddedWorkerDevToolsAgentHost::Activate() { 89 return false; 90 } 91 92 bool 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 108 void 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 119 void EmbeddedWorkerDevToolsAgentHost::Attach() { 120 if (state_ != WORKER_INSPECTED) { 121 state_ = WORKER_INSPECTED; 122 AttachToWorker(); 123 } 124 IPCDevToolsAgentHost::Attach(); 125 } 126 127 void 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 136 bool 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 150 void 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 162 void EmbeddedWorkerDevToolsAgentHost::WorkerContextStarted() { 163 } 164 165 void 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 172 void 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 187 bool EmbeddedWorkerDevToolsAgentHost::Matches( 188 const SharedWorkerInstance& other) { 189 return shared_worker_ && shared_worker_->Matches(other); 190 } 191 192 bool EmbeddedWorkerDevToolsAgentHost::Matches( 193 const ServiceWorkerIdentifier& other) { 194 return service_worker_ && service_worker_->Matches(other); 195 } 196 197 bool EmbeddedWorkerDevToolsAgentHost::IsTerminated() { 198 return state_ == WORKER_TERMINATED; 199 } 200 201 EmbeddedWorkerDevToolsAgentHost::~EmbeddedWorkerDevToolsAgentHost() { 202 DCHECK_EQ(WORKER_TERMINATED, state_); 203 EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData( 204 worker_id_); 205 } 206 207 void EmbeddedWorkerDevToolsAgentHost::AttachToWorker() { 208 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) 209 host->AddRoute(worker_id_.second, this); 210 } 211 212 void EmbeddedWorkerDevToolsAgentHost::DetachFromWorker() { 213 if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) 214 host->RemoveRoute(worker_id_.second); 215 } 216 217 void EmbeddedWorkerDevToolsAgentHost::WorkerCreated() { 218 AddRef(); // Balanced in WorkerDestroyed() 219 } 220 221 void EmbeddedWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend( 222 const std::string& message) { 223 SendMessageToClient(message); 224 } 225 226 void EmbeddedWorkerDevToolsAgentHost::OnSaveAgentRuntimeState( 227 const std::string& state) { 228 saved_agent_state_ = state; 229 } 230 231 } // namespace content 232