blob: fd07240e06bed10c1ffeae2c2e02d4a6398f4c6d [file] [log] [blame]
/*
* Copyright (C) 2013 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "StorageManager.h"
#include "SecurityOriginData.h"
#include "StorageAreaProxyMessages.h"
#include "StorageManagerMessages.h"
#include "WebProcessProxy.h"
#include "WorkQueue.h"
#include <WebCore/SecurityOriginHash.h>
#include <WebCore/StorageMap.h>
using namespace WebCore;
namespace WebKit {
class StorageManager::StorageArea : public ThreadSafeRefCounted<StorageManager::StorageArea> {
public:
static PassRefPtr<StorageArea> create(unsigned quotaInBytes);
~StorageArea();
void addListener(CoreIPC::Connection*, uint64_t storageAreaID);
void removeListener(CoreIPC::Connection*, uint64_t storageAreaID);
void setItem(CoreIPC::Connection*, uint64_t storageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException);
private:
explicit StorageArea(unsigned quotaInBytes);
void dispatchEvents(CoreIPC::Connection*, uint64_t storageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const;
RefPtr<StorageMap> m_storageMap;
HashSet<std::pair<RefPtr<CoreIPC::Connection>, uint64_t> > m_eventListeners;
};
PassRefPtr<StorageManager::StorageArea> StorageManager::StorageArea::create(unsigned quotaInBytes)
{
return adoptRef(new StorageArea(quotaInBytes));
}
StorageManager::StorageArea::StorageArea(unsigned quotaInBytes)
: m_storageMap(StorageMap::create(quotaInBytes))
{
}
StorageManager::StorageArea::~StorageArea()
{
ASSERT(m_eventListeners.isEmpty());
}
void StorageManager::StorageArea::addListener(CoreIPC::Connection* connection, uint64_t storageAreaID)
{
ASSERT(!m_eventListeners.contains(std::make_pair(connection, storageAreaID)));
m_eventListeners.add(std::make_pair(connection, storageAreaID));
}
void StorageManager::StorageArea::removeListener(CoreIPC::Connection* connection, uint64_t storageAreaID)
{
ASSERT(m_eventListeners.contains(std::make_pair(connection, storageAreaID)));
m_eventListeners.remove(std::make_pair(connection, storageAreaID));
}
void StorageManager::StorageArea::setItem(CoreIPC::Connection* connection, uint64_t storageAreaID, const String& key, const String& value, const String& urlString, bool& quotaException)
{
ASSERT(m_storageMap->hasOneRef());
String oldValue;
m_storageMap->setItem(key, value, oldValue, quotaException);
if (!quotaException)
dispatchEvents(connection, storageAreaID, key, oldValue, value, urlString);
}
void StorageManager::StorageArea::dispatchEvents(CoreIPC::Connection* connection, uint64_t storageAreaID, const String& key, const String& oldValue, const String& newValue, const String& urlString) const
{
for (HashSet<std::pair<RefPtr<CoreIPC::Connection>, uint64_t> >::const_iterator it = m_eventListeners.begin(), end = m_eventListeners.end(); it != end; ++it) {
if (it->first == connection && it->second == storageAreaID) {
// We don't want to dispatch events to the storage area that originated the event.
continue;
}
it->first->send(Messages::StorageAreaProxy::DispatchStorageEvent(key, oldValue, newValue, urlString), it->second);
}
}
class StorageManager::SessionStorageNamespace : public ThreadSafeRefCounted<SessionStorageNamespace> {
public:
static PassRefPtr<SessionStorageNamespace> create(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
~SessionStorageNamespace();
bool isEmpty() const { return m_storageAreaMap.isEmpty(); }
CoreIPC::Connection* allowedConnection() const { return m_allowedConnection.get(); }
void setAllowedConnection(CoreIPC::Connection*);
PassRefPtr<StorageArea> getOrCreateStorageArea(PassRefPtr<SecurityOrigin>);
void cloneTo(SessionStorageNamespace& newSessionStorageNamespace);
private:
SessionStorageNamespace(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes);
RefPtr<CoreIPC::Connection> m_allowedConnection;
unsigned m_quotaInBytes;
HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageArea> > m_storageAreaMap;
};
PassRefPtr<StorageManager::SessionStorageNamespace> StorageManager::SessionStorageNamespace::create(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
{
return adoptRef(new SessionStorageNamespace(allowedConnection, quotaInBytes));
}
StorageManager::SessionStorageNamespace::SessionStorageNamespace(CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
: m_allowedConnection(allowedConnection)
, m_quotaInBytes(quotaInBytes)
{
}
StorageManager::SessionStorageNamespace::~SessionStorageNamespace()
{
}
void StorageManager::SessionStorageNamespace::setAllowedConnection(CoreIPC::Connection* allowedConnection)
{
ASSERT(!allowedConnection || !m_allowedConnection);
m_allowedConnection = allowedConnection;
}
PassRefPtr<StorageManager::StorageArea> StorageManager::SessionStorageNamespace::getOrCreateStorageArea(PassRefPtr<SecurityOrigin> securityOrigin)
{
HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageArea> >::AddResult result = m_storageAreaMap.add(securityOrigin, 0);
if (result.isNewEntry)
result.iterator->value = StorageArea::create(m_quotaInBytes);
return result.iterator->value;
}
void StorageManager::SessionStorageNamespace::cloneTo(SessionStorageNamespace& newSessionStorageNamespace)
{
ASSERT(newSessionStorageNamespace.isEmpty());
// FIXME: Implement.
}
PassRefPtr<StorageManager> StorageManager::create()
{
return adoptRef(new StorageManager);
}
StorageManager::StorageManager()
: m_queue(WorkQueue::create("com.apple.WebKit.StorageManager"))
{
}
StorageManager::~StorageManager()
{
}
void StorageManager::createSessionStorageNamespace(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
{
m_queue->dispatch(bind(&StorageManager::createSessionStorageNamespaceInternal, this, storageNamespaceID, RefPtr<CoreIPC::Connection>(allowedConnection), quotaInBytes));
}
void StorageManager::destroySessionStorageNamespace(uint64_t storageNamespaceID)
{
m_queue->dispatch(bind(&StorageManager::destroySessionStorageNamespaceInternal, this, storageNamespaceID));
}
void StorageManager::setAllowedSessionStorageNamespaceConnection(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection)
{
m_queue->dispatch(bind(&StorageManager::setAllowedSessionStorageNamespaceConnectionInternal, this, storageNamespaceID, RefPtr<CoreIPC::Connection>(allowedConnection)));
}
void StorageManager::cloneSessionStorageNamespace(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
{
m_queue->dispatch(bind(&StorageManager::cloneSessionStorageNamespaceInternal, this, storageNamespaceID, newStorageNamespaceID));
}
void StorageManager::processWillOpenConnection(WebProcessProxy* webProcessProxy)
{
webProcessProxy->connection()->addWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName(), m_queue.get(), this);
}
void StorageManager::processWillCloseConnection(WebProcessProxy* webProcessProxy)
{
webProcessProxy->connection()->removeWorkQueueMessageReceiver(Messages::StorageManager::messageReceiverName());
}
void StorageManager::createStorageArea(CoreIPC::Connection* connection, uint64_t storageAreaID, uint64_t storageNamespaceID, const SecurityOriginData& securityOriginData)
{
std::pair<RefPtr<CoreIPC::Connection>, uint64_t> connectionAndStorageAreaIDPair(connection, storageAreaID);
// FIXME: This should be a message check.
ASSERT((HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea> >::isValidKey(connectionAndStorageAreaIDPair)));
HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea> >::AddResult result = m_storageAreas.add(connectionAndStorageAreaIDPair, 0);
// FIXME: This should be a message check.
ASSERT(result.isNewEntry);
if (!storageNamespaceID) {
// FIXME: This is a local storage namespace. Do something.
ASSERT_NOT_REACHED();
}
ASSERT((HashMap<uint64_t, RefPtr<SessionStorageNamespace> >::isValidKey(storageNamespaceID)));
SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID).get();
// FIXME: These should be message checks.
ASSERT(sessionStorageNamespace);
ASSERT(connection == sessionStorageNamespace->allowedConnection());
RefPtr<StorageArea> storageArea = sessionStorageNamespace->getOrCreateStorageArea(securityOriginData.securityOrigin());
storageArea->addListener(connection, storageAreaID);
result.iterator->value = storageArea.release();
}
void StorageManager::destroyStorageArea(CoreIPC::Connection* connection, uint64_t storageAreaID)
{
std::pair<RefPtr<CoreIPC::Connection>, uint64_t> connectionAndStorageAreaIDPair(connection, storageAreaID);
// FIXME: This should be a message check.
ASSERT((HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea> >::isValidKey(connectionAndStorageAreaIDPair)));
HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea> >::iterator it = m_storageAreas.find(connectionAndStorageAreaIDPair);
// FIXME: This should be a message check.
ASSERT(it != m_storageAreas.end());
it->value->removeListener(connection, storageAreaID);
m_storageAreas.remove(connectionAndStorageAreaIDPair);
}
void StorageManager::getValues(CoreIPC::Connection*, uint64_t, HashMap<String, String>&)
{
// FIXME: Implement this.
}
void StorageManager::setItem(CoreIPC::Connection* connection, uint64_t storageAreaID, const String& key, const String& value, const String& urlString)
{
StorageArea* storageArea = findStorageArea(connection, storageAreaID);
// FIXME: This should be a message check.
ASSERT(storageArea);
bool quotaError;
storageArea->setItem(connection, storageAreaID, key, value, urlString, quotaError);
connection->send(Messages::StorageAreaProxy::DidSetItem(key, quotaError), storageAreaID);
}
void StorageManager::createSessionStorageNamespaceInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection, unsigned quotaInBytes)
{
ASSERT(!m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.set(storageNamespaceID, SessionStorageNamespace::create(allowedConnection, quotaInBytes));
}
void StorageManager::destroySessionStorageNamespaceInternal(uint64_t storageNamespaceID)
{
ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.remove(storageNamespaceID);
}
void StorageManager::setAllowedSessionStorageNamespaceConnectionInternal(uint64_t storageNamespaceID, CoreIPC::Connection* allowedConnection)
{
ASSERT(m_sessionStorageNamespaces.contains(storageNamespaceID));
m_sessionStorageNamespaces.get(storageNamespaceID)->setAllowedConnection(allowedConnection);
}
void StorageManager::cloneSessionStorageNamespaceInternal(uint64_t storageNamespaceID, uint64_t newStorageNamespaceID)
{
SessionStorageNamespace* sessionStorageNamespace = m_sessionStorageNamespaces.get(storageNamespaceID).get();
ASSERT(sessionStorageNamespace);
SessionStorageNamespace* newSessionStorageNamespace = m_sessionStorageNamespaces.get(newStorageNamespaceID).get();
ASSERT(newSessionStorageNamespace);
sessionStorageNamespace->cloneTo(*newSessionStorageNamespace);
}
StorageManager::StorageArea* StorageManager::findStorageArea(CoreIPC::Connection* connection, uint64_t storageAreaID) const
{
std::pair<CoreIPC::Connection*, uint64_t> connectionAndStorageAreaIDPair(connection, storageAreaID);
if (!HashMap<std::pair<RefPtr<CoreIPC::Connection>, uint64_t>, RefPtr<StorageArea> >::isValidKey(connectionAndStorageAreaIDPair))
return 0;
return m_storageAreas.get(connectionAndStorageAreaIDPair).get();
}
} // namespace WebKit