1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
|
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the Qt WebKit module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\page qtwebkit-guide-cache.html
\title QtWebKit Guide - Client Storage
\chapter Client Storage
This section of the \l{QtWebKit Guide} serves as an introduction to the
\l{HTML5 Web Storage} features of QtWebKit.
Traditional mobile web development centered around the limitations of client
handsets, which had very little storage for applications. As handsets become
more powerful, however, this assumption is no longer valid. HTML5's newly
introduced \l{HTML5 Web Storage}{Web Storage} features expand application
storage on the client.
HTML 5 standardizes access to an application's local data via \c{LocalStorage}
and \c{SessionStorage} APIs. These APIs boost the amount of client storage
available to web applications. They also can effectively replace cookies as a
means to maintain application state and track user preferences.
Local storage persists indefinitely, while session storage lasts only for the
duration of a window session. Local storage is available from any page or window
from the same site, while session storage is local to each window. Both local
and session storage rely on simple key/value pairs, with keys and values both
stored as strings.
Local and session storage are not the only client storage available. HTML 5
WebSQL serves as a more full-featured, client-side database. WebSQL brings
SQLite-based structured database functionality, typically deployed on servers,
to client browser applications. WebSQL is appropriate for data-intensive
applications requiring complex queries rather than simple key/value access.
WebSQL database transaction calls help avoid interfaces from locking up,
facilitate rollback and error handling, and protect against SQL injection.
Database versioning allows you to manage schema changes incrementally.
\section1 Simple Data Storage
The \c{localStorage} and \c{sessionStorage} APIs offer applications up to 5MB of
data storage. They both share the same simple key/value interface, but have
different namespaces and also differ in the extent to which data is available.
Local storage persists indefinitely, while session storage only lasts for the
duration of a window session. Local storage is available from any page or window
from the same site, while session storage is local to each window.
The following examples demonstrate the API interface. While these use
\c{localStorage} as an example, the same set of API calls work for
\c{sessionStorage}, which is also available within the \c{window} object.
The following performs an initial check for support of browser-based
storage and assigns the database to a variable:
\code
if (window.localStorage) {
var db = window.localStorage;
// storage functionality here
}
else {
// store data remotely?
}
\endcode
The \c{getItem()} method retrieves the value of a database field named
\c{key}:
\code
var value = db.getItem("key");
\endcode
Note that both keys and values are represented as strings. If you specify any
other type of data, it is converted silently to a string representation. (See
\l{Storing Non-String Data} for ways around this limitation.) If \c{getItem()}
returns \c{null} rather than a string value, it means the specified key does not
exist.
The \c{setItem()} method establishes a new value. When adding data, it is a good
idea to check to make sure you haven't exceeded the allotted storage space:
\code
try {
db.setItem("key", "string");
}
catch(err) {
if (err.QUOTA_EXCEEDED_ERR) {
// storage space is exceeded
}
}
\endcode
The \c{removeItem()} method deletes database fields:
\code
db.removeItem("key");
\endcode
The \c{clear()} method deletes all key/value pairs within the database, either
for an entire site in the case of \c{localStorage}, or for an individual window
session in the case of \c{sessionStorage}:
\code
db.clear();
\endcode
Databases can be accessed as arrays using index notation, useful in cases where
you may not know all the field names. The \c{length} property returns the number
of fields in the database, and the \c{key()} method returns the name of the key
corresponding to a given index. The following reflects the contents of a
database in a JavaScript object:
\code
var obj = {};
for ( var i = 0, l = db.length ; i < l ; i++ ) {
obj[ db.key(i) ] = db.getItem( db.key(i) );
}
\endcode
Since keys correspond to array indexes, you should not add or remove keys during
any operation that iterates over the full set of key/value pairs. Newly
introduced keys are introduced randomly into the array's sequence.
The following displays simple storage functionality. The application prompts for
a login and password if they are unavailable. This locally stored data is
available the next time users open the browser. However, the contents of the
credit card field is stored only for the duration of the browing session.
\l{ex_storage}{\inlineimage webkit-guide/scr_storage.png
}
\l{storage_css}{(CSS)}
\l{storage_js}{(JavaScript)}
\section2 Storing Non-String Data
Since local and session storage APIs only support string values, you need to
be careful not to allow errors that result from passive conversions from
other data types. The following sample shows how such an error might come
about:
\code
var db = window.localStorage;
var saveCardInfo;
// user expresses preference NOT to save credit card info:
saveCardInfo = false;
// BUG happens here...
db.setItem("save_card_info", saveCardInfo);
// variable is now a string, not a boolean:
saveCardInfo = db.getItem("save_card_info");
// both "true" and "false" strings evaluate as true...
if ( saveCardInfo ) {
// ...so this code always executes...
}
else {
// ...and this code never executes.
}
\endcode
The user's preference to retain credit card information is expressed within
the application as a \c{true} or \c{false} boolean value. When each value is
passed to storage, however, it is passively converted to a string. When
reassigned to a JavaScript variable, it no longer serves as a valid boolean
test. The application falsely assumes users want to save credit card
information, regardless of their expressed preference.
The following sample fixes the problem. Instead of using \c{true} and
\c{false} boolean values, it converts \c{1} and \c{0} strings to numbers:
\code
var db = window.localStorage;
var saveCardInfo = 0;
db.setItem("save_card_info", saveCardInfo);
// multiplying forces numeric output:
saveCardInfo = db.getItem("save_card_info") * 1;
\endcode
For a more reliable alternative, store values as JSON strings and rely on
automatic type conversion when subsequently parsing them. The following
sample shows how parsing JSON preserves both boolean and numeric data:
\code
var saveCardInfo = true; // boolean
var shipMethod = 2; // number
var db = window.localStorage;
db.setItem("save_card_info", JSON.stringify(saveCardInfo));
db.setItem("ship_method", JSON.stringify(shipMethod));
saveCardInfo = JSON.parse(db.getItem("save_card_info")); // boolean
shipMethod = JSON.parse(db.getItem("ship_method")); // number
\endcode
Note that this simple approach may cause problems of its own. For example,
perhaps the words \c{true} and \c{false} really should be represented
as strings. Encapsulating data within objects accounts for such variability:
\code
var db = window.localStorage;
var obj = {
bool : true,
str : "true",
num : 1
};
db.setItem("appState", JSON.stringify(obj)); // to database...
// "appState" is "{'bool':true,'num':1,'str':'true'}"
obj = JSON.parse(db.getItem("appState")); // ...and back
// obj is same as initially defined.
\endcode
The ability to save objects as JSON strings means that you can save an
application's state within a single database field. For example, you might
use the following approach to save the entire contents of a shopping cart in
a single field for later use:
\code
var db = window.localStorage;
var cart = { items: [] };
cart.message = "From your loving uncle";
cart.items.push({
description : "Floor to Ceiling Shoe Rack",
id : 203174676,
price : 99.95,
quantity : 1,
weight : 20,
});
cart.items.push({
description : "Automatic Laser Toy for Cats",
id : 203345371,
price : 19.95,
quantity : 2,
weight : 0.5,
});
// save all cumulative items:
db.setItem("cart", JSON.stringify(cart));
// extract items from storage:
cart = JSON.parse(db.getItem("cart"));
\endcode
JSON allows you to store data types, but functions are ignored. That makes
it more difficult to preserve objects representing fully functional
applications.
\section2 Storage Events
The \c{storage} event allows applications to respond indirectly to modified
data resulting from calls to \c{setItem()}, \c{removeItem()}, or
\c{clear()}. This may be useful in providing users with visual feedback
notifying them of data that is modified locally, perhaps rather than being
sent to a remote server:
\code
window.addEventListener("storage", function(event){
var icon = document.querySelector("#indicator");
if (event.storageArea.length) {
icon.className = "writing";
}
else {
icon.className = "empty";
}
}, false);
\endcode
The \c{storage} event's \c{storageArea} attribute returns the
\c{localStorage} or \c{sessionStorage} object being modified. The \c{key} is
the name of the field being modified, and \c{oldValue} and \c{newValue} are
its values before and after the event. The \c{url} is the page that called
the method triggering the change.
\section1 WebSQL Databases
While common local- or session-based databases are capable of storing complex
data structures, QtWebKit-based browsers can also rely upon the WebSQL standard,
which brings SQLite-based structured database functionality, typically deployed
on servers, to client browser applications. Based on SQLite version 3.6.19,
WebSQL is appropriate for data-intensive applications requring complex queries
rather than simple key/value access.
The following test confirms support for WebSQL:
\code
if (!!window.openDatabase) {
// supports WebSQL
}
\endcode
Calls to databases made via the WebSQL API are made asynchronously via
transactions to avoid the user interface from locking up, as database
interaction may occur from several windows at a time.
The three core API methods are:
\list
\o \c{openDatabase()}
\o \c{transaction()}
\o \c{executeSql()}
\endlist
\section2 Creating and Opening a New Database
To create and open a database, use \c{openDatabase()}on the Window object,
for example:
\code
var db = openDatabase('mydb', '1.0', 'my first database', 2*1024*1024);
var db = openDatabase('notes', '', 'The Example Notes App!', 1048576);
\endcode
The four required arguments are the database name, version, display name,
and estimated size in bytes. You can supply a function as an optional fifth
argument to serve as a callback when a database is created. It may be used
to call the \c{changeversion()} method, in which case the callback is
invoked with an empty string for the database version.
The second example above specifies an empty string for the version. In this
case, the database opens no matter what the database version is. (An
\c{openDatabase()} call specifying the wrong version for an existing
database throws an \c{INVALID_STATE_ERR} exception.) You can then query the
version by examining the database object's version property, for example:
\code
var version = db.version;
\endcode
Note that you don't need to close a client-side Web SQL database when
you're done working with it.
\section2 Transaction Calls and ExecuteSQL Method
Performing database transactions is superior to running SQL statements
directly because transactions are not committed if they fail and you can
undo them if needed. Transactions also allow you to handle errors using a
callback. To implement a transaction, specify a callback function such as
the following:
\code
db.transaction(function (tx) {
// SQL details on the tx object go here
}
\endcode
The \c{transaction()} method takes one to three arguments:
\list
\o a required transaction callback, in which \c{executeSQL()} calls
belong
\o an optional transaction error object
\o an optional success callback.
\endlist
Use the \c{executeSQL()} method to specify SQL statements for read and write
operations. The method protects against SQL injection and provides a
callback method to process the results of any SQL queries you specify. The
\c{executeSQL()} method takes from one to four arguments:
\list
\o a required SQL statement
\o an optional object array of arguments
\o an optional SQL statement callback
\o an optional SQL statement error callback
\endlist
The example below creates the database if it doesn't exist, adds a
two-column table to the database, and adds a row of data to the table:
\code
var db = openDatabase('mydb', '1.0', 'my first database', 2 * 1024 * 1024);
db.transaction(function (tx) {
tx.executeSql('CREATE TABLE IF NOT EXISTS foo (id unique, text)');
tx.executeSql('INSERT INTO foo (id, text) VALUES (1, "synergies")');
});
\endcode
To capture data from the user or an external source, use \c{?} placeholders
to map that data into the SQL query. This ensures the data doesn't
compromise database security, for example from SQL injection:
\code
tx.executeSql('INSERT INTO foo (id, text) VALUES (?, ?)', [id, value]);
\endcode
\c{id} and \c{value} are external variables, and \c{executeSql} maps
the items in the array to the \c{?}s.
To select values from the table, use a callback to capture the results:
\code
tx.executeSql('SELECT * FROM foo', [], function(tx, results) {
for (var i = 0 , len = results.rows.length; i < len; i++) {
// do something with results.rows.item(i).text
}
});
\endcode
No fields are mapped in the above query, but to use the third argument you
need to pass in an empty array as the second argument.
The SQL statement callback for \c{executeSQL()} is called with the
\c{transaction} object and a SQL statement \c{result} object. The \c{result}
gives access to the ID of the last inserted row, the number of rows
affected, and an indexed list representing the rows returned, in the order
returned.
The \c{result} object contains an array-like \c{rows} object. It has a
length, but to access individual rows you need to use
\c{results.rows.item(i)}, where \c{i} is the index of the row. This returns
an object representation of each row. For example, if your database has a
\c{name} and an \c{age} field, the \c{row} contains a \c{name} and an
\c{age} property. The value of the \c{age} field can be accessed using
\c{results.rows.item(i).age}.
\section2 Changing Database Versions
Each database has one version at a time and multiple versions cannot exist
at one time. Versions allow you to manage schema changes incrementally.
You can change the version of a client-side Web SQL database using the
\c{changeversion()} method:
\code
if (db.version == "1.0") {
try {
// comment out for crash recovery.
db.changeVersion("1.0", "2.0", cv_1_0_2_0, oops_1_0_2_0, success_1_0_2_0);
} catch(e) {
alert('changeversion 1.0 -> 2.0 failed');
alert('DB Version: '+db.version);
}
}
\endcode
\c{changeversion()} takes the following arguments: required old and new
version numbers, optional SQL transaction callback, optional SQL transaction
error callback, and optional success callback.
\section2 Errors
Asynchronous API errors are reported using callbacks that have a
\c{SQLError} object as one of their arguments. \c{SQLError} contains a code
from the table below and a localized message string.
Error codes are:
\list
\o 0 \c{UNKNOWN_ERROR} Transaction failed for reasons unrelated to the DB
\o 1 \c{DATABASE_ERROR} Statement failed for DB reasons not covered by other
code
\o 2 \c{VERSION_ERROR} DB version doesn't match expected version
\o 3 \c{TOO_LARGE_ERROR} Data returned from DB was too large. Try the
\c{SQL LIMIT} modifier.
\o 4 \c{QUOTA_ERROR} Insufficient remaining storage
\o 5 \c{SYNTAX_ERROR} Syntax error, argument mismatch, or unallowed
statement
\o 6 \c{CONSTRAINT_ERROR} An \c{INSERT}, \c{UPDATE}, or \c{REPLACE}
statement failed due to a constraint error
\o 7 \c{TIMEOUT_ERROR} Timeout waiting for transaction lock
\endlist
\bold{See Also:}
\l{HTML5 Doctor: Introducing Web SQL Databases}
\list
\o \l{QtWebKit Guide} -back to the main page
\endlist
*/
*/
|