1/*
2 * Copyright (C) 2012-2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "PlugInAutoStartProvider.h"
28
29#include "APIArray.h"
30#include "ImmutableDictionary.h"
31#include "WebContext.h"
32#include "WebContextClient.h"
33#include "WebProcessMessages.h"
34#include <wtf/CurrentTime.h>
35
36using namespace WebCore;
37
38static const double plugInAutoStartExpirationTimeThreshold = 30 * 24 * 60 * 60;
39
40namespace WebKit {
41
42PlugInAutoStartProvider::PlugInAutoStartProvider(WebContext* context)
43    : m_context(context)
44{
45    m_hashToOriginMap.add(SessionID::defaultSessionID(), HashMap<unsigned, String>());
46    m_autoStartTable.add(SessionID::defaultSessionID(), AutoStartTable());
47}
48
49static double expirationTimeFromNow()
50{
51    return currentTime() + plugInAutoStartExpirationTimeThreshold;
52}
53
54void PlugInAutoStartProvider::addAutoStartOriginHash(const String& pageOrigin, unsigned plugInOriginHash, SessionID sessionID)
55{
56    auto sessionIterator = m_hashToOriginMap.find(sessionID);
57    if (sessionIterator == m_hashToOriginMap.end()) {
58        if (m_hashToOriginMap.get(SessionID::defaultSessionID()).contains(plugInOriginHash))
59            return;
60        sessionIterator = m_hashToOriginMap.set(sessionID, HashMap<unsigned, String>()).iterator;
61    } else if (sessionIterator->value.contains(plugInOriginHash) || m_hashToOriginMap.get(SessionID::defaultSessionID()).contains(plugInOriginHash))
62        return;
63
64    AutoStartTable::iterator it = m_autoStartTable.add(sessionID, AutoStartTable()).iterator->value.add(pageOrigin, PlugInAutoStartOriginMap()).iterator;
65
66    double expirationTime = expirationTimeFromNow();
67    it->value.set(plugInOriginHash, expirationTime);
68    sessionIterator->value.set(plugInOriginHash, pageOrigin);
69
70    m_context->sendToAllProcesses(Messages::WebProcess::DidAddPlugInAutoStartOriginHash(plugInOriginHash, expirationTime, sessionID));
71
72    if (!sessionID.isEphemeral())
73        m_context->client().plugInAutoStartOriginHashesChanged(m_context);
74}
75
76SessionPlugInAutoStartOriginMap PlugInAutoStartProvider::autoStartOriginHashesCopy() const
77{
78    SessionPlugInAutoStartOriginMap sessionMap;
79
80    for (const auto& sessionKeyOriginHash : m_autoStartTable) {
81        PlugInAutoStartOriginMap& map = sessionMap.add(sessionKeyOriginHash.key, PlugInAutoStartOriginMap()).iterator->value;
82        for (const auto& keyOriginHash : sessionKeyOriginHash.value) {
83            for (const auto& originHash : keyOriginHash.value)
84                map.set(originHash.key, originHash.value);
85        }
86    }
87    return sessionMap;
88}
89
90PassRefPtr<ImmutableDictionary> PlugInAutoStartProvider::autoStartOriginsTableCopy() const
91{
92    ImmutableDictionary::MapType map;
93
94    double now = currentTime();
95    for (const auto& stringOriginHash : m_autoStartTable.get(SessionID::defaultSessionID())) {
96        ImmutableDictionary::MapType hashMap;
97        for (const auto& originHash : stringOriginHash.value) {
98            if (now <= originHash.value)
99                hashMap.set(String::number(originHash.key), API::Double::create(originHash.value));
100        }
101        if (hashMap.size())
102            map.set(stringOriginHash.key, ImmutableDictionary::create(WTF::move(hashMap)));
103    }
104
105    return ImmutableDictionary::create(WTF::move(map));
106}
107
108void PlugInAutoStartProvider::setAutoStartOriginsTable(ImmutableDictionary& table)
109{
110    setAutoStartOriginsTableWithItemsPassingTest(table, [](double) {
111        return true;
112    });
113}
114
115void PlugInAutoStartProvider::setAutoStartOriginsFilteringOutEntriesAddedAfterTime(ImmutableDictionary& table, double time)
116{
117    double adjustedTimestamp = time + plugInAutoStartExpirationTimeThreshold;
118    setAutoStartOriginsTableWithItemsPassingTest(table, [adjustedTimestamp](double expirationTimestamp) {
119        return adjustedTimestamp > expirationTimestamp;
120    });
121}
122
123void PlugInAutoStartProvider::setAutoStartOriginsTableWithItemsPassingTest(ImmutableDictionary& table, std::function<bool(double expirationTimestamp)> isExpirationTimeAcceptable)
124{
125    ASSERT(isExpirationTimeAcceptable);
126
127    m_hashToOriginMap.clear();
128    m_autoStartTable.clear();
129    HashMap<unsigned, double> hashMap;
130    HashMap<unsigned, String>& hashToOriginMap = m_hashToOriginMap.add(SessionID::defaultSessionID(), HashMap<unsigned, String>()).iterator->value;
131    AutoStartTable& ast = m_autoStartTable.add(SessionID::defaultSessionID(), AutoStartTable()).iterator->value;
132
133    for (auto& strDict : table.map()) {
134        PlugInAutoStartOriginMap hashes;
135        for (auto& hashTime : static_cast<ImmutableDictionary*>(strDict.value.get())->map()) {
136            bool ok;
137            unsigned hash = hashTime.key.toUInt(&ok);
138            if (!ok)
139                continue;
140
141            if (hashTime.value->type() != API::Double::APIType)
142                continue;
143
144            double expirationTime = static_cast<API::Double*>(hashTime.value.get())->value();
145            if (!isExpirationTimeAcceptable(expirationTime))
146                continue;
147
148            hashes.set(hash, expirationTime);
149            hashMap.set(hash, expirationTime);
150            hashToOriginMap.set(hash, strDict.key);
151        }
152
153        if (!hashes.isEmpty())
154            ast.set(strDict.key, hashes);
155    }
156
157    m_context->sendToAllProcesses(Messages::WebProcess::ResetPlugInAutoStartOriginDefaultHashes(hashMap));
158}
159
160void PlugInAutoStartProvider::setAutoStartOriginsArray(API::Array& originList)
161{
162    m_autoStartOrigins.clear();
163    for (const auto& string : originList.elementsOfType<API::String>())
164        m_autoStartOrigins.append(string->string());
165}
166
167void PlugInAutoStartProvider::didReceiveUserInteraction(unsigned plugInOriginHash, SessionID sessionID)
168{
169    HashMap<WebCore::SessionID, HashMap<unsigned, String>>::const_iterator sessionIterator = m_hashToOriginMap.find(sessionID);
170    HashMap<unsigned, String>::const_iterator it;
171    bool contains = false;
172    if (sessionIterator != m_hashToOriginMap.end()) {
173        it = sessionIterator->value.find(plugInOriginHash);
174        contains = it != sessionIterator->value.end();
175    }
176    if (!contains) {
177        sessionIterator = m_hashToOriginMap.find(SessionID::defaultSessionID());
178        it = sessionIterator->value.find(plugInOriginHash);
179        if (it == sessionIterator->value.end()) {
180            ASSERT_NOT_REACHED();
181            return;
182        }
183    }
184
185    double newExpirationTime = expirationTimeFromNow();
186    m_autoStartTable.add(sessionID, AutoStartTable()).iterator->value.add(it->value, PlugInAutoStartOriginMap()).iterator->value.set(plugInOriginHash, newExpirationTime);
187    m_context->sendToAllProcesses(Messages::WebProcess::DidAddPlugInAutoStartOriginHash(plugInOriginHash, newExpirationTime, sessionID));
188    m_context->client().plugInAutoStartOriginHashesChanged(m_context);
189}
190
191} // namespace WebKit
192