- add sources.
[platform/framework/web/crosswalk.git] / src / tools / perf / page_sets / endure / indexeddb_app.js
1 // Copyright 2013 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
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).
10
11 self.indexedDB = self.indexedDB || self.webkitIndexedDB ||
12   self.mozIndexedDB;
13 self.IDBKeyRange = self.IDBKeyRange || self.webkitIDBKeyRange;
14
15 var $ = function(s) {
16   return document.querySelector(s);
17 };
18
19 function status(message) {
20   var elem = $('#status');
21   while (elem.firstChild)
22     elem.removeChild(elem.firstChild);
23   elem.appendChild(document.createTextNode(message));
24 }
25
26 function log(message) {
27   status(message);
28 }
29
30 function error(message) {
31   status(message);
32   console.error(message);
33 }
34
35 function unexpectedErrorCallback(e) {
36   error("Unexpected error callback: (" + e.target.error.name + ") " +
37         e.target.webkitErrorMessage);
38 }
39
40 function unexpectedAbortCallback(e) {
41   error("Unexpected abort callback: (" + e.target.error.name + ") " +
42         e.target.webkitErrorMessage);
43 }
44
45 function unexpectedBlockedCallback(e) {
46   error("Unexpected blocked callback!");
47 }
48
49 var DBNAME = 'endurance-db';
50 var DBVERSION = 1;
51 var MAX_DOC_ID = 25;
52
53 var db;
54
55 function initdb() {
56   var request = indexedDB.deleteDatabase(DBNAME);
57   request.onerror = unexpectedErrorCallback;
58   request.onblocked = unexpectedBlockedCallback;
59   request.onsuccess = function () {
60     request = indexedDB.open(DBNAME, DBVERSION);
61     request.onerror = unexpectedErrorCallback;
62     request.onblocked = unexpectedBlockedCallback;
63     request.onupgradeneeded = function () {
64       db = request.result;
65       request.transaction.onabort = unexpectedAbortCallback;
66
67       var syncStore = db.createObjectStore(
68         'sync-chunks', {keyPath: 'sequence', autoIncrement: true});
69       syncStore.createIndex('doc-index', 'docid');
70
71       var docStore = db.createObjectStore(
72         'docs', {keyPath: 'docid'});
73       docStore.createIndex(
74         'owner-index', 'owner', {multiEntry: true});
75
76       var userEventStore = db.createObjectStore(
77         'user-events', {keyPath: 'sequence', autoIncrement: true});
78       userEventStore.createIndex('doc-index', 'docid');
79     };
80     request.onsuccess = function () {
81       log('initialized');
82       $('#offline').disabled = true;
83       $('#online').disabled = false;
84     };
85   };
86 }
87
88 var offline = true;
89 var worker = new Worker('indexeddb_app_worker.js?cachebust');
90 worker.onmessage = function (event) {
91   var data = event.data;
92   switch (data.type) {
93     case 'ABORT':
94       unexpectedAbortCallback(
95         {target:{
96            error: data.error,
97            webkitErrorMessage: data.webkitErrorMessage}});
98       break;
99     case 'ERROR':
100       unexpectedErrorCallback(
101         {target:{
102            error: data.error,
103            webkitErrorMessage: data.webkitErrorMessage}});
104       break;
105     case 'BLOCKED':
106       unexpectedBlockedCallback(
107         {target:{
108            error: data.error,
109            webkitErrorMessage: data.webkitErrorMessage}});
110       break;
111     case 'LOG':
112       log('WORKER: ' + data.message);
113       break;
114     case 'ERROR':
115       error('WORKER: ' + data.message);
116       break;
117     }
118 };
119 worker.onerror = function (event) {
120   error("Error in: " + event.filename + "(" + event.lineno + "): " +
121         event.message);
122 };
123
124 $('#offline').addEventListener('click', goOffline);
125 $('#online').addEventListener('click', goOnline);
126
127 var EVENT_INTERVAL = 100;
128 var eventIntervalId = 0;
129
130 function goOffline() {
131   if (offline)
132     return;
133   offline = true;
134   $('#offline').disabled = offline;
135   $('#online').disabled = !offline;
136   $('#state').innerHTML = 'offline';
137   log('offline');
138
139   worker.postMessage({type: 'offline'});
140
141   eventIntervalId = setInterval(recordEvent, EVENT_INTERVAL);
142 }
143
144 function goOnline() {
145   if (!offline)
146     return;
147   offline = false;
148   $('#offline').disabled = offline;
149   $('#online').disabled = !offline;
150   $('#state').innerHTML = 'online';
151   log('online');
152
153   worker.postMessage({type: 'online'});
154
155   setTimeout(playbackEvents, 100);
156   clearInterval(eventIntervalId);
157   eventIntervalId = 0;
158 };
159
160 function recordEvent() {
161   if (!db) {
162     error("Database not initialized");
163     return;
164   }
165
166   var transaction = db.transaction(['user-events'], 'readwrite');
167   var store = transaction.objectStore('user-events');
168   var record = {
169     // 'sequence' key will be generated
170     docid: Math.floor(Math.random() * MAX_DOC_ID),
171     timestamp: new Date(),
172     data: randomString(256)
173   };
174
175   log('putting user event');
176   var request = store.put(record);
177   request.onerror = unexpectedErrorCallback;
178   transaction.onabort = unexpectedAbortCallback;
179   transaction.oncomplete = function () {
180     log('put user event');
181   };
182 }
183
184 function sendEvent(record, callback) {
185   setTimeout(
186     function () {
187       if (offline)
188         callback(false);
189       else {
190         var serialization = JSON.stringify(record);
191         callback(true);
192       }
193     },
194     Math.random() * 200); // Simulate network jitter
195 }
196
197 var PLAYBACK_NONE = 0;
198 var PLAYBACK_SUCCESS = 1;
199 var PLAYBACK_FAILURE = 2;
200
201 function playbackEvent(callback) {
202   log('playbackEvent');
203   var result = false;
204   var transaction = db.transaction(['user-events'], 'readonly');
205   transaction.onabort = unexpectedAbortCallback;
206   var store = transaction.objectStore('user-events');
207   var cursorRequest = store.openCursor();
208   cursorRequest.onerror = unexpectedErrorCallback;
209   cursorRequest.onsuccess = function () {
210     var cursor = cursorRequest.result;
211     if (cursor) {
212       var record = cursor.value;
213       var key = cursor.key;
214       // NOTE: sendEvent is asynchronous so transaction should finish
215       sendEvent(
216         record,
217         function (success) {
218           if (success) {
219             // Use another transaction to delete event
220             var transaction = db.transaction(['user-events'], 'readwrite');
221             transaction.onabort = unexpectedAbortCallback;
222             var store = transaction.objectStore('user-events');
223             var deleteRequest = store.delete(key);
224             deleteRequest.onerror = unexpectedErrorCallback;
225             transaction.oncomplete = function () {
226               // successfully sent and deleted event
227               callback(PLAYBACK_SUCCESS);
228             };
229           } else {
230             // No progress made
231             callback(PLAYBACK_FAILURE);
232           }
233         });
234     } else {
235       callback(PLAYBACK_NONE);
236     }
237   };
238 }
239
240 var playback = false;
241
242 function playbackEvents() {
243   log('playbackEvents');
244   if (!db) {
245     error("Database not initialized");
246     return;
247   }
248
249   if (playback)
250     return;
251
252   playback = true;
253   log("Playing back events");
254
255   function nextEvent() {
256     playbackEvent(
257       function (result) {
258         switch (result) {
259           case PLAYBACK_NONE:
260             playback = false;
261             log("Done playing back events");
262             return;
263           case PLAYBACK_SUCCESS:
264             setTimeout(nextEvent, 0);
265             return;
266           case PLAYBACK_FAILURE:
267             playback = false;
268             log("Failure during playback (dropped offline?)");
269             return;
270         }
271       });
272   }
273
274   nextEvent();
275 }
276
277 function randomString(len) {
278   var s = '';
279   while (len--)
280     s += Math.floor((Math.random() * 36)).toString(36);
281   return s;
282 }
283
284 window.onload = function () {
285   log("initializing...");
286   initdb();
287 };