~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/platform/blackberry/CookieDatabaseBackingStore/CookieDatabaseBackingStore.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2009 Julien Chaffraix <jchaffraix@pleyo.com>
 
3
 * Copyright (C) 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 * 1. Redistributions of source code must retain the above copyright
 
9
 *    notice, this list of conditions and the following disclaimer.
 
10
 * 2. Redistributions in binary form must reproduce the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer in the
 
12
 *    documentation and/or other materials provided with the distribution.
 
13
 *
 
14
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 
15
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
16
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
17
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 
18
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
19
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
20
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
21
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 
22
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 
23
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
24
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
25
*/
 
26
#define ENABLE_COOKIE_DEBUG 0
 
27
 
 
28
#include "config.h"
 
29
#include "CookieDatabaseBackingStore.h"
 
30
 
 
31
#include "CookieManager.h"
 
32
#include "Logging.h"
 
33
#include "ParsedCookie.h"
 
34
#include "SQLiteStatement.h"
 
35
#include "SQLiteTransaction.h"
 
36
#include <wtf/text/StringBuilder.h>
 
37
#include <wtf/text/WTFString.h>
 
38
 
 
39
#if ENABLE_COOKIE_DEBUG
 
40
#include <BlackBerryPlatformLog.h>
 
41
#define CookieLog(format, ...) BlackBerry::Platform::logAlways(BlackBerry::Platform::LogLevelInfo, format, ## __VA_ARGS__)
 
42
#else
 
43
#define CookieLog(format, ...)
 
44
#endif
 
45
 
 
46
#include <BlackBerryPlatformExecutableMessage.h>
 
47
#include <BlackBerryPlatformNavigatorHandler.h>
 
48
 
 
49
using BlackBerry::Platform::MessageClient;
 
50
using BlackBerry::Platform::TypedReplyBuffer;
 
51
using BlackBerry::Platform::createMethodCallMessage;
 
52
 
 
53
static const double s_databaseTimerInterval = 2;
 
54
 
 
55
namespace WebCore {
 
56
 
 
57
CookieDatabaseBackingStore::CookieDatabaseBackingStore()
 
58
    : MessageClient(MessageClient::ReplyFeature | MessageClient::SyncFeature)
 
59
    , m_tableName("cookies") // This is chosen to match Mozilla's table name.
 
60
    , m_dbTimer(this, &CookieDatabaseBackingStore::sendChangesToDatabaseTimerFired)
 
61
    , m_insertStatement(0)
 
62
    , m_updateStatement(0)
 
63
    , m_deleteStatement(0)
 
64
{
 
65
    m_dbTimerClient = new BlackBerry::Platform::GenericTimerClient(this);
 
66
    m_dbTimer.setClient(m_dbTimerClient);
 
67
 
 
68
    createThread("cookie_database", pthread_attr_default);
 
69
}
 
70
 
 
71
CookieDatabaseBackingStore::~CookieDatabaseBackingStore()
 
72
{
 
73
    deleteGuardedObject(m_dbTimerClient);
 
74
    m_dbTimerClient = 0;
 
75
    // FIXME: This object will never be deleted due to the set up of CookieManager (it's a singleton)
 
76
    CookieLog("CookieBackingStore - Destructing");
 
77
#ifndef NDEBUG
 
78
    {
 
79
        MutexLocker lock(m_mutex);
 
80
        ASSERT(m_changedCookies.isEmpty());
 
81
    }
 
82
#endif
 
83
}
 
84
 
 
85
void CookieDatabaseBackingStore::onThreadFinished()
 
86
{
 
87
    CookieLog("CookieManager - flushing cookies to backingStore...");
 
88
    // This is called from shutdown, so we need to be sure the OS doesn't kill us before the db write finishes.
 
89
    // Once should be enough since this extends terimination by 2 seconds.
 
90
    BlackBerry::Platform::NavigatorHandler::sendExtendTerminate();
 
91
    sendChangesToDatabaseSynchronously();
 
92
    CookieLog("CookieManager - finished flushing cookies to backingStore.");
 
93
 
 
94
    MessageClient::onThreadFinished();
 
95
}
 
96
 
 
97
void CookieDatabaseBackingStore::open(const String& cookieJar)
 
98
{
 
99
    dispatchMessage(createMethodCallMessage(&CookieDatabaseBackingStore::invokeOpen, this, cookieJar));
 
100
}
 
101
 
 
102
void CookieDatabaseBackingStore::invokeOpen(const String& cookieJar)
 
103
{
 
104
    ASSERT(isCurrentThread());
 
105
    if (m_db.isOpen())
 
106
        close();
 
107
 
 
108
    if (!m_db.open(cookieJar)) {
 
109
        LOG_ERROR("Could not open the cookie database. No cookie will be stored!");
 
110
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
111
        return;
 
112
    }
 
113
 
 
114
    m_db.executeCommand("PRAGMA locking_mode=EXCLUSIVE;");
 
115
    m_db.executeCommand("PRAGMA journal_mode=WAL;");
 
116
 
 
117
    const String primaryKeyFields("PRIMARY KEY (protocol, host, path, name)");
 
118
    const String databaseFields("name TEXT, value TEXT, host TEXT, path TEXT, expiry DOUBLE, lastAccessed DOUBLE, isSecure INTEGER, isHttpOnly INTEGER, creationTime DOUBLE, protocol TEXT");
 
119
 
 
120
    StringBuilder createTableQuery;
 
121
    createTableQuery.append("CREATE TABLE IF NOT EXISTS ");
 
122
    createTableQuery.append(m_tableName);
 
123
    // This table schema is compliant with Mozilla's.
 
124
    createTableQuery.append(" (" + databaseFields + ", " + primaryKeyFields+");");
 
125
 
 
126
    m_db.setBusyTimeout(1000);
 
127
 
 
128
    if (!m_db.executeCommand(createTableQuery.toString())) {
 
129
        LOG_ERROR("Could not create the table to store the cookies into. No cookie will be stored!");
 
130
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
131
        close();
 
132
        return;
 
133
    }
 
134
 
 
135
    StringBuilder insertQuery;
 
136
    insertQuery.append("INSERT OR REPLACE INTO ");
 
137
    insertQuery.append(m_tableName);
 
138
    insertQuery.append(" (name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly, creationTime, protocol) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10);");
 
139
 
 
140
    m_insertStatement = new SQLiteStatement(m_db, insertQuery.toString());
 
141
    if (m_insertStatement->prepare()) {
 
142
        LOG_ERROR("Cannot save cookies");
 
143
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
144
    }
 
145
 
 
146
    StringBuilder updateQuery;
 
147
    updateQuery.append("UPDATE ");
 
148
    updateQuery.append(m_tableName);
 
149
    // The where statement is chosen to match CookieMap key.
 
150
    updateQuery.append(" SET name = ?1, value = ?2, host = ?3, path = ?4, expiry = ?5, lastAccessed = ?6, isSecure = ?7, isHttpOnly = ?8, creationTime = ?9, protocol = ?10 where name = ?1 and host = ?3 and path = ?4;");
 
151
    m_updateStatement = new SQLiteStatement(m_db, updateQuery.toString());
 
152
 
 
153
    if (m_updateStatement->prepare()) {
 
154
        LOG_ERROR("Cannot update cookies");
 
155
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
156
    }
 
157
 
 
158
    StringBuilder deleteQuery;
 
159
    deleteQuery.append("DELETE FROM ");
 
160
    deleteQuery.append(m_tableName);
 
161
    // The where statement is chosen to match CookieMap key.
 
162
    deleteQuery.append(" WHERE name=?1 and host=?2 and path=?3 and protocol=?4;");
 
163
    m_deleteStatement = new SQLiteStatement(m_db, deleteQuery.toString());
 
164
 
 
165
    if (m_deleteStatement->prepare()) {
 
166
        LOG_ERROR("Cannot delete cookies");
 
167
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
168
    }
 
169
 
 
170
}
 
171
 
 
172
void CookieDatabaseBackingStore::close()
 
173
{
 
174
    ASSERT(isCurrentThread());
 
175
    CookieLog("CookieBackingStore - Closing");
 
176
 
 
177
    size_t changedCookiesSize;
 
178
    {
 
179
        MutexLocker lock(m_mutex);
 
180
        if (m_dbTimer.started())
 
181
            m_dbTimer.stop();
 
182
        changedCookiesSize = m_changedCookies.size();
 
183
    }
 
184
 
 
185
    if (changedCookiesSize > 0)
 
186
        invokeSendChangesToDatabase();
 
187
 
 
188
    delete m_insertStatement;
 
189
    m_insertStatement = 0;
 
190
    delete m_updateStatement;
 
191
    m_updateStatement = 0;
 
192
    delete m_deleteStatement;
 
193
    m_deleteStatement = 0;
 
194
 
 
195
    if (m_db.isOpen())
 
196
        m_db.close();
 
197
}
 
198
 
 
199
void CookieDatabaseBackingStore::insert(const ParsedCookie* cookie)
 
200
{
 
201
    CookieLog("CookieBackingStore - adding inserting cookie %s to queue.", cookie->toString().utf8().data());
 
202
    addToChangeQueue(cookie, Insert);
 
203
}
 
204
 
 
205
void CookieDatabaseBackingStore::update(const ParsedCookie* cookie)
 
206
{
 
207
    CookieLog("CookieBackingStore - adding updating cookie %s to queue.", cookie->toString().utf8().data());
 
208
    addToChangeQueue(cookie, Update);
 
209
}
 
210
 
 
211
void CookieDatabaseBackingStore::remove(const ParsedCookie* cookie)
 
212
{
 
213
    CookieLog("CookieBackingStore - adding deleting cookie %s to queue.", cookie->toString().utf8().data());
 
214
    addToChangeQueue(cookie, Delete);
 
215
}
 
216
 
 
217
void CookieDatabaseBackingStore::removeAll()
 
218
{
 
219
    dispatchMessage(createMethodCallMessage(&CookieDatabaseBackingStore::invokeRemoveAll, this));
 
220
}
 
221
 
 
222
void CookieDatabaseBackingStore::invokeRemoveAll()
 
223
{
 
224
    ASSERT(isCurrentThread());
 
225
    if (!m_db.isOpen())
 
226
        return;
 
227
 
 
228
    CookieLog("CookieBackingStore - remove All cookies from backingstore");
 
229
 
 
230
    {
 
231
        MutexLocker lock(m_mutex);
 
232
        m_changedCookies.clear();
 
233
    }
 
234
 
 
235
    StringBuilder deleteQuery;
 
236
    deleteQuery.append("DELETE FROM ");
 
237
    deleteQuery.append(m_tableName);
 
238
    deleteQuery.append(";");
 
239
 
 
240
    SQLiteStatement deleteStatement(m_db, deleteQuery.toString());
 
241
    if (deleteStatement.prepare()) {
 
242
        LOG_ERROR("Could not prepare DELETE * statement");
 
243
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
244
        return;
 
245
    }
 
246
 
 
247
    if (!deleteStatement.executeCommand()) {
 
248
        LOG_ERROR("Cannot delete cookie from database");
 
249
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
250
        return;
 
251
    }
 
252
}
 
253
 
 
254
void CookieDatabaseBackingStore::getCookiesFromDatabase(Vector<ParsedCookie*>& stackOfCookies, unsigned int limit)
 
255
{
 
256
    // It is not a huge performance hit to wait on the reply here because this is only done once during setup and when turning off private mode.
 
257
    TypedReplyBuffer< Vector<ParsedCookie*>* > replyBuffer(0);
 
258
    dispatchMessage(createMethodCallMessageWithReturn(&CookieDatabaseBackingStore::invokeGetCookiesWithLimit, &replyBuffer, this, limit));
 
259
    Vector<ParsedCookie*>* cookies = replyBuffer.pointer();
 
260
    if (cookies)
 
261
        stackOfCookies.swap(*cookies);
 
262
    delete cookies;
 
263
}
 
264
 
 
265
Vector<ParsedCookie*>* CookieDatabaseBackingStore::invokeGetCookiesWithLimit(unsigned int limit)
 
266
{
 
267
    ASSERT(isCurrentThread());
 
268
 
 
269
    // Check that the table exists to avoid doing an unnecessary request.
 
270
    if (!m_db.isOpen())
 
271
        return 0;
 
272
 
 
273
    StringBuilder selectQuery;
 
274
    selectQuery.append("SELECT name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly, creationTime, protocol FROM ");
 
275
    selectQuery.append(m_tableName);
 
276
    if (limit > 0) {
 
277
        selectQuery.append(" ORDER BY lastAccessed ASC");
 
278
        selectQuery.append(" LIMIT " + String::number(limit));
 
279
    }
 
280
    selectQuery.append(";");
 
281
 
 
282
    CookieLog("CookieBackingStore - invokeGetAllCookies with select query %s", selectQuery.toString().utf8().data());
 
283
 
 
284
    SQLiteStatement selectStatement(m_db, selectQuery.toString());
 
285
 
 
286
    if (selectStatement.prepare()) {
 
287
        LOG_ERROR("Cannot retrieved cookies from the database");
 
288
        LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
289
        return 0;
 
290
    }
 
291
 
 
292
    Vector<ParsedCookie*>* cookies = new Vector<ParsedCookie*>;
 
293
    while (selectStatement.step() == SQLResultRow) {
 
294
        // There is a row to fetch
 
295
 
 
296
        String name = selectStatement.getColumnText(0);
 
297
        String value = selectStatement.getColumnText(1);
 
298
        String domain = selectStatement.getColumnText(2);
 
299
        String path = selectStatement.getColumnText(3);
 
300
        double expiry = selectStatement.getColumnDouble(4);
 
301
        double lastAccessed = selectStatement.getColumnDouble(5);
 
302
        bool isSecure = selectStatement.getColumnInt(6);
 
303
        bool isHttpOnly = selectStatement.getColumnInt(7);
 
304
        double creationTime = selectStatement.getColumnDouble(8);
 
305
        String protocol = selectStatement.getColumnText(9);
 
306
 
 
307
        cookies->append(new ParsedCookie(name, value, domain, protocol, path, expiry, lastAccessed, creationTime, isSecure, isHttpOnly));
 
308
    }
 
309
 
 
310
    return cookies;
 
311
}
 
312
 
 
313
void CookieDatabaseBackingStore::sendChangesToDatabaseSynchronously()
 
314
{
 
315
    CookieLog("CookieBackingStore - sending to database immediately");
 
316
    {
 
317
        MutexLocker lock(m_mutex);
 
318
        if (m_dbTimer.started())
 
319
            m_dbTimer.stop();
 
320
    }
 
321
    if (isCurrentThread())
 
322
        invokeSendChangesToDatabase();
 
323
    else
 
324
        dispatchSyncMessage(createMethodCallMessage(&CookieDatabaseBackingStore::invokeSendChangesToDatabase, this));
 
325
}
 
326
 
 
327
void CookieDatabaseBackingStore::sendChangesToDatabase(int nextInterval)
 
328
{
 
329
    MutexLocker lock(m_mutex);
 
330
    if (!m_dbTimer.started()) {
 
331
        CookieLog("CookieBackingStore - Starting one shot send to database");
 
332
        m_dbTimer.start(nextInterval);
 
333
    } else {
 
334
#ifndef NDEBUG
 
335
        CookieLog("CookieBackingStore - Timer already running, skipping this request");
 
336
#endif
 
337
    }
 
338
}
 
339
 
 
340
void CookieDatabaseBackingStore::sendChangesToDatabaseTimerFired()
 
341
{
 
342
    dispatchMessage(createMethodCallMessage(&CookieDatabaseBackingStore::invokeSendChangesToDatabase, this));
 
343
}
 
344
 
 
345
void CookieDatabaseBackingStore::invokeSendChangesToDatabase()
 
346
{
 
347
    ASSERT(isCurrentThread());
 
348
 
 
349
    if (!m_db.isOpen()) {
 
350
        LOG_ERROR("Timer Fired, but database is closed.");
 
351
        return;
 
352
    }
 
353
 
 
354
    Vector<CookieAction> changedCookies;
 
355
    {
 
356
        MutexLocker lock(m_mutex);
 
357
        changedCookies.swap(m_changedCookies);
 
358
        ASSERT(m_changedCookies.isEmpty());
 
359
    }
 
360
 
 
361
    if (changedCookies.isEmpty()) {
 
362
        CookieLog("CookieBackingStore - Timer fired, but no cookies in changelist");
 
363
        return;
 
364
    }
 
365
    CookieLog("CookieBackingStore - Timer fired, sending changes to database. We have %d changes", changedCookies.size());
 
366
    SQLiteTransaction transaction(m_db, false);
 
367
    transaction.begin();
 
368
 
 
369
    // Iterate through every element in the change list to make calls
 
370
    // If error occurs, ignore it and continue to the next statement
 
371
    size_t sizeOfChange = changedCookies.size();
 
372
    for (size_t i = 0; i < sizeOfChange; i++) {
 
373
        SQLiteStatement* m_statement;
 
374
        const ParsedCookie cookie = changedCookies[i].first;
 
375
        UpdateParameter action = changedCookies[i].second;
 
376
 
 
377
        if (action == Delete) {
 
378
            m_statement = m_deleteStatement;
 
379
            CookieLog("CookieBackingStore - deleting cookie %s.", cookie.toString().utf8().data());
 
380
 
 
381
            // Binds all the values
 
382
            if (m_statement->bindText(1, cookie.name()) || m_statement->bindText(2, cookie.domain())
 
383
                || m_statement->bindText(3, cookie.path()) || m_statement->bindText(4, cookie.protocol())) {
 
384
                LOG_ERROR("Cannot bind cookie data to delete");
 
385
                LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
386
                ASSERT_NOT_REACHED();
 
387
                continue;
 
388
            }
 
389
        } else {
 
390
            if (action == Update) {
 
391
                CookieLog("CookieBackingStore - updating cookie %s.", cookie.toString().utf8().data());
 
392
                m_statement = m_updateStatement;
 
393
            } else {
 
394
                CookieLog("CookieBackingStore - inserting cookie %s.", cookie.toString().utf8().data());
 
395
                m_statement = m_insertStatement;
 
396
            }
 
397
 
 
398
            // Binds all the values
 
399
            if (m_statement->bindText(1, cookie.name()) || m_statement->bindText(2, cookie.value())
 
400
                || m_statement->bindText(3, cookie.domain()) || m_statement->bindText(4, cookie.path())
 
401
                || m_statement->bindDouble(5, cookie.expiry()) || m_statement->bindDouble(6, cookie.lastAccessed())
 
402
                || m_statement->bindInt64(7, cookie.isSecure()) || m_statement->bindInt64(8, cookie.isHttpOnly())
 
403
                || m_statement->bindDouble(9, cookie.creationTime()) || m_statement->bindText(10, cookie.protocol())) {
 
404
                LOG_ERROR("Cannot bind cookie data to save");
 
405
                LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
406
                ASSERT_NOT_REACHED();
 
407
                continue;
 
408
            }
 
409
        }
 
410
 
 
411
        int rc = m_statement->step();
 
412
        m_statement->reset();
 
413
        if (rc != SQLResultOk && rc != SQLResultDone) {
 
414
            LOG_ERROR("Cannot make call to the database");
 
415
            LOG_ERROR("SQLite Error Message: %s", m_db.lastErrorMsg());
 
416
            ASSERT_NOT_REACHED();
 
417
            continue;
 
418
        }
 
419
    }
 
420
    transaction.commit();
 
421
    CookieLog("CookieBackingStore - transaction complete");
 
422
}
 
423
 
 
424
void CookieDatabaseBackingStore::addToChangeQueue(const ParsedCookie* changedCookie, UpdateParameter actionParam)
 
425
{
 
426
    ASSERT(!changedCookie->isSession());
 
427
    ParsedCookie cookieCopy(changedCookie);
 
428
    CookieAction action(cookieCopy, actionParam);
 
429
    {
 
430
        MutexLocker lock(m_mutex);
 
431
        m_changedCookies.append(action);
 
432
        CookieLog("CookieBackingStore - m_changedcookies has %d.", m_changedCookies.size());
 
433
    }
 
434
    sendChangesToDatabase(s_databaseTimerInterval);
 
435
}
 
436
 
 
437
} // namespace WebCore