Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / resources / IndexedDBViews.js
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 /**
32  * @constructor
33  * @extends {WebInspector.VBox}
34  * @param {!WebInspector.IndexedDBModel.Database} database
35  */
36 WebInspector.IDBDatabaseView = function(database)
37 {
38     WebInspector.VBox.call(this);
39     this.registerRequiredCSS("indexedDBViews.css");
40
41     this.element.classList.add("indexed-db-database-view");
42
43     this._headersListElement = this.element.createChild("ol", "outline-disclosure");
44     this._headersTreeOutline = new TreeOutline(this._headersListElement);
45     this._headersTreeOutline.expandTreeElementsWhenArrowing = true;
46
47     this._securityOriginTreeElement = new TreeElement("", null, false);
48     this._securityOriginTreeElement.selectable = false;
49     this._headersTreeOutline.appendChild(this._securityOriginTreeElement);
50
51     this._nameTreeElement = new TreeElement("", null, false);
52     this._nameTreeElement.selectable = false;
53     this._headersTreeOutline.appendChild(this._nameTreeElement);
54
55     this._intVersionTreeElement = new TreeElement("", null, false);
56     this._intVersionTreeElement.selectable = false;
57     this._headersTreeOutline.appendChild(this._intVersionTreeElement);
58
59     this._stringVersionTreeElement = new TreeElement("", null, false);
60     this._stringVersionTreeElement.selectable = false;
61     this._headersTreeOutline.appendChild(this._stringVersionTreeElement);
62
63     this.update(database);
64 }
65
66 WebInspector.IDBDatabaseView.prototype = {
67     /**
68      * @param {string} name
69      * @param {string} value
70      */
71     _formatHeader: function(name, value)
72     {
73         var fragment = document.createDocumentFragment();
74         fragment.createChild("div", "attribute-name").textContent = name + ":";
75         fragment.createChild("div", "attribute-value source-code").textContent = value;
76
77         return fragment;
78     },
79
80     _refreshDatabase: function()
81     {
82         this._securityOriginTreeElement.title = this._formatHeader(WebInspector.UIString("Security origin"), this._database.databaseId.securityOrigin);
83         this._nameTreeElement.title = this._formatHeader(WebInspector.UIString("Name"), this._database.databaseId.name);
84         this._stringVersionTreeElement.title = this._formatHeader(WebInspector.UIString("String Version"), this._database.version);
85         this._intVersionTreeElement.title = this._formatHeader(WebInspector.UIString("Integer Version"), this._database.intVersion);
86     },
87
88     /**
89      * @param {!WebInspector.IndexedDBModel.Database} database
90      */
91     update: function(database)
92     {
93         this._database = database;
94         this._refreshDatabase();
95     },
96
97     __proto__: WebInspector.VBox.prototype
98 }
99
100
101 /**
102  * @constructor
103  * @extends {WebInspector.VBox}
104  * @param {!WebInspector.IndexedDBModel} model
105  * @param {!WebInspector.IndexedDBModel.DatabaseId} databaseId
106  * @param {!WebInspector.IndexedDBModel.ObjectStore} objectStore
107  * @param {?WebInspector.IndexedDBModel.Index} index
108  */
109 WebInspector.IDBDataView = function(model, databaseId, objectStore, index)
110 {
111     WebInspector.VBox.call(this);
112     this.registerRequiredCSS("indexedDBViews.css");
113
114     this._model = model;
115     this._databaseId = databaseId;
116     this._isIndex = !!index;
117
118     this.element.classList.add("indexed-db-data-view");
119
120     var editorToolbar = this._createEditorToolbar();
121     this.element.appendChild(editorToolbar);
122
123     this._dataGridContainer = this.element.createChild("div", "fill");
124     this._dataGridContainer.classList.add("data-grid-container");
125
126     this._refreshButton = new WebInspector.StatusBarButton(WebInspector.UIString("Refresh"), "refresh-storage-status-bar-item");
127     this._refreshButton.addEventListener("click", this._refreshButtonClicked, this);
128
129     this._clearButton = new WebInspector.StatusBarButton(WebInspector.UIString("Clear object store"), "clear-storage-status-bar-item");
130     this._clearButton.addEventListener("click", this._clearButtonClicked, this);
131
132     this._pageSize = 50;
133     this._skipCount = 0;
134
135     this.update(objectStore, index);
136     this._entries = [];
137 }
138
139 WebInspector.IDBDataView.prototype = {
140     /**
141      * @return {!WebInspector.DataGrid}
142      */
143     _createDataGrid: function()
144     {
145         var keyPath = this._isIndex ? this._index.keyPath : this._objectStore.keyPath;
146
147         var columns = [];
148         columns.push({id: "number", title: WebInspector.UIString("#"), width: "50px"});
149         columns.push({id: "key", titleDOMFragment: this._keyColumnHeaderFragment(WebInspector.UIString("Key"), keyPath)});
150         if (this._isIndex)
151             columns.push({id: "primaryKey", titleDOMFragment: this._keyColumnHeaderFragment(WebInspector.UIString("Primary key"), this._objectStore.keyPath)});
152         columns.push({id: "value", title: WebInspector.UIString("Value")});
153
154         var dataGrid = new WebInspector.DataGrid(columns);
155         return dataGrid;
156     },
157
158     /**
159      * @param {string} prefix
160      * @param {*} keyPath
161      * @return {!DocumentFragment}
162      */
163     _keyColumnHeaderFragment: function(prefix, keyPath)
164     {
165         var keyColumnHeaderFragment = document.createDocumentFragment();
166         keyColumnHeaderFragment.appendChild(document.createTextNode(prefix));
167         if (keyPath === null)
168             return keyColumnHeaderFragment;
169
170         keyColumnHeaderFragment.appendChild(document.createTextNode(" (" + WebInspector.UIString("Key path: ")));
171         if (keyPath instanceof Array) {
172             keyColumnHeaderFragment.appendChild(document.createTextNode("["));
173             for (var i = 0; i < keyPath.length; ++i) {
174                 if (i != 0)
175                     keyColumnHeaderFragment.appendChild(document.createTextNode(", "));
176                 keyColumnHeaderFragment.appendChild(this._keyPathStringFragment(keyPath[i]));
177             }
178             keyColumnHeaderFragment.appendChild(document.createTextNode("]"));
179         } else {
180             var keyPathString = /** @type {string} */ (keyPath);
181             keyColumnHeaderFragment.appendChild(this._keyPathStringFragment(keyPathString));
182         }
183         keyColumnHeaderFragment.appendChild(document.createTextNode(")"));
184         return keyColumnHeaderFragment;
185     },
186
187     /**
188      * @param {string} keyPathString
189      * @return {!DocumentFragment}
190      */
191     _keyPathStringFragment: function(keyPathString)
192     {
193         var keyPathStringFragment = document.createDocumentFragment();
194         keyPathStringFragment.appendChild(document.createTextNode("\""));
195         var keyPathSpan = keyPathStringFragment.createChild("span", "source-code console-formatted-string");
196         keyPathSpan.textContent = keyPathString;
197         keyPathStringFragment.appendChild(document.createTextNode("\""));
198         return keyPathStringFragment;
199     },
200
201     /**
202      * @return {!Element}
203      */
204     _createEditorToolbar: function()
205     {
206         var editorToolbar = document.createElement("div");
207         editorToolbar.classList.add("status-bar");
208         editorToolbar.classList.add("data-view-toolbar");
209
210         this._pageBackButton = editorToolbar.createChild("button", "back-button");
211         this._pageBackButton.classList.add("status-bar-item");
212         this._pageBackButton.title = WebInspector.UIString("Show previous page.");
213         this._pageBackButton.disabled = true;
214         this._pageBackButton.appendChild(document.createElement("img"));
215         this._pageBackButton.addEventListener("click", this._pageBackButtonClicked.bind(this), false);
216         editorToolbar.appendChild(this._pageBackButton);
217
218         this._pageForwardButton = editorToolbar.createChild("button", "forward-button");
219         this._pageForwardButton.classList.add("status-bar-item");
220         this._pageForwardButton.title = WebInspector.UIString("Show next page.");
221         this._pageForwardButton.disabled = true;
222         this._pageForwardButton.appendChild(document.createElement("img"));
223         this._pageForwardButton.addEventListener("click", this._pageForwardButtonClicked.bind(this), false);
224         editorToolbar.appendChild(this._pageForwardButton);
225
226         this._keyInputElement = editorToolbar.createChild("input", "key-input");
227         this._keyInputElement.placeholder = WebInspector.UIString("Start from key");
228         this._keyInputElement.addEventListener("paste", this._keyInputChanged.bind(this));
229         this._keyInputElement.addEventListener("cut", this._keyInputChanged.bind(this));
230         this._keyInputElement.addEventListener("keypress", this._keyInputChanged.bind(this));
231         this._keyInputElement.addEventListener("keydown", this._keyInputChanged.bind(this));
232
233         return editorToolbar;
234     },
235
236     _pageBackButtonClicked: function()
237     {
238         this._skipCount = Math.max(0, this._skipCount - this._pageSize);
239         this._updateData(false);
240     },
241
242     _pageForwardButtonClicked: function()
243     {
244         this._skipCount = this._skipCount + this._pageSize;
245         this._updateData(false);
246     },
247
248     _keyInputChanged: function()
249     {
250         window.setTimeout(this._updateData.bind(this, false), 0);
251     },
252
253     /**
254      * @param {!WebInspector.IndexedDBModel.ObjectStore} objectStore
255      * @param {?WebInspector.IndexedDBModel.Index} index
256      */
257     update: function(objectStore, index)
258     {
259         this._objectStore = objectStore;
260         this._index = index;
261
262         if (this._dataGrid)
263             this._dataGrid.detach();
264         this._dataGrid = this._createDataGrid();
265         this._dataGrid.show(this._dataGridContainer);
266
267         this._skipCount = 0;
268         this._updateData(true);
269     },
270
271     /**
272      * @param {string} keyString
273      */
274     _parseKey: function(keyString)
275     {
276         var result;
277         try {
278             result = JSON.parse(keyString);
279         } catch (e) {
280             result = keyString;
281         }
282         return result;
283     },
284
285     /**
286      * @param {boolean} force
287      */
288     _updateData: function(force)
289     {
290         var key = this._parseKey(this._keyInputElement.value);
291         var pageSize = this._pageSize;
292         var skipCount = this._skipCount;
293         this._refreshButton.setEnabled(false);
294         this._clearButton.setEnabled(!this._isIndex);
295
296         if (!force && this._lastKey === key && this._lastPageSize === pageSize && this._lastSkipCount === skipCount)
297             return;
298
299         if (this._lastKey !== key || this._lastPageSize !== pageSize) {
300             skipCount = 0;
301             this._skipCount = 0;
302         }
303         this._lastKey = key;
304         this._lastPageSize = pageSize;
305         this._lastSkipCount = skipCount;
306
307         /**
308          * @param {!Array.<!WebInspector.IndexedDBModel.Entry>} entries
309          * @param {boolean} hasMore
310          * @this {WebInspector.IDBDataView}
311          */
312         function callback(entries, hasMore)
313         {
314             this._refreshButton.setEnabled(true);
315             this.clear();
316             this._entries = entries;
317             for (var i = 0; i < entries.length; ++i) {
318                 var data = {};
319                 data["number"] = i + skipCount;
320                 data["key"] = entries[i].key;
321                 data["primaryKey"] = entries[i].primaryKey;
322                 data["value"] = entries[i].value;
323
324                 var primaryKey = JSON.stringify(this._isIndex ? entries[i].primaryKey : entries[i].key);
325                 var node = new WebInspector.IDBDataGridNode(data);
326                 this._dataGrid.rootNode().appendChild(node);
327             }
328
329             this._pageBackButton.disabled = skipCount === 0;
330             this._pageForwardButton.disabled = !hasMore;
331         }
332
333         var idbKeyRange = key ? window.webkitIDBKeyRange.lowerBound(key) : null;
334         if (this._isIndex)
335             this._model.loadIndexData(this._databaseId, this._objectStore.name, this._index.name, idbKeyRange, skipCount, pageSize, callback.bind(this));
336         else
337             this._model.loadObjectStoreData(this._databaseId, this._objectStore.name, idbKeyRange, skipCount, pageSize, callback.bind(this));
338     },
339
340     _refreshButtonClicked: function(event)
341     {
342         this._updateData(true);
343     },
344
345     _clearButtonClicked: function(event)
346     {
347         /**
348          * @this {WebInspector.IDBDataView}
349          */
350         function cleared() {
351             this._clearButton.setEnabled(true);
352             this._updateData(true);
353         }
354         this._clearButton.setEnabled(false);
355         this._model.clearObjectStore(this._databaseId, this._objectStore.name, cleared.bind(this));
356     },
357
358     get statusBarItems()
359     {
360         return [this._refreshButton.element, this._clearButton.element];
361     },
362
363     clear: function()
364     {
365         this._dataGrid.rootNode().removeChildren();
366         for (var i = 0; i < this._entries.length; ++i) {
367             this._entries[i].key.release();
368             this._entries[i].primaryKey.release();
369             this._entries[i].value.release();
370         }
371         this._entries = [];
372     },
373
374     __proto__: WebInspector.VBox.prototype
375 }
376
377 /**
378  * @constructor
379  * @extends {WebInspector.DataGridNode}
380  * @param {!Object.<string, *>} data
381  */
382 WebInspector.IDBDataGridNode = function(data)
383 {
384     WebInspector.DataGridNode.call(this, data, false);
385     this.selectable = false;
386 }
387
388 WebInspector.IDBDataGridNode.prototype = {
389     /**
390      * @return {!Element}
391      */
392     createCell: function(columnIdentifier)
393     {
394         var cell = WebInspector.DataGridNode.prototype.createCell.call(this, columnIdentifier);
395         var value = this.data[columnIdentifier];
396
397         switch (columnIdentifier) {
398         case "value":
399         case "key":
400         case "primaryKey":
401             cell.removeChildren();
402             this._formatValue(cell, value);
403             break;
404         default:
405         }
406
407         return cell;
408     },
409
410     _formatValue: function(cell, value)
411     {
412         var type = value.subtype || value.type;
413         var contents = cell.createChild("div", "source-code console-formatted-" + type);
414
415         switch (type) {
416         case "object":
417         case "array":
418             var section = new WebInspector.ObjectPropertiesSection(value, value.description)
419             section.editable = false;
420             section.skipProto = true;
421             contents.appendChild(section.element);
422             break;
423         case "string":
424             contents.classList.add("primitive-value");
425             contents.appendChild(document.createTextNode("\"" + value.description + "\""));
426             break;
427         default:
428             contents.classList.add("primitive-value");
429             contents.appendChild(document.createTextNode(value.description));
430         }
431     },
432
433     __proto__: WebInspector.DataGridNode.prototype
434 }
435