[IndexedDB] Adding traces, perf tests
[chromium-blink-merge.git] / tools / perf / page_sets / indexeddb_perf / endure / app.js
blobad611b5da46e1c89dcb88d433ff82283d0e5143c
1 // Copyright (c) 2012 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 // This file simulates a typical foreground process of an offline-capable
6 // authoring application. When in an "offline" state, simulated user actions
7 // are recorded for later playback in an IDB data store. When in an "online"
8 // state, the recorded actions are drained from the store (as if being sent
9 // to the server).
11 var $ = function(s) {
12   return document.querySelector(s);
15 function status(message) {
16   var elem = $('#status');
17   while (elem.firstChild)
18     elem.removeChild(elem.firstChild);
19   elem.appendChild(document.createTextNode(message));
22 function log(message) {
23   status(message);
26 function error(message) {
27   status(message);
28   console.error(message);
31 function unexpectedErrorCallback(e) {
32   error("Unexpected error callback: (" + e.target.error.name + ") " +
33         e.target.error.message);
36 function unexpectedAbortCallback(e) {
37   error("Unexpected abort callback: (" + e.target.error.name + ") " +
38         e.target.error.message);
41 function unexpectedBlockedCallback(e) {
42   error("Unexpected blocked callback!");
45 var DBNAME = 'endurance-db';
46 var DBVERSION = 1;
47 var MAX_DOC_ID = 25;
49 var db;
51 function initdb() {
52   var request = indexedDB.deleteDatabase(DBNAME);
53   request.onerror = unexpectedErrorCallback;
54   request.onblocked = unexpectedBlockedCallback;
55   request.onsuccess = function () {
56     request = indexedDB.open(DBNAME, DBVERSION);
57     request.onerror = unexpectedErrorCallback;
58     request.onblocked = unexpectedBlockedCallback;
59     request.onupgradeneeded = function () {
60       db = request.result;
61       request.transaction.onabort = unexpectedAbortCallback;
63       var syncStore = db.createObjectStore(
64         'sync-chunks', {keyPath: 'sequence', autoIncrement: true});
65       syncStore.createIndex('doc-index', 'docid');
67       var docStore = db.createObjectStore(
68         'docs', {keyPath: 'docid'});
69       docStore.createIndex(
70         'owner-index', 'owner', {multiEntry: true});
72       var userEventStore = db.createObjectStore(
73         'user-events', {keyPath: 'sequence', autoIncrement: true});
74       userEventStore.createIndex('doc-index', 'docid');
75     };
76     request.onsuccess = function () {
77       log('initialized');
78       $('#offline').disabled = true;
79       $('#online').disabled = false;
80     };
81   };
84 var offline = true;
85 var worker = new Worker('app-worker.js?cachebust');
86 worker.onmessage = function (event) {
87   var data = event.data;
88   switch (data.type) {
89     case 'ABORT':
90       unexpectedAbortCallback({target: {error: data.error}});
91       break;
92     case 'ERROR':
93       unexpectedErrorCallback({target: {error: data.error}});
94       break;
95     case 'BLOCKED':
96       unexpectedBlockedCallback({target: {error: data.error}});
97       break;
98     case 'LOG':
99       log('WORKER: ' + data.message);
100       break;
101     case 'ERROR':
102       error('WORKER: ' + data.message);
103       break;
104     }
106 worker.onerror = function (event) {
107   error("Error in: " + event.filename + "(" + event.lineno + "): " +
108         event.message);
111 $('#offline').addEventListener('click', goOffline);
112 $('#online').addEventListener('click', goOnline);
114 var EVENT_INTERVAL = 100;
115 var eventIntervalId = 0;
117 function goOffline() {
118   if (offline)
119     return;
120   offline = true;
121   $('#offline').disabled = offline;
122   $('#online').disabled = !offline;
123   $('#state').innerHTML = 'offline';
124   log('offline');
126   worker.postMessage({type: 'offline'});
128   eventIntervalId = setInterval(recordEvent, EVENT_INTERVAL);
131 function goOnline() {
132   if (!offline)
133     return;
134   offline = false;
135   $('#offline').disabled = offline;
136   $('#online').disabled = !offline;
137   $('#state').innerHTML = 'online';
138   log('online');
140   worker.postMessage({type: 'online'});
142   setTimeout(playbackEvents, 100);
143   clearInterval(eventIntervalId);
144   eventIntervalId = 0;
147 function recordEvent() {
148   if (!db) {
149     error("Database not initialized");
150     return;
151   }
153   var transaction = db.transaction(['user-events'], 'readwrite');
154   var store = transaction.objectStore('user-events');
155   var record = {
156     // 'sequence' key will be generated
157     docid: Math.floor(Math.random() * MAX_DOC_ID),
158     timestamp: new Date(),
159     data: randomString(256)
160   };
162   log('putting user event');
163   var request = store.put(record);
164   request.onerror = unexpectedErrorCallback;
165   transaction.onabort = unexpectedAbortCallback;
166   transaction.oncomplete = function () {
167     log('put user event');
168   };
171 function sendEvent(record, callback) {
172   setTimeout(
173     function () {
174       if (offline)
175         callback(false);
176       else {
177         var serialization = JSON.stringify(record);
178         callback(true);
179       }
180     },
181     Math.random() * 200); // Simulate network jitter
184 var PLAYBACK_NONE = 0;
185 var PLAYBACK_SUCCESS = 1;
186 var PLAYBACK_FAILURE = 2;
188 function playbackEvent(callback) {
189   log('playbackEvent');
190   var result = false;
191   var transaction = db.transaction(['user-events'], 'readonly');
192   transaction.onabort = unexpectedAbortCallback;
193   var store = transaction.objectStore('user-events');
194   var cursorRequest = store.openCursor();
195   cursorRequest.onerror = unexpectedErrorCallback;
196   cursorRequest.onsuccess = function () {
197     var cursor = cursorRequest.result;
198     if (cursor) {
199       var record = cursor.value;
200       var key = cursor.key;
201       // NOTE: sendEvent is asynchronous so transaction should finish
202       sendEvent(
203         record,
204         function (success) {
205           if (success) {
206             // Use another transaction to delete event
207             var transaction = db.transaction(['user-events'], 'readwrite');
208             transaction.onabort = unexpectedAbortCallback;
209             var store = transaction.objectStore('user-events');
210             var deleteRequest = store.delete(key);
211             deleteRequest.onerror = unexpectedErrorCallback;
212             transaction.oncomplete = function () {
213               // successfully sent and deleted event
214               callback(PLAYBACK_SUCCESS);
215             };
216           } else {
217             // No progress made
218             callback(PLAYBACK_FAILURE);
219           }
220         });
221     } else {
222       callback(PLAYBACK_NONE);
223     }
224   };
227 var playback = false;
229 function playbackEvents() {
230   log('playbackEvents');
231   if (!db) {
232     error("Database not initialized");
233     return;
234   }
236   if (playback)
237     return;
239   playback = true;
240   log("Playing back events");
242   function nextEvent() {
243     playbackEvent(
244       function (result) {
245         switch (result) {
246           case PLAYBACK_NONE:
247             playback = false;
248             log("Done playing back events");
249             return;
250           case PLAYBACK_SUCCESS:
251             setTimeout(nextEvent, 0);
252             return;
253           case PLAYBACK_FAILURE:
254             playback = false;
255             log("Failure during playback (dropped offline?)");
256             return;
257         }
258       });
259   }
261   nextEvent();
264 function randomString(len) {
265   var s = '';
266   while (len--)
267     s += Math.floor((Math.random() * 36)).toString(36);
268   return s;
271 window.onload = function () {
272   log("initializing...");
273   initdb();