⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 databasetracker.cpp

📁 linux下开源浏览器WebKit的源码,市面上的很多商用浏览器都是移植自WebKit
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1.  Redistributions of source code must retain the above copyright *     notice, this list of conditions and the following disclaimer. * 2.  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. * 3.  Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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. */#include "config.h"#include "DatabaseTracker.h"#include "ChromeClient.h"#include "Database.h"#include "DatabaseTrackerClient.h"#include "Document.h"#include "FileSystem.h"#include "Logging.h"#include "OriginQuotaManager.h"#include "Page.h"#include "SecurityOrigin.h"#include "SecurityOriginHash.h"#include "SQLiteStatement.h"#include <wtf/MainThread.h>#include <wtf/StdLibExtras.h>using namespace std;namespace WebCore {OriginQuotaManager& DatabaseTracker::originQuotaManager(){    populateOrigins();    ASSERT(m_quotaManager);    return *m_quotaManager;}DatabaseTracker& DatabaseTracker::tracker(){    DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ());    return tracker;}DatabaseTracker::DatabaseTracker()    : m_client(0)    , m_proposedDatabase(0)#ifndef NDEBUG    , m_thread(currentThread())#endif{}void DatabaseTracker::setDatabaseDirectoryPath(const String& path){    ASSERT(currentThread() == m_thread);    ASSERT(!m_database.isOpen());    m_databaseDirectoryPath = path;}const String& DatabaseTracker::databaseDirectoryPath() const{    ASSERT(currentThread() == m_thread);    return m_databaseDirectoryPath;}String DatabaseTracker::trackerDatabasePath() const{    ASSERT(currentThread() == m_thread);    if (m_databaseDirectoryPath.isEmpty())        return String();    return pathByAppendingComponent(m_databaseDirectoryPath, "Databases.db");}void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist){    ASSERT(currentThread() == m_thread);    if (m_database.isOpen())        return;    String databasePath = trackerDatabasePath();    if (databasePath.isEmpty())        return;    if (!createIfDoesNotExist && !fileExists(databasePath))        return;    makeAllDirectories(m_databaseDirectoryPath);    if (!m_database.open(databasePath)) {        // FIXME: What do do here?        return;    }    if (!m_database.tableExists("Origins")) {        if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) {            // FIXME: and here        }    }    if (!m_database.tableExists("Databases")) {        if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) {            // FIXME: and here        }    }}bool DatabaseTracker::canEstablishDatabase(Document* document, const String& name, const String& displayName, unsigned long estimatedSize){    ASSERT(currentThread() == m_thread);    // Populate the origins before we establish a database; this guarantees that quotaForOrigin    // can run on the database thread later.    populateOrigins();    SecurityOrigin* origin = document->securityOrigin();    // Since we're imminently opening a database within this Document's origin, make sure this origin is being tracked by the QuotaTracker    // by fetching it's current usage now    unsigned long long usage = usageForOrigin(origin);        // If a database already exists, ignore the passed-in estimated size and say it's OK.    if (hasEntryForDatabase(origin, name))        return true;    // If the database will fit, allow its creation.    unsigned long long requirement = usage + max(1UL, estimatedSize);    if (requirement < usage)        return false; // If the estimated size is so big it causes an overflow, don't allow creation.    if (requirement <= quotaForOrigin(origin))        return true;    // Give the chrome client a chance to increase the quota.    // Temporarily make the details of the proposed database available, so the client can get at them.    Page* page = document->page();    if (!page)        return false;    pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0));    m_proposedDatabase = &details;    page->chrome()->client()->exceededDatabaseQuota(document->frame(), name);    m_proposedDatabase = 0;    // If the database will fit now, allow its creation.    return requirement <= quotaForOrigin(origin);}bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin){    ASSERT(currentThread() == m_thread);    populateOrigins();    MutexLocker lockQuotaMap(m_quotaMapGuard);    return m_quotaMap->contains(origin);}bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier){    ASSERT(currentThread() == m_thread);    openTrackerDatabase(false);    if (!m_database.isOpen())        return false;    SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;");    if (statement.prepare() != SQLResultOk)        return false;    statement.bindText(1, origin->databaseIdentifier());    statement.bindText(2, databaseIdentifier);    return statement.step() == SQLResultRow;}String DatabaseTracker::originPath(SecurityOrigin* origin) const{    ASSERT(currentThread() == m_thread);    if (m_databaseDirectoryPath.isEmpty())        return String();    return pathByAppendingComponent(m_databaseDirectoryPath, origin->databaseIdentifier());}String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists){    ASSERT(currentThread() == m_thread);    if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)        return String();    String originIdentifier = origin->databaseIdentifier();    String originPath = this->originPath(origin);        // Make sure the path for this SecurityOrigin exists    if (createIfNotExists && !makeAllDirectories(originPath))        return String();        // See if we have a path for this database yet    openTrackerDatabase(false);    if (!m_database.isOpen())        return String();    SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;");    if (statement.prepare() != SQLResultOk)        return String();    statement.bindText(1, originIdentifier);    statement.bindText(2, name);    int result = statement.step();    if (result == SQLResultRow)        return pathByAppendingComponent(originPath, statement.getColumnText(0));    if (!createIfNotExists)        return String();            if (result != SQLResultDone) {        LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data());        return String();    }    statement.finalize();        SQLiteStatement sequenceStatement(m_database, "SELECT seq FROM sqlite_sequence WHERE name='Databases';");    // FIXME: More informative error handling here, even though these steps should never fail    if (sequenceStatement.prepare() != SQLResultOk)        return String();    result = sequenceStatement.step();    // This has a range of 2^63 and starts at 0 for every time a user resets Safari -    // I can't imagine it'd over overflow    int64_t seq = 0;    if (result == SQLResultRow) {        seq = sequenceStatement.getColumnInt64(0);    } else if (result != SQLResultDone)        return String();    sequenceStatement.finalize();    String filename;    do {        ++seq;        filename = pathByAppendingComponent(originPath, String::format("%016llx.db", seq));    } while (fileExists(filename));    if (!addDatabase(origin, name, String::format("%016llx.db", seq)))        return String();    // If this origin's quota is being tracked (open handle to a database in this origin), add this new database    // to the quota manager now    {        Locker<OriginQuotaManager> locker(originQuotaManager());        if (originQuotaManager().tracksOrigin(origin))            originQuotaManager().addDatabase(origin, name, filename);    }        return filename;}void DatabaseTracker::populateOrigins(){    if (m_quotaMap)        return;    ASSERT(currentThread() == m_thread);    m_quotaMap.set(new QuotaMap);    m_quotaManager.set(new OriginQuotaManager);    openTrackerDatabase(false);    if (!m_database.isOpen())        return;    SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins");    if (statement.prepare() != SQLResultOk)        return;    int result;    while ((result = statement.step()) == SQLResultRow) {        RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0));        m_quotaMap->set(origin.get(), statement.getColumnInt64(1));    }    if (result != SQLResultDone)        LOG_ERROR("Failed to read in all origins from the database");}void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result){    ASSERT(currentThread() == m_thread);    populateOrigins();    MutexLocker lockQuotaMap(m_quotaMapGuard);    copyKeysToVector(*m_quotaMap, result);}bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector){    ASSERT(currentThread() == m_thread);    openTrackerDatabase(false);    if (!m_database.isOpen())        return false;    SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;");    if (statement.prepare() != SQLResultOk)        return false;    statement.bindText(1, origin->databaseIdentifier());    int result;    while ((result = statement.step()) == SQLResultRow)        resultVector.append(statement.getColumnText(0));    if (result != SQLResultDone) {        LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data());        return false;    }    return true;}DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin){    ASSERT(currentThread() == m_thread);    if (m_proposedDatabase && m_proposedDatabase->first == origin && m_proposedDatabase->second.name() == name)        return m_proposedDatabase->second;    String originIdentifier = origin->databaseIdentifier();    openTrackerDatabase(false);    if (!m_database.isOpen())        return DatabaseDetails();    SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?");    if (statement.prepare() != SQLResultOk)        return DatabaseDetails();       statement.bindText(1, originIdentifier);    statement.bindText(2, name);        int result = statement.step();    if (result == SQLResultDone)        return DatabaseDetails();        if (result != SQLResultRow) {        LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data());        return DatabaseDetails();    }        return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin));}void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize){    ASSERT(currentThread() == m_thread);    String originIdentifier = origin->databaseIdentifier();    int64_t guid = 0;        openTrackerDatabase(true);    if (!m_database.isOpen())        return;    SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?");    if (statement.prepare() != SQLResultOk)        return;            statement.bindText(1, originIdentifier);    statement.bindText(2, name);        int result = statement.step();    if (result == SQLResultRow)        guid = statement.getColumnInt64(0);    statement.finalize();    if (guid == 0) {        if (result != SQLResultDone)            LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data());        else {            // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker            // But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case            // So we'll print an error instead            LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker",                       name.ascii().data(), originIdentifier.ascii().data());        }        return;    }        SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?");    if (updateStatement.prepare() != SQLResultOk)        return;        updateStatement.bindText(1, displayName);    updateStatement.bindInt64(2, estimatedSize);    updateStatement.bindInt64(3, guid);        if (updateStatement.step() != SQLResultDone) {        LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data());        return;      }        if (m_client)        m_client->dispatchDidModifyDatabase(origin, name);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -