Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / imports / localstorage / plugin.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <QStringList>
42 #include <QtDeclarative/qdeclarativeextensionplugin.h>
43 #include <QtDeclarative/qdeclarative.h>
44 #include <private/qdeclarativeengine_p.h>
45 #include <QDebug>
46 #include <private/qv8engine_p.h>
47 #include <QtSql/qsqldatabase.h>
48 #include <QtSql/qsqlquery.h>
49 #include <QtSql/qsqlerror.h>
50 #include <QtSql/qsqlrecord.h>
51 #include <QtSql/qsqlfield.h>
52 #include <QtCore/qstandardpaths.h>
53 #include <QtCore/qstack.h>
54 #include <QtCore/qcryptographichash.h>
55 #include <QtCore/qsettings.h>
56 #include <QtCore/qdir.h>
57 #include <private/qv8sqlerrors_p.h>
58
59
60 #define V8THROW_SQL(error, desc) \
61 { \
62     v8::Local<v8::Value> v = v8::Exception::Error(engine->toString(desc)); \
63     v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \
64     v8::ThrowException(v); \
65     return v8::Handle<v8::Value>(); \
66 }
67
68 #define V8THROW_SQL_VOID(error, desc) \
69 { \
70     v8::Local<v8::Value> v = v8::Exception::Error(engine->toString(desc)); \
71     v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \
72     v8::ThrowException(v); \
73     return; \
74 }
75
76 #define V8THROW_REFERENCE(string) { \
77     v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
78     return v8::Handle<v8::Value>(); \
79 }
80
81 #define V8THROW_REFERENCE_VOID(string) { \
82     v8::ThrowException(v8::Exception::ReferenceError(v8::String::New(string))); \
83     return; \
84 }
85
86 class QDeclarativeSqlDatabaseData : public QV8Engine::Deletable
87 {
88 public:
89     QDeclarativeSqlDatabaseData(QV8Engine *engine);
90     ~QDeclarativeSqlDatabaseData();
91
92     v8::Persistent<v8::Function> constructor;
93     v8::Persistent<v8::Function> queryConstructor;
94     v8::Persistent<v8::Function> rowsConstructor;
95 };
96
97 V8_DEFINE_EXTENSION(QDeclarativeSqlDatabaseData, databaseData)
98
99 class QV8SqlDatabaseResource : public QV8ObjectResource
100 {
101     V8_RESOURCE_TYPE(SQLDatabaseType)
102
103 public:
104     enum Type { Database, Query, Rows };
105     QV8SqlDatabaseResource(QV8Engine *e)
106     : QV8ObjectResource(e), type(Database), inTransaction(false), readonly(false), forwardOnly(false) {}
107
108     ~QV8SqlDatabaseResource() {
109     }
110
111     Type type;
112     QSqlDatabase database;
113
114     QString version; // type == Database
115
116     bool inTransaction; // type == Query
117     bool readonly;   // type == Query
118
119     QSqlQuery query; // type == Rows
120     bool forwardOnly; // type == Rows
121 };
122
123 static v8::Handle<v8::Value> qmlsqldatabase_version(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
124 {
125     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
126     if (!r || r->type != QV8SqlDatabaseResource::Database)
127         V8THROW_REFERENCE("Not a SQLDatabase object");
128
129     return r->engine->toString(r->version);
130 }
131
132 static v8::Handle<v8::Value> qmlsqldatabase_rows_length(v8::Local<v8::String> /* property */, const v8::AccessorInfo& info)
133 {
134     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
135     if (!r || r->type != QV8SqlDatabaseResource::Rows)
136         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
137
138     int s = r->query.size();
139     if (s < 0) {
140         // Inefficient
141         if (r->query.last()) {
142             s = r->query.at() + 1;
143         } else {
144             s = 0;
145         }
146     }
147     return v8::Integer::New(s);
148 }
149
150 static v8::Handle<v8::Value> qmlsqldatabase_rows_forwardOnly(v8::Local<v8::String> /* property */,
151                                                              const v8::AccessorInfo& info)
152 {
153     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
154     if (!r || r->type != QV8SqlDatabaseResource::Rows)
155         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
156     return v8::Boolean::New(r->query.isForwardOnly());
157 }
158
159 static void qmlsqldatabase_rows_setForwardOnly(v8::Local<v8::String> /* property */,
160                                                v8::Local<v8::Value> value,
161                                                const v8::AccessorInfo& info)
162 {
163     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
164     if (!r || r->type != QV8SqlDatabaseResource::Rows)
165         V8THROW_REFERENCE_VOID("Not a SQLDatabase::Rows object");
166
167     r->query.setForwardOnly(value->BooleanValue());
168 }
169
170 QDeclarativeSqlDatabaseData::~QDeclarativeSqlDatabaseData()
171 {
172     qPersistentDispose(constructor);
173     qPersistentDispose(queryConstructor);
174     qPersistentDispose(rowsConstructor);
175 }
176
177 static QString qmlsqldatabase_databasesPath(QV8Engine *engine)
178 {
179     return engine->engine()->offlineStoragePath() +
180            QDir::separator() + QLatin1String("Databases");
181 }
182
183 static void qmlsqldatabase_initDatabasesPath(QV8Engine *engine)
184 {
185     QDir().mkpath(qmlsqldatabase_databasesPath(engine));
186 }
187
188 static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV8Engine *engine)
189 {
190     return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName;
191 }
192
193 static v8::Handle<v8::Value> qmlsqldatabase_rows_index(QV8SqlDatabaseResource *r, uint32_t index)
194 {
195     if (r->query.at() == (int)index || r->query.seek(index)) {
196
197         QSqlRecord record = r->query.record();
198         // XXX optimize
199         v8::Local<v8::Object> row = v8::Object::New();
200         for (int ii = 0; ii < record.count(); ++ii) {
201             QVariant v = record.value(ii);
202             if (v.isNull()) {
203                 row->Set(r->engine->toString(record.fieldName(ii)), v8::Null());
204             } else {
205                 row->Set(r->engine->toString(record.fieldName(ii)),
206                          r->engine->fromVariant(v));
207             }
208         }
209         return row;
210     } else {
211         return v8::Undefined();
212     }
213 }
214
215 static v8::Handle<v8::Value> qmlsqldatabase_rows_index(uint32_t index, const v8::AccessorInfo& info)
216 {
217     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(info.This());
218     if (!r || r->type != QV8SqlDatabaseResource::Rows)
219         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
220
221     return qmlsqldatabase_rows_index(r, index);
222 }
223
224 static v8::Handle<v8::Value> qmlsqldatabase_rows_item(const v8::Arguments& args)
225 {
226     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
227     if (!r || r->type != QV8SqlDatabaseResource::Rows)
228         V8THROW_REFERENCE("Not a SQLDatabase::Rows object");
229
230     return qmlsqldatabase_rows_index(r, args.Length()?args[0]->Uint32Value():0);
231 }
232
233 static v8::Handle<v8::Value> qmlsqldatabase_executeSql(const v8::Arguments& args)
234 {
235     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
236     if (!r || r->type != QV8SqlDatabaseResource::Query)
237         V8THROW_REFERENCE("Not a SQLDatabase::Query object");
238
239     QV8Engine *engine = r->engine;
240
241     if (!r->inTransaction)
242         V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,QDeclarativeEngine::tr("executeSql called outside transaction()"));
243
244     QSqlDatabase db = r->database;
245
246     QString sql = engine->toString(args[0]);
247
248     if (r->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
249         V8THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QDeclarativeEngine::tr("Read-only Transaction"));
250     }
251
252     QSqlQuery query(db);
253     bool err = false;
254
255     v8::Handle<v8::Value> result = v8::Undefined();
256
257     if (query.prepare(sql)) {
258         if (args.Length() > 1) {
259             v8::Local<v8::Value> values = args[1];
260             if (values->IsArray()) {
261                 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(values);
262                 uint32_t size = array->Length();
263                 for (uint32_t ii = 0; ii < size; ++ii)
264                     query.bindValue(ii, engine->toVariant(array->Get(ii), -1));
265             } else if (values->IsObject() && !values->ToObject()->GetExternalResource()) {
266                 v8::Local<v8::Object> object = values->ToObject();
267                 v8::Local<v8::Array> names = object->GetPropertyNames();
268                 uint32_t size = names->Length();
269                 for (uint32_t ii = 0; ii < size; ++ii)
270                     query.bindValue(engine->toString(names->Get(ii)),
271                                     engine->toVariant(object->Get(names->Get(ii)), -1));
272             } else {
273                 query.bindValue(0, engine->toVariant(values, -1));
274             }
275         }
276         if (query.exec()) {
277             v8::Handle<v8::Object> rows = databaseData(engine)->rowsConstructor->NewInstance();
278             QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
279             r->type = QV8SqlDatabaseResource::Rows;
280             r->database = db;
281             r->query = query;
282             rows->SetExternalResource(r);
283
284             v8::Local<v8::Object> resultObject = v8::Object::New();
285             result = resultObject;
286             // XXX optimize
287             resultObject->Set(v8::String::New("rowsAffected"), v8::Integer::New(query.numRowsAffected()));
288             resultObject->Set(v8::String::New("insertId"), engine->toString(query.lastInsertId().toString()));
289             resultObject->Set(v8::String::New("rows"), rows);
290         } else {
291             err = true;
292         }
293     } else {
294         err = true;
295     }
296     if (err)
297         V8THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text());
298
299     return result;
300 }
301
302 static v8::Handle<v8::Value> qmlsqldatabase_changeVersion(const v8::Arguments& args)
303 {
304     if (args.Length() < 2)
305         return v8::Undefined();
306
307     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
308     if (!r || r->type != QV8SqlDatabaseResource::Database)
309         V8THROW_REFERENCE("Not a SQLDatabase object");
310
311     QV8Engine *engine = r->engine;
312
313     QSqlDatabase db = r->database;
314     QString from_version = engine->toString(args[0]);
315     QString to_version = engine->toString(args[1]);
316     v8::Handle<v8::Value> callback = args[2];
317
318     if (from_version != r->version)
319         V8THROW_SQL(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(r->version));
320
321     v8::Local<v8::Object> instance = databaseData(engine)->queryConstructor->NewInstance();
322     QV8SqlDatabaseResource *r2 = new QV8SqlDatabaseResource(engine);
323     r2->type = QV8SqlDatabaseResource::Query;
324     r2->database = db;
325     r2->version = r->version;
326     r2->inTransaction = true;
327     instance->SetExternalResource(r2);
328
329     bool ok = true;
330     if (callback->IsFunction()) {
331         ok = false;
332         db.transaction();
333
334         v8::TryCatch tc;
335         v8::Handle<v8::Value> callbackArgs[] = { instance };
336         v8::Handle<v8::Function>::Cast(callback)->Call(engine->global(), 1, callbackArgs);
337
338         if (tc.HasCaught()) {
339             db.rollback();
340             tc.ReThrow();
341             return v8::Handle<v8::Value>();
342         } else if (!db.commit()) {
343             db.rollback();
344             V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("SQL transaction failed"));
345         } else {
346             ok = true;
347         }
348     }
349
350     r2->inTransaction = false;
351
352     if (ok) {
353         r2->version = to_version;
354 #ifndef QT_NO_SETTINGS
355         QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(),engine) + QLatin1String(".ini"), QSettings::IniFormat);
356         ini.setValue(QLatin1String("Version"), to_version);
357 #endif
358     }
359
360     return v8::Undefined();
361 }
362
363 static v8::Handle<v8::Value> qmlsqldatabase_transaction_shared(const v8::Arguments& args, bool readOnly)
364 {
365     QV8SqlDatabaseResource *r = v8_resource_cast<QV8SqlDatabaseResource>(args.This());
366     if (!r || r->type != QV8SqlDatabaseResource::Database)
367         V8THROW_REFERENCE("Not a SQLDatabase object");
368
369     QV8Engine *engine = r->engine;
370
371     if (args.Length() == 0 || !args[0]->IsFunction())
372         V8THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR,QDeclarativeEngine::tr("transaction: missing callback"));
373
374     QSqlDatabase db = r->database;
375     v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(args[0]);
376
377     v8::Local<v8::Object> instance = databaseData(engine)->queryConstructor->NewInstance();
378     QV8SqlDatabaseResource *q = new QV8SqlDatabaseResource(engine);
379     q->type = QV8SqlDatabaseResource::Query;
380     q->database = db;
381     q->readonly = readOnly;
382     q->inTransaction = true;
383     instance->SetExternalResource(q);
384
385     db.transaction();
386     v8::TryCatch tc;
387     v8::Handle<v8::Value> callbackArgs[] = { instance };
388     callback->Call(engine->global(), 1, callbackArgs);
389
390     q->inTransaction = false;
391
392     if (tc.HasCaught()) {
393         db.rollback();
394         tc.ReThrow();
395         return v8::Handle<v8::Value>();
396     } else if (!db.commit()) {
397         db.rollback();
398     }
399
400     return v8::Undefined();
401 }
402
403 static v8::Handle<v8::Value> qmlsqldatabase_transaction(const v8::Arguments& args)
404 {
405     return qmlsqldatabase_transaction_shared(args, false);
406 }
407
408 static v8::Handle<v8::Value> qmlsqldatabase_read_transaction(const v8::Arguments& args)
409 {
410     return qmlsqldatabase_transaction_shared(args, true);
411 }
412
413 QDeclarativeSqlDatabaseData::QDeclarativeSqlDatabaseData(QV8Engine *engine)
414 {
415     {
416     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
417     ft->InstanceTemplate()->SetHasExternalResource(true);
418     ft->PrototypeTemplate()->Set(v8::String::New("transaction"),
419                                  V8FUNCTION(qmlsqldatabase_transaction, engine));
420     ft->PrototypeTemplate()->Set(v8::String::New("readTransaction"),
421                                  V8FUNCTION(qmlsqldatabase_read_transaction, engine));
422     ft->PrototypeTemplate()->SetAccessor(v8::String::New("version"), qmlsqldatabase_version);
423     ft->PrototypeTemplate()->Set(v8::String::New("changeVersion"),
424                                  V8FUNCTION(qmlsqldatabase_changeVersion, engine));
425     constructor = qPersistentNew<v8::Function>(ft->GetFunction());
426     }
427
428     {
429     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
430     ft->InstanceTemplate()->SetHasExternalResource(true);
431     ft->PrototypeTemplate()->Set(v8::String::New("executeSql"),
432                                  V8FUNCTION(qmlsqldatabase_executeSql, engine));
433     queryConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
434     }
435     {
436     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
437     ft->InstanceTemplate()->SetHasExternalResource(true);
438     ft->PrototypeTemplate()->Set(v8::String::New("item"), V8FUNCTION(qmlsqldatabase_rows_item, engine));
439     ft->PrototypeTemplate()->SetAccessor(v8::String::New("length"), qmlsqldatabase_rows_length);
440     ft->InstanceTemplate()->SetAccessor(v8::String::New("forwardOnly"), qmlsqldatabase_rows_forwardOnly,
441                                         qmlsqldatabase_rows_setForwardOnly);
442     ft->InstanceTemplate()->SetIndexedPropertyHandler(qmlsqldatabase_rows_index);
443     rowsConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
444     }
445 }
446
447 /*
448 HTML5 "spec" says "rs.rows[n]", but WebKit only impelments "rs.rows.item(n)". We do both (and property iterator).
449 We add a "forwardOnly" property that stops Qt caching results (code promises to only go forward
450 through the data.
451 */
452
453
454 /*!
455     \qmlclass LocalStorage QQuickLocalStorage
456     \inqmlmodule QtQuick.LocalStorage 2
457     \since QtQuick 2.0
458
459     \brief The LocalStorage module API provides the ability to access local offline storage in an SQL database.
460
461 These databases are user-specific and QML-specific, but accessible to all QML applications.
462 They are stored in the \c Databases subdirectory
463 of QDeclarativeEngine::offlineStoragePath(), currently as SQLite databases.
464
465 Database connections are automatically closed during Javascript garbage collection.
466
467 The API can be used from JavaScript functions in your QML:
468
469 \snippet declarative/sqllocalstorage/hello.qml 0
470
471 The API conforms to the Synchronous API of the HTML5 Web Database API,
472 \link http://www.w3.org/TR/2009/WD-webdatabase-20091029/ W3C Working Draft 29 October 2009\endlink.
473
474 The \l{declarative/sqllocalstorage}{SQL Local Storage example} demonstrates the basics of
475 using the Offline Storage API.
476
477 \section3 Open or create a databaseData
478 \code
479 import QtQuick.LocalStorage 2.0 as LS
480
481 db = LS.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
482 \endcode
483 The above code returns the database identified by \i identifier. If the database does not already exist, it
484 is created, and the function \i callback is called with the database as a parameter. \i description
485 and \i estimated_size are written to the INI file (described below), but are otherwise currently
486 unused.
487
488 May throw exception with code property SQLException.DATABASE_ERR, or SQLException.VERSION_ERR.
489
490 When a database is first created, an INI file is also created specifying its characteristics:
491
492 \table
493 \header \o \bold {Key} \o \bold {Value}
494 \row \o Name \o The name of the database passed to \c openDatabase()
495 \row \o Version \o The version of the database passed to \c openDatabase()
496 \row \o Description \o The description of the database passed to \c openDatabase()
497 \row \o EstimatedSize \o The estimated size (in bytes) of the database passed to \c openDatabase()
498 \row \o Driver \o Currently "QSQLITE"
499 \endtable
500
501 This data can be used by application tools.
502
503 \section3 db.changeVersion(from, to, callback(tx))
504
505 This method allows you to perform a \i{Scheme Upgrade}.
506
507 If the current version of \i db is not \i from, then an exception is thrown.
508
509 Otherwise, a database transaction is created and passed to \i callback. In this function,
510 you can call \i executeSql on \i tx to upgrade the database.
511
512 May throw exception with code property SQLException.DATABASE_ERR or SQLException.UNKNOWN_ERR.
513
514 \section3 db.transaction(callback(tx))
515
516 This method creates a read/write transaction and passed to \i callback. In this function,
517 you can call \i executeSql on \i tx to read and modify the database.
518
519 If the callback throws exceptions, the transaction is rolled back.
520
521 \section3 db.readTransaction(callback(tx))
522
523 This method creates a read-only transaction and passed to \i callback. In this function,
524 you can call \i executeSql on \i tx to read the database (with SELECT statements).
525
526 \section3 results = tx.executeSql(statement, values)
527
528 This method executes a SQL \i statement, binding the list of \i values to SQL positional parameters ("?").
529
530 It returns a results object, with the following properties:
531
532 \table
533 \header \o \bold {Type} \o \bold {Property} \o \bold {Value} \o \bold {Applicability}
534 \row \o int \o rows.length \o The number of rows in the result \o SELECT
535 \row \o var \o rows.item(i) \o Function that returns row \i i of the result \o SELECT
536 \row \o int \o rowsAffected \o The number of rows affected by a modification \o UPDATE, DELETE
537 \row \o string \o insertId \o The id of the row inserted \o INSERT
538 \endtable
539
540 May throw exception with code property SQLException.DATABASE_ERR, SQLException.SYNTAX_ERR, or SQLException.UNKNOWN_ERR.
541  */
542 class QQuickLocalStorage : public QObject
543 {
544     Q_OBJECT
545 public:
546     QQuickLocalStorage(QObject *parent=0) : QObject(parent)
547     {
548     }
549     ~QQuickLocalStorage() {
550     }
551
552    Q_INVOKABLE void openDatabaseSync(QDeclarativeV8Function* args);
553 };
554
555 /*!
556  * \qmlmethod object LocalStorage::openDatabaseSync(string name, string version, string description, int estimated_size, jsobject callback(db))
557  * \brief Open or create a local storage sql database by given parameters.
558  *
559  *  \c name is the database name
560  *  \c version is the database version
561  *  \c description is the database display name
562  *  \c estimated_size is the database's estimated size, in bytes
563  *  \c callback is an optional parameter, which is invoked if the database has not yet been created.
564  * \return the database object
565  */
566 void QQuickLocalStorage::openDatabaseSync(QDeclarativeV8Function *args)
567 {
568 #ifndef QT_NO_SETTINGS
569     QV8Engine *engine = args->engine();
570     qmlsqldatabase_initDatabasesPath(engine);
571
572     QSqlDatabase database;
573
574     QString dbname = engine->toString((*args)[0]);
575     QString dbversion = engine->toString((*args)[1]);
576     QString dbdescription = engine->toString((*args)[2]);
577     int dbestimatedsize = (*args)[3]->Int32Value();
578     v8::Handle<v8::Value> dbcreationCallback = (*args)[4];
579
580     QCryptographicHash md5(QCryptographicHash::Md5);
581     md5.addData(dbname.toUtf8());
582     QString dbid(QLatin1String(md5.result().toHex()));
583
584     QString basename = qmlsqldatabase_databaseFile(dbid, engine);
585     bool created = false;
586     QString version = dbversion;
587
588     {
589         QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat);
590
591         if (QSqlDatabase::connectionNames().contains(dbid)) {
592             database = QSqlDatabase::database(dbid);
593             version = ini.value(QLatin1String("Version")).toString();
594             if (version != dbversion && !dbversion.isEmpty() && !version.isEmpty())
595                 V8THROW_SQL_VOID(SQLEXCEPTION_VERSION_ERR, QDeclarativeEngine::tr("SQL: database version mismatch"));
596         } else {
597             created = !QFile::exists(basename+QLatin1String(".sqlite"));
598             database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid);
599             if (created) {
600                 ini.setValue(QLatin1String("Name"), dbname);
601                 if (dbcreationCallback->IsFunction())
602                     version = QString();
603                 ini.setValue(QLatin1String("Version"), version);
604                 ini.setValue(QLatin1String("Description"), dbdescription);
605                 ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize);
606                 ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE"));
607             } else {
608                 if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) {
609                     // Incompatible
610                     V8THROW_SQL_VOID(SQLEXCEPTION_VERSION_ERR,QDeclarativeEngine::tr("SQL: database version mismatch"));
611                 }
612                 version = ini.value(QLatin1String("Version")).toString();
613             }
614             database.setDatabaseName(basename+QLatin1String(".sqlite"));
615         }
616         if (!database.isOpen())
617             database.open();
618     }
619
620     v8::Local<v8::Object> instance = databaseData(engine)->constructor->NewInstance();
621
622     QV8SqlDatabaseResource *r = new QV8SqlDatabaseResource(engine);
623     r->database = database;
624     r->version = version;
625     instance->SetExternalResource(r);
626
627     if (created && dbcreationCallback->IsFunction()) {
628         v8::TryCatch tc;
629         v8::Handle<v8::Function> callback = v8::Handle<v8::Function>::Cast(dbcreationCallback);
630         v8::Handle<v8::Value> args[] = { instance };
631         callback->Call(engine->global(), 1, args);
632         if (tc.HasCaught()) {
633             tc.ReThrow();
634             return;
635         }
636     }
637
638     args->returnValue(instance);
639 #endif // QT_NO_SETTINGS
640 }
641
642 static QObject *module_api_factory(QDeclarativeEngine *engine, QJSEngine *scriptEngine)
643 {
644    Q_UNUSED(engine)
645    Q_UNUSED(scriptEngine)
646    QQuickLocalStorage *api = new QQuickLocalStorage();
647
648    return api;
649 }
650
651 class QDeclarativeLocalStoragePlugin : public QDeclarativeExtensionPlugin
652 {
653     Q_OBJECT
654 public:
655     QDeclarativeLocalStoragePlugin()
656     {
657     }
658
659     void registerTypes(const char *uri)
660     {
661         Q_ASSERT(QLatin1String(uri) == "QtQuick.LocalStorage");
662         qmlRegisterModuleApi(uri, 2, 0, module_api_factory);
663     }
664 };
665
666 #include "plugin.moc"
667
668 Q_EXPORT_PLUGIN2(plugin, QDeclarativeLocalStoragePlugin);