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.
5 cr.define('serviceworker', function() {
8 function initialize() {
9 if (window.location.hash == "#iframe") {
10 // This page is loaded from chrome://inspect.
11 window.addEventListener('message', onMessage.bind(this), false);
16 function onMessage(event) {
17 if (event.origin != 'chrome://inspect') {
20 sendCommand(event.data.action, event.data.worker);
24 chrome.send('GetOptions');
25 chrome.send('getAllRegistrations');
28 function onOptions(options) {
30 var container = $('serviceworker-options');
31 if (container.childNodes) {
32 template = container.childNodes[0];
35 template = jstGetTemplate('serviceworker-options-template');
36 container.appendChild(template);
38 jstProcess(new JsEvalContext(options), template);
39 var inputs = container.querySelectorAll('input[type=\'checkbox\']');
40 for (var i = 0; i < inputs.length; ++i) {
41 if (!inputs[i].hasClickEvent) {
42 inputs[i].addEventListener('click', (function(event) {
43 chrome.send('SetOption',
44 [event.target.className, event.target.checked]);
45 }).bind(this), false);
46 inputs[i].hasClickEvent = true;
51 function progressNodeFor(link) {
52 return link.parentNode.querySelector('.operation-status');
55 // All commands are completed with 'onOperationComplete'.
56 var COMMANDS = ['stop', 'sync', 'push', 'inspect', 'unregister', 'start'];
57 function commandHandler(command) {
58 return function(event) {
59 var link = event.target;
60 progressNodeFor(link).style.display = 'inline';
61 sendCommand(command, link.cmdArgs, (function(status) {
62 progressNodeFor(link).style.display = 'none';
68 var commandCallbacks = [];
69 function sendCommand(command, args, callback) {
71 while (callbackId in commandCallbacks) {
74 commandCallbacks[callbackId] = callback;
75 chrome.send(command, [callbackId, args]);
78 // Fired from the backend after the command call has completed.
79 function onOperationComplete(status, callbackId) {
80 var callback = commandCallbacks[callbackId];
81 delete commandCallbacks[callbackId];
88 // Send the active ServiceWorker information to chrome://inspect.
89 function sendToInspectPage(live_registrations,
92 live_registrations.forEach(function(registration) {
93 [registration.active, registration.waiting].forEach(function(version) {
94 if (!version || version.running_status != 'RUNNING') {
98 'scope': registration.scope,
99 'url': version.script_url,
100 'partition_id': partition_id,
101 'version_id': version.version_id,
102 'process_id': version.process_id,
103 'devtools_agent_route_id':
104 version.devtools_agent_route_id
108 window.parent.postMessage(
109 {'partition_id': partition_id, 'workers': workers},
113 var allLogMessages = {};
114 // Set log for a worker version.
115 function fillLogForVersion(partition_id, version) {
119 if (!(partition_id in allLogMessages)) {
120 allLogMessages[partition_id] = {};
122 var logMessages = allLogMessages[partition_id];
123 if (version.version_id in logMessages) {
124 version.log = logMessages[version.version_id];
130 // Get the unregistered workers.
131 // |unregistered_registrations| will be filled with the registrations which
132 // are in |live_registrations| but not in |stored_registrations|.
133 // |unregistered_versions| will be filled with the versions which
134 // are in |live_versions| but not in |stored_registrations| nor in
135 // |live_registrations|.
136 function getUnregisteredWorkers(stored_registrations,
139 unregistered_registrations,
140 unregistered_versions) {
141 var registration_id_set = {};
142 var version_id_set = {};
143 stored_registrations.forEach(function(registration) {
144 registration_id_set[registration.registration_id] = true;
146 [stored_registrations, live_registrations].forEach(function(registrations) {
147 registrations.forEach(function(registration) {
148 [registration.active, registration.waiting].forEach(function(version) {
150 version_id_set[version.version_id] = true;
155 live_registrations.forEach(function(registration) {
156 if (!registration_id_set[registration.registration_id]) {
157 registration.unregistered = true;
158 unregistered_registrations.push(registration);
161 live_versions.forEach(function(version) {
162 if (!version_id_set[version.version_id]) {
163 unregistered_versions.push(version);
168 // Fired once per partition from the backend.
169 function onPartitionData(live_registrations,
171 stored_registrations,
174 if (window.location.hash == "#iframe") {
175 // This page is loaded from chrome://inspect.
176 sendToInspectPage(live_registrations, partition_id);
179 var unregistered_registrations = [];
180 var unregistered_versions = [];
181 getUnregisteredWorkers(stored_registrations,
184 unregistered_registrations,
185 unregistered_versions);
187 var container = $('serviceworker-list');
188 // Existing templates are keyed by partition_id. This allows
189 // the UI to be updated in-place rather than refreshing the
191 for (var i = 0; i < container.childNodes.length; ++i) {
192 if (container.childNodes[i].partition_id == partition_id) {
193 template = container.childNodes[i];
196 // This is probably the first time we're loading.
198 template = jstGetTemplate('serviceworker-list-template');
199 container.appendChild(template);
201 var fillLogFunc = fillLogForVersion.bind(this, partition_id);
202 stored_registrations.forEach(function(registration) {
203 [registration.active, registration.waiting].forEach(fillLogFunc);
205 unregistered_registrations.forEach(function(registration) {
206 [registration.active, registration.waiting].forEach(fillLogFunc);
208 unregistered_versions.forEach(fillLogFunc);
209 jstProcess(new JsEvalContext({
210 stored_registrations: stored_registrations,
211 unregistered_registrations: unregistered_registrations,
212 unregistered_versions: unregistered_versions,
213 partition_id: partition_id,
214 partition_path: partition_path}),
216 for (var i = 0; i < COMMANDS.length; ++i) {
217 var handler = commandHandler(COMMANDS[i]);
218 var links = container.querySelectorAll('button.' + COMMANDS[i]);
219 for (var j = 0; j < links.length; ++j) {
220 if (!links[j].hasClickEvent) {
221 links[j].addEventListener('click', handler, false);
222 links[j].hasClickEvent = true;
228 function onWorkerStarted(partition_id, version_id, process_id, thread_id) {
232 function onWorkerStopped(partition_id, version_id, process_id, thread_id) {
236 function onErrorReported(partition_id,
241 outputLogMessage(partition_id,
243 'Error: ' + JSON.stringify(error_info) + '\n');
246 function onConsoleMessageReported(partition_id,
251 outputLogMessage(partition_id,
253 'Console: ' + JSON.stringify(message) + '\n');
256 function onVersionStateChanged(partition_id, version_id) {
260 function onRegistrationStored(scope) {
264 function onRegistrationDeleted(scope) {
268 function outputLogMessage(partition_id, version_id, message) {
269 if (!(partition_id in allLogMessages)) {
270 allLogMessages[partition_id] = {};
272 var logMessages = allLogMessages[partition_id];
273 if (version_id in logMessages) {
274 logMessages[version_id] += message;
276 logMessages[version_id] = message;
279 var logAreas = document.querySelectorAll('textarea.serviceworker-log');
280 for (var i = 0; i < logAreas.length; ++i) {
281 var logArea = logAreas[i];
282 if (logArea.partition_id == partition_id &&
283 logArea.version_id == version_id) {
284 logArea.value += message;
290 initialize: initialize,
291 onOptions: onOptions,
292 onOperationComplete: onOperationComplete,
293 onPartitionData: onPartitionData,
294 onWorkerStarted: onWorkerStarted,
295 onWorkerStopped: onWorkerStopped,
296 onErrorReported: onErrorReported,
297 onConsoleMessageReported: onConsoleMessageReported,
298 onVersionStateChanged: onVersionStateChanged,
299 onRegistrationStored: onRegistrationStored,
300 onRegistrationDeleted: onRegistrationDeleted,
304 document.addEventListener('DOMContentLoaded', serviceworker.initialize);