OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 cr.define('serviceworker', function() { | 5 cr.define('serviceworker', function() { |
6 'use strict'; | 6 'use strict'; |
7 | 7 |
8 function initialize() { | 8 function initialize() { |
9 if (window.location.hash == "#iframe") { | 9 if (window.location.hash == "#iframe") { |
10 // This page is loaded from chrome://inspect. | 10 // This page is loaded from chrome://inspect. |
11 window.addEventListener('message', onMessage.bind(this), false); | 11 window.addEventListener('message', onMessage.bind(this), false); |
| 12 } |
| 13 update(); |
| 14 } |
| 15 |
| 16 function onMessage(event) { |
| 17 if (event.origin != 'chrome://inspect') { |
| 18 return; |
| 19 } |
| 20 sendCommand(event.data.action, event.data.worker); |
| 21 } |
| 22 |
| 23 function update() { |
| 24 chrome.send('getAllRegistrations'); |
| 25 } |
| 26 |
| 27 function progressNodeFor(link) { |
| 28 return link.parentNode.querySelector('.operation-status'); |
| 29 } |
| 30 |
| 31 // All commands are completed with 'onOperationComplete'. |
| 32 var COMMANDS = ['stop', 'sync', 'inspect', 'unregister', 'start']; |
| 33 function commandHandler(command) { |
| 34 return function(event) { |
| 35 var link = event.target; |
| 36 progressNodeFor(link).style.display = 'inline'; |
| 37 sendCommand(command, link.cmdArgs, (function(status) { |
| 38 progressNodeFor(link).style.display = 'none'; |
| 39 }).bind(null, link)); |
| 40 return false; |
| 41 }; |
| 42 }; |
| 43 |
| 44 var commandCallbacks = []; |
| 45 function sendCommand(command, args, callback) { |
| 46 var callbackId = 0; |
| 47 while (callbackId in commandCallbacks) { |
| 48 callbackId++; |
| 49 } |
| 50 commandCallbacks[callbackId] = callback; |
| 51 chrome.send(command, [callbackId, args]); |
| 52 } |
| 53 |
| 54 // Fired from the backend after the command call has completed. |
| 55 function onOperationComplete(status, callbackId) { |
| 56 var callback = commandCallbacks[callbackId]; |
| 57 delete commandCallbacks[callbackId]; |
| 58 if (callback) { |
| 59 callback(status); |
| 60 } |
| 61 update(); |
| 62 } |
| 63 |
| 64 // Send the active ServiceWorker information to chrome://inspect. |
| 65 function sendToInspectPage(live_registrations, |
| 66 partition_id) { |
| 67 var workers = []; |
| 68 live_registrations.forEach(function(registration) { |
| 69 [registration.active, registration.pending].forEach(function(version) { |
| 70 if (!version || version.running_status != 'RUNNING') { |
| 71 return; |
12 } | 72 } |
13 update(); | 73 workers.push({ |
14 } | 74 'scope': registration.scope, |
15 | 75 'url': registration.script_url, |
16 function onMessage(event) { | 76 'partition_id': partition_id, |
17 if (event.origin != 'chrome://inspect') { | 77 'version_id': version.version_id, |
18 return; | 78 'process_id': version.process_id, |
| 79 'devtools_agent_route_id': |
| 80 version.devtools_agent_route_id |
| 81 }); |
| 82 }); |
| 83 }); |
| 84 window.parent.postMessage( |
| 85 {'partition_id': partition_id, 'workers': workers}, |
| 86 'chrome://inspect'); |
| 87 } |
| 88 |
| 89 var allLogMessages = {}; |
| 90 // Set log for a worker version. |
| 91 function fillLogForVersion(partition_id, version) { |
| 92 if (!version) { |
| 93 return; |
| 94 } |
| 95 if (!(partition_id in allLogMessages)) { |
| 96 allLogMessages[partition_id] = {}; |
| 97 } |
| 98 var logMessages = allLogMessages[partition_id]; |
| 99 if (version.version_id in logMessages) { |
| 100 version.log = logMessages[version.version_id]; |
| 101 } else { |
| 102 version.log = ''; |
| 103 } |
| 104 } |
| 105 |
| 106 // Get the unregistered workers. |
| 107 // |unregistered_registrations| will be filled with the registrations which |
| 108 // are in |live_registrations| but not in |stored_registrations|. |
| 109 // |unregistered_versions| will be filled with the versions which |
| 110 // are in |live_versions| but not in |stored_registrations| nor in |
| 111 // |live_registrations|. |
| 112 function getUnregisteredWorkers(stored_registrations, |
| 113 live_registrations, |
| 114 live_versions, |
| 115 unregistered_registrations, |
| 116 unregistered_versions) { |
| 117 var registration_id_set = {}; |
| 118 var version_id_set = {}; |
| 119 stored_registrations.forEach(function(registration) { |
| 120 registration_id_set[registration.registration_id] = true; |
| 121 }); |
| 122 [stored_registrations, live_registrations].forEach(function(registrations) { |
| 123 registrations.forEach(function(registration) { |
| 124 [registration.active, registration.pending].forEach(function(version) { |
| 125 if (version) { |
| 126 version_id_set[version.version_id] = true; |
| 127 } |
| 128 }); |
| 129 }); |
| 130 }); |
| 131 live_registrations.forEach(function(registration) { |
| 132 if (!registration_id_set[registration.registration_id]) { |
| 133 registration.unregistered = true; |
| 134 unregistered_registrations.push(registration); |
| 135 } |
| 136 }); |
| 137 live_versions.forEach(function(version) { |
| 138 if (!version_id_set[version.version_id]) { |
| 139 unregistered_versions.push(version); |
| 140 } |
| 141 }); |
| 142 } |
| 143 |
| 144 // Fired once per partition from the backend. |
| 145 function onPartitionData(live_registrations, |
| 146 live_versions, |
| 147 stored_registrations, |
| 148 partition_id, |
| 149 partition_path) { |
| 150 if (window.location.hash == "#iframe") { |
| 151 // This page is loaded from chrome://inspect. |
| 152 sendToInspectPage(live_registrations, partition_id); |
| 153 return; |
| 154 } |
| 155 var unregistered_registrations = []; |
| 156 var unregistered_versions = []; |
| 157 getUnregisteredWorkers(stored_registrations, |
| 158 live_registrations, |
| 159 live_versions, |
| 160 unregistered_registrations, |
| 161 unregistered_versions); |
| 162 var template; |
| 163 var container = $('serviceworker-list'); |
| 164 // Existing templates are keyed by partition_path. This allows |
| 165 // the UI to be updated in-place rather than refreshing the |
| 166 // whole page. |
| 167 for (var i = 0; i < container.childNodes.length; ++i) { |
| 168 if (container.childNodes[i].partition_path == partition_path) { |
| 169 template = container.childNodes[i]; |
| 170 } |
| 171 } |
| 172 // This is probably the first time we're loading. |
| 173 if (!template) { |
| 174 template = jstGetTemplate('serviceworker-list-template'); |
| 175 container.appendChild(template); |
| 176 } |
| 177 var fillLogFunc = fillLogForVersion.bind(this, partition_id); |
| 178 stored_registrations.forEach(function(registration) { |
| 179 [registration.active, registration.pending].forEach(fillLogFunc); |
| 180 }); |
| 181 unregistered_registrations.forEach(function(registration) { |
| 182 [registration.active, registration.pending].forEach(fillLogFunc); |
| 183 }); |
| 184 unregistered_versions.forEach(fillLogFunc); |
| 185 jstProcess(new JsEvalContext({ |
| 186 stored_registrations: stored_registrations, |
| 187 unregistered_registrations: unregistered_registrations, |
| 188 unregistered_versions: unregistered_versions, |
| 189 partition_id: partition_id, |
| 190 partition_path: partition_path}), |
| 191 template); |
| 192 for (var i = 0; i < COMMANDS.length; ++i) { |
| 193 var handler = commandHandler(COMMANDS[i]); |
| 194 var links = container.querySelectorAll('button.' + COMMANDS[i]); |
| 195 for (var j = 0; j < links.length; ++j) { |
| 196 if (!links[j].hasClickEvent) { |
| 197 links[j].addEventListener('click', handler, false); |
| 198 links[j].hasClickEvent = true; |
19 } | 199 } |
20 chrome.send(event.data.action, | 200 } |
21 [event.data.partition_path, event.data.scope]); | 201 } |
22 } | 202 } |
23 | 203 |
24 function update() { | 204 function onWorkerStarted(partition_id, version_id, process_id, thread_id) { |
25 chrome.send('getAllRegistrations'); | 205 update(); |
26 } | 206 } |
27 | 207 |
28 function progressNodeFor(link) { | 208 function onWorkerStopped(partition_id, version_id, process_id, thread_id) { |
29 return link.parentNode.querySelector('.operation-status'); | 209 update(); |
30 } | 210 } |
31 | 211 |
32 // All commands are sent with the partition_path and scope, and | 212 function onErrorReported(partition_id, |
33 // are all completed with 'onOperationComplete'. | 213 version_id, |
34 var COMMANDS = ['unregister', 'start', 'stop', 'sync', 'inspect']; | 214 process_id, |
35 function commandHandler(command) { | 215 thread_id, |
36 return function(event) { | 216 error_info) { |
37 var link = event.target; | 217 outputLogMessage(partition_id, |
38 progressNodeFor(link).style.display = 'inline'; | 218 version_id, |
39 chrome.send(command, [link.partition_path, | 219 'Error: ' + JSON.stringify(error_info) + '\n'); |
40 link.scope]); | 220 } |
41 return false; | 221 |
42 }; | 222 function onConsoleMessageReported(partition_id, |
43 }; | 223 version_id, |
44 | 224 process_id, |
45 function withNode(selector, partition_path, scope, callback) { | 225 thread_id, |
46 var links = document.querySelectorAll(selector); | 226 message) { |
47 for (var i = 0; i < links.length; ++i) { | 227 outputLogMessage(partition_id, |
48 var link = links[i]; | 228 version_id, |
49 if (partition_path == link.partition_path && | 229 'Console: ' + JSON.stringify(message) + '\n'); |
50 scope == link.scope) { | 230 } |
51 callback(link); | 231 |
52 } | 232 function onVersionStateChanged(partition_id, version_id) { |
53 } | 233 update(); |
54 } | 234 } |
55 | 235 |
56 // Fired from the backend after the start call has completed | 236 function onRegistrationStored(scope) { |
57 function onOperationComplete(status, path, scope) { | 237 update(); |
58 // refreshes the ui, displaying any relevant buttons | 238 } |
59 withNode('button', path, scope, function(link) { | 239 |
60 progressNodeFor(link).style.display = 'none'; | 240 function onRegistrationDeleted(scope) { |
61 }); | 241 update(); |
62 update(); | 242 } |
63 } | 243 |
64 | 244 function outputLogMessage(partition_id, version_id, message) { |
65 var allLogMessages = {}; | 245 if (!(partition_id in allLogMessages)) { |
66 | 246 allLogMessages[partition_id] = {}; |
67 // Send the active ServiceWorker information to chrome://inspect. | 247 } |
68 function sendToInspectPage(registrations, partition_id, partition_path) { | 248 var logMessages = allLogMessages[partition_id]; |
69 var workers = []; | 249 if (version_id in logMessages) { |
70 for (var i = 0; i < registrations.length; i++) { | 250 logMessages[version_id] += message; |
71 var registration = registrations[i]; | 251 } else { |
72 if (!registration.active || | 252 logMessages[version_id] = message; |
73 registration.active.running_status != 'RUNNING') { | 253 } |
74 continue; | 254 |
75 } | 255 var logAreas = document.querySelectorAll('textarea.serviceworker-log'); |
76 workers.push({ | 256 for (var i = 0; i < logAreas.length; ++i) { |
77 'partition_path': partition_path, | 257 var logArea = logAreas[i]; |
78 'scope': registration.scope, | 258 if (logArea.partition_id == partition_id && |
79 'url': registration.script_url | 259 logArea.version_id == version_id) { |
80 }) | 260 logArea.value += message; |
81 } | 261 } |
82 window.parent.postMessage({ | 262 } |
83 'partition_id': partition_id, | 263 } |
84 'workers': workers, | 264 |
85 }, 'chrome://inspect') | 265 return { |
86 } | 266 initialize: initialize, |
87 | 267 onOperationComplete: onOperationComplete, |
88 // Fired once per partition from the backend. | 268 onPartitionData: onPartitionData, |
89 function onPartitionData(registrations, partition_id, partition_path) { | 269 onWorkerStarted: onWorkerStarted, |
90 if (window.location.hash == "#iframe") { | 270 onWorkerStopped: onWorkerStopped, |
91 // This page is loaded from chrome://inspect. | 271 onErrorReported: onErrorReported, |
92 sendToInspectPage(registrations, partition_id, partition_path); | 272 onConsoleMessageReported: onConsoleMessageReported, |
93 return; | 273 onVersionStateChanged: onVersionStateChanged, |
94 } | 274 onRegistrationStored: onRegistrationStored, |
95 var template; | 275 onRegistrationDeleted: onRegistrationDeleted, |
96 var container = $('serviceworker-list'); | 276 }; |
97 | |
98 // Existing templates are keyed by partition_path. This allows | |
99 // the UI to be updated in-place rather than refreshing the | |
100 // whole page. | |
101 for (var i = 0; i < container.childNodes.length; ++i) { | |
102 if (container.childNodes[i].partition_path == partition_path) { | |
103 template = container.childNodes[i]; | |
104 } | |
105 } | |
106 | |
107 // This is probably the first time we're loading. | |
108 if (!template) { | |
109 template = jstGetTemplate('serviceworker-list-template'); | |
110 container.appendChild(template); | |
111 } | |
112 | |
113 // Set log for each worker versions. | |
114 if (!(partition_id in allLogMessages)) { | |
115 allLogMessages[partition_id] = {}; | |
116 } | |
117 var logMessages = allLogMessages[partition_id]; | |
118 registrations.forEach(function (worker) { | |
119 [worker.active, worker.pending].forEach(function (version) { | |
120 if (version) { | |
121 if (version.version_id in logMessages) { | |
122 version.log = logMessages[version.version_id]; | |
123 } else { | |
124 version.log = ''; | |
125 } | |
126 } | |
127 }); | |
128 }); | |
129 | |
130 jstProcess(new JsEvalContext({ registrations: registrations, | |
131 partition_id: partition_id, | |
132 partition_path: partition_path}), | |
133 template); | |
134 for (var i = 0; i < COMMANDS.length; ++i) { | |
135 var handler = commandHandler(COMMANDS[i]); | |
136 var links = container.querySelectorAll('button.' + COMMANDS[i]); | |
137 for (var j = 0; j < links.length; ++j) { | |
138 if (!links[j].hasClickEvent) { | |
139 links[j].addEventListener('click', handler, false); | |
140 links[j].hasClickEvent = true; | |
141 } | |
142 } | |
143 } | |
144 } | |
145 | |
146 function onWorkerStarted(partition_id, version_id, process_id, thread_id) { | |
147 update(); | |
148 } | |
149 | |
150 function onWorkerStopped(partition_id, version_id, process_id, thread_id) { | |
151 update(); | |
152 } | |
153 | |
154 function onErrorReported(partition_id, | |
155 version_id, | |
156 process_id, | |
157 thread_id, | |
158 error_info) { | |
159 outputLogMessage(partition_id, | |
160 version_id, | |
161 'Error: ' + JSON.stringify(error_info) + '\n'); | |
162 } | |
163 | |
164 function onConsoleMessageReported(partition_id, | |
165 version_id, | |
166 process_id, | |
167 thread_id, | |
168 message) { | |
169 outputLogMessage(partition_id, | |
170 version_id, | |
171 'Console: ' + JSON.stringify(message) + '\n'); | |
172 } | |
173 | |
174 function onVersionStateChanged(partition_id, version_id) { | |
175 update(); | |
176 } | |
177 | |
178 function onRegistrationStored(scope) { | |
179 update(); | |
180 } | |
181 | |
182 function onRegistrationDeleted(scope) { | |
183 update(); | |
184 } | |
185 | |
186 function outputLogMessage(partition_id, version_id, message) { | |
187 if (!(partition_id in allLogMessages)) { | |
188 allLogMessages[partition_id] = {}; | |
189 } | |
190 var logMessages = allLogMessages[partition_id]; | |
191 if (version_id in logMessages) { | |
192 logMessages[version_id] += message; | |
193 } else { | |
194 logMessages[version_id] = message; | |
195 } | |
196 | |
197 var logAreas = | |
198 document.querySelectorAll('textarea.serviceworker-log'); | |
199 for (var i = 0; i < logAreas.length; ++i) { | |
200 var logArea = logAreas[i]; | |
201 if (logArea.partition_id == partition_id && | |
202 logArea.version_id == version_id) { | |
203 logArea.value += message; | |
204 } | |
205 } | |
206 } | |
207 | |
208 return { | |
209 initialize: initialize, | |
210 update: update, | |
211 onOperationComplete: onOperationComplete, | |
212 onPartitionData: onPartitionData, | |
213 onWorkerStarted: onWorkerStarted, | |
214 onWorkerStopped: onWorkerStopped, | |
215 onErrorReported: onErrorReported, | |
216 onConsoleMessageReported: onConsoleMessageReported, | |
217 onVersionStateChanged: onVersionStateChanged, | |
218 onRegistrationStored: onRegistrationStored, | |
219 onRegistrationDeleted: onRegistrationDeleted, | |
220 }; | |
221 }); | 277 }); |
222 | 278 |
223 document.addEventListener('DOMContentLoaded', serviceworker.initialize); | 279 document.addEventListener('DOMContentLoaded', serviceworker.initialize); |
OLD | NEW |