• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5cr.define('serviceworker', function() {
6  'use strict';
7
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);
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('GetOptions');
25      chrome.send('getAllRegistrations');
26  }
27
28  function onOptions(options) {
29    var template;
30    var container = $('serviceworker-options');
31    if (container.childNodes) {
32      template = container.childNodes[0];
33    }
34    if (!template) {
35      template = jstGetTemplate('serviceworker-options-template');
36      container.appendChild(template);
37    }
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;
47      }
48    }
49  }
50
51  function progressNodeFor(link) {
52    return link.parentNode.querySelector('.operation-status');
53  }
54
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';
63      }).bind(null, link));
64      return false;
65    };
66  };
67
68  var commandCallbacks = [];
69  function sendCommand(command, args, callback) {
70    var callbackId = 0;
71    while (callbackId in commandCallbacks) {
72      callbackId++;
73    }
74    commandCallbacks[callbackId] = callback;
75    chrome.send(command, [callbackId, args]);
76  }
77
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];
82    if (callback) {
83      callback(status);
84    }
85    update();
86  }
87
88  // Send the active ServiceWorker information to chrome://inspect.
89  function sendToInspectPage(live_registrations,
90                 partition_id) {
91    var workers = [];
92    live_registrations.forEach(function(registration) {
93      [registration.active, registration.waiting].forEach(function(version) {
94        if (!version || version.running_status != 'RUNNING') {
95          return;
96        }
97        workers.push({
98          'scope': registration.scope,
99          'url': registration.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
105        });
106      });
107    });
108    window.parent.postMessage(
109        {'partition_id': partition_id, 'workers': workers},
110        'chrome://inspect');
111  }
112
113  var allLogMessages = {};
114  // Set log for a worker version.
115  function fillLogForVersion(partition_id, version) {
116    if (!version) {
117      return;
118    }
119    if (!(partition_id in allLogMessages)) {
120      allLogMessages[partition_id] = {};
121    }
122    var logMessages = allLogMessages[partition_id];
123    if (version.version_id in logMessages) {
124      version.log = logMessages[version.version_id];
125    } else {
126      version.log = '';
127    }
128  }
129
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,
137                                  live_registrations,
138                                  live_versions,
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;
145    });
146    [stored_registrations, live_registrations].forEach(function(registrations) {
147      registrations.forEach(function(registration) {
148        [registration.active, registration.waiting].forEach(function(version) {
149          if (version) {
150            version_id_set[version.version_id] = true;
151          }
152        });
153      });
154    });
155    live_registrations.forEach(function(registration) {
156      if (!registration_id_set[registration.registration_id]) {
157        registration.unregistered = true;
158        unregistered_registrations.push(registration);
159      }
160    });
161    live_versions.forEach(function(version) {
162      if (!version_id_set[version.version_id]) {
163        unregistered_versions.push(version);
164      }
165    });
166  }
167
168  // Fired once per partition from the backend.
169  function onPartitionData(live_registrations,
170                           live_versions,
171                           stored_registrations,
172                           partition_id,
173                           partition_path) {
174    if (window.location.hash == "#iframe") {
175      // This page is loaded from chrome://inspect.
176      sendToInspectPage(live_registrations, partition_id);
177      return;
178    }
179    var unregistered_registrations = [];
180    var unregistered_versions = [];
181    getUnregisteredWorkers(stored_registrations,
182                           live_registrations,
183                           live_versions,
184                           unregistered_registrations,
185                           unregistered_versions);
186    var template;
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
190    // whole page.
191    for (var i = 0; i < container.childNodes.length; ++i) {
192      if (container.childNodes[i].partition_id == partition_id) {
193        template = container.childNodes[i];
194      }
195    }
196    // This is probably the first time we're loading.
197    if (!template) {
198      template = jstGetTemplate('serviceworker-list-template');
199      container.appendChild(template);
200    }
201    var fillLogFunc = fillLogForVersion.bind(this, partition_id);
202    stored_registrations.forEach(function(registration) {
203      [registration.active, registration.waiting].forEach(fillLogFunc);
204    });
205    unregistered_registrations.forEach(function(registration) {
206      [registration.active, registration.waiting].forEach(fillLogFunc);
207    });
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}),
215               template);
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;
223        }
224      }
225    }
226  }
227
228  function onWorkerStarted(partition_id, version_id, process_id, thread_id) {
229    update();
230  }
231
232  function onWorkerStopped(partition_id, version_id, process_id, thread_id) {
233    update();
234  }
235
236  function onErrorReported(partition_id,
237                           version_id,
238                           process_id,
239                           thread_id,
240                           error_info) {
241    outputLogMessage(partition_id,
242                     version_id,
243                     'Error: ' + JSON.stringify(error_info) + '\n');
244  }
245
246  function onConsoleMessageReported(partition_id,
247                                    version_id,
248                                    process_id,
249                                    thread_id,
250                                    message) {
251    outputLogMessage(partition_id,
252                     version_id,
253                     'Console: ' + JSON.stringify(message) + '\n');
254  }
255
256  function onVersionStateChanged(partition_id, version_id) {
257    update();
258  }
259
260  function onRegistrationStored(scope) {
261    update();
262  }
263
264  function onRegistrationDeleted(scope) {
265    update();
266  }
267
268  function outputLogMessage(partition_id, version_id, message) {
269    if (!(partition_id in allLogMessages)) {
270      allLogMessages[partition_id] = {};
271    }
272    var logMessages = allLogMessages[partition_id];
273    if (version_id in logMessages) {
274      logMessages[version_id] += message;
275    } else {
276      logMessages[version_id] = message;
277    }
278
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;
285      }
286    }
287  }
288
289  return {
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,
301  };
302});
303
304document.addEventListener('DOMContentLoaded', serviceworker.initialize);
305