1/* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.Object} 34 */ 35WebInspector.WorkerManager = function() 36{ 37 this._workerIdToWindow = {}; 38 InspectorBackend.registerWorkerDispatcher(new WebInspector.WorkerDispatcher(this)); 39} 40 41WebInspector.WorkerManager.isWorkerFrontend = function() 42{ 43 return !!WebInspector.queryParamsObject["dedicatedWorkerId"] || 44 !!WebInspector.queryParamsObject["isSharedWorker"]; 45} 46 47WebInspector.WorkerManager.isDedicatedWorkerFrontend = function() 48{ 49 return !!WebInspector.queryParamsObject["dedicatedWorkerId"]; 50} 51 52WebInspector.WorkerManager.loaded = function() 53{ 54 var workerId = WebInspector.queryParamsObject["dedicatedWorkerId"]; 55 if (workerId) 56 WebInspector.WorkerManager._initializeDedicatedWorkerFrontend(workerId); 57 else 58 WebInspector.workerManager = new WebInspector.WorkerManager(); 59} 60 61WebInspector.WorkerManager.loadCompleted = function() 62{ 63 // Make sure script execution of dedicated worker is resumed and then paused 64 // on the first script statement in case we autoattached to it. 65 if (WebInspector.queryParamsObject["workerPaused"]) { 66 DebuggerAgent.pause(); 67 RuntimeAgent.run(calculateTitle); 68 } else if (WebInspector.WorkerManager.isWorkerFrontend()) 69 calculateTitle(); 70 71 function calculateTitle() 72 { 73 WebInspector.WorkerManager._calculateWorkerInspectorTitle(); 74 } 75 76 if (WebInspector.workerManager) 77 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.MainFrameNavigated, WebInspector.workerManager._mainFrameNavigated, WebInspector.workerManager); 78} 79 80WebInspector.WorkerManager._initializeDedicatedWorkerFrontend = function(workerId) 81{ 82 function receiveMessage(event) 83 { 84 var message = event.data; 85 InspectorBackend.dispatch(message); 86 } 87 window.addEventListener("message", receiveMessage, true); 88 89 90 InspectorBackend.sendMessageObjectToBackend = function(message) 91 { 92 window.opener.postMessage({workerId: workerId, command: "sendMessageToBackend", message: message}, "*"); 93 } 94} 95 96WebInspector.WorkerManager._calculateWorkerInspectorTitle = function() 97{ 98 var expression = "location.href"; 99 if (WebInspector.queryParamsObject["isSharedWorker"]) 100 expression += " + (this.name ? ' (' + this.name + ')' : '')"; 101 RuntimeAgent.evaluate.invoke({expression:expression, doNotPauseOnExceptionsAndMuteConsole:true, returnByValue: true}, evalCallback.bind(this)); 102 103 /** 104 * @param {?Protocol.Error} error 105 * @param {!RuntimeAgent.RemoteObject} result 106 * @param {boolean=} wasThrown 107 */ 108 function evalCallback(error, result, wasThrown) 109 { 110 if (error || wasThrown) { 111 console.error(error); 112 return; 113 } 114 InspectorFrontendHost.inspectedURLChanged(result.value); 115 } 116} 117 118WebInspector.WorkerManager.Events = { 119 WorkerAdded: "worker-added", 120 WorkerRemoved: "worker-removed", 121 WorkersCleared: "workers-cleared", 122} 123 124WebInspector.WorkerManager.prototype = { 125 _workerCreated: function(workerId, url, inspectorConnected) 126 { 127 if (inspectorConnected) 128 this._openInspectorWindow(workerId, true); 129 this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerAdded, {workerId: workerId, url: url, inspectorConnected: inspectorConnected}); 130 }, 131 132 _workerTerminated: function(workerId) 133 { 134 this.closeWorkerInspector(workerId); 135 this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkerRemoved, workerId); 136 }, 137 138 _sendMessageToWorkerInspector: function(workerId, message) 139 { 140 var workerInspectorWindow = this._workerIdToWindow[workerId]; 141 if (workerInspectorWindow) 142 workerInspectorWindow.postMessage(message, "*"); 143 }, 144 145 openWorkerInspector: function(workerId) 146 { 147 var existingInspector = this._workerIdToWindow[workerId]; 148 if (existingInspector) { 149 existingInspector.focus(); 150 return; 151 } 152 153 this._openInspectorWindow(workerId, false); 154 WorkerAgent.connectToWorker(workerId); 155 }, 156 157 _openInspectorWindow: function(workerId, workerIsPaused) 158 { 159 var search = window.location.search; 160 var hash = window.location.hash; 161 var url = window.location.href; 162 // Make sure hash is in rear 163 url = url.replace(hash, ""); 164 url += (search ? "&dedicatedWorkerId=" : "?dedicatedWorkerId=") + workerId; 165 if (workerIsPaused) 166 url += "&workerPaused=true"; 167 url = url.replace("docked=true&", ""); 168 url = url.replace("can_dock=true&", ""); 169 url += hash; 170 var width = WebInspector.settings.workerInspectorWidth.get(); 171 var height = WebInspector.settings.workerInspectorHeight.get(); 172 // Set location=0 just to make sure the front-end will be opened in a separate window, not in new tab. 173 var workerInspectorWindow = window.open(url, undefined, "location=0,width=" + width + ",height=" + height); 174 workerInspectorWindow.addEventListener("resize", this._onWorkerInspectorResize.bind(this, workerInspectorWindow), false); 175 this._workerIdToWindow[workerId] = workerInspectorWindow; 176 workerInspectorWindow.addEventListener("beforeunload", this._workerInspectorClosing.bind(this, workerId), true); 177 178 // Listen to beforeunload in detached state and to the InspectorClosing event in case of attached inspector. 179 window.addEventListener("unload", this._pageInspectorClosing.bind(this), true); 180 }, 181 182 closeWorkerInspector: function(workerId) 183 { 184 var workerInspectorWindow = this._workerIdToWindow[workerId]; 185 if (workerInspectorWindow) 186 workerInspectorWindow.close(); 187 }, 188 189 _mainFrameNavigated: function(event) 190 { 191 for (var workerId in this._workerIdToWindow) 192 this.closeWorkerInspector(workerId); 193 this.dispatchEventToListeners(WebInspector.WorkerManager.Events.WorkersCleared); 194 }, 195 196 _pageInspectorClosing: function() 197 { 198 this._ignoreWorkerInspectorClosing = true; 199 for (var workerId in this._workerIdToWindow) { 200 this._workerIdToWindow[workerId].close(); 201 WorkerAgent.disconnectFromWorker(parseInt(workerId, 10)); 202 } 203 }, 204 205 _onWorkerInspectorResize: function(workerInspectorWindow) 206 { 207 var doc = workerInspectorWindow.document; 208 WebInspector.settings.workerInspectorWidth.set(doc.width); 209 WebInspector.settings.workerInspectorHeight.set(doc.height); 210 }, 211 212 _workerInspectorClosing: function(workerId, event) 213 { 214 if (event.target.location.href === "about:blank") 215 return; 216 if (this._ignoreWorkerInspectorClosing) 217 return; 218 delete this._workerIdToWindow[workerId]; 219 WorkerAgent.disconnectFromWorker(workerId); 220 }, 221 222 _disconnectedFromWorker: function() 223 { 224 var screen = new WebInspector.WorkerTerminatedScreen(); 225 WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, screen.hide, screen); 226 screen.showModal(); 227 }, 228 229 __proto__: WebInspector.Object.prototype 230} 231 232/** 233 * @constructor 234 * @implements {WorkerAgent.Dispatcher} 235 */ 236WebInspector.WorkerDispatcher = function(workerManager) 237{ 238 this._workerManager = workerManager; 239 window.addEventListener("message", this._receiveMessage.bind(this), true); 240} 241 242WebInspector.WorkerDispatcher.prototype = { 243 _receiveMessage: function(event) 244 { 245 var workerId = event.data["workerId"]; 246 workerId = parseInt(workerId, 10); 247 var command = event.data.command; 248 var message = event.data.message; 249 250 if (command == "sendMessageToBackend") 251 WorkerAgent.sendMessageToWorker(workerId, message); 252 }, 253 254 workerCreated: function(workerId, url, inspectorConnected) 255 { 256 this._workerManager._workerCreated(workerId, url, inspectorConnected); 257 }, 258 259 workerTerminated: function(workerId) 260 { 261 this._workerManager._workerTerminated(workerId); 262 }, 263 264 dispatchMessageFromWorker: function(workerId, message) 265 { 266 this._workerManager._sendMessageToWorkerInspector(workerId, message); 267 }, 268 269 disconnectedFromWorker: function() 270 { 271 this._workerManager._disconnectedFromWorker(); 272 } 273} 274 275/** 276 * @constructor 277 * @extends {WebInspector.HelpScreen} 278 */ 279WebInspector.WorkerTerminatedScreen = function() 280{ 281 WebInspector.HelpScreen.call(this, WebInspector.UIString("Inspected worker terminated")); 282 var p = this.contentElement.createChild("p"); 283 p.classList.add("help-section"); 284 p.textContent = WebInspector.UIString("Inspected worker has terminated. Once it restarts we will attach to it automatically."); 285} 286 287WebInspector.WorkerTerminatedScreen.prototype = { 288 /** 289 * @override 290 */ 291 willHide: function() 292 { 293 WebInspector.debuggerModel.removeEventListener(WebInspector.DebuggerModel.Events.GlobalObjectCleared, this.hide, this); 294 WebInspector.HelpScreen.prototype.willHide.call(this); 295 }, 296 297 __proto__: WebInspector.HelpScreen.prototype 298} 299