initial import
[vuplus_webkit] / Source / WebKit2 / UIProcess / VisitedLinkProvider.cpp
1 /*
2  * Copyright (C) 2010 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 "VisitedLinkProvider.h"
28
29 #include "SharedMemory.h"
30 #include "VisitedLinkTable.h"
31 #include "WebContext.h"
32 #include "WebProcessMessages.h"
33
34 using namespace WebCore;
35
36 namespace WebKit {
37
38 static const int VisitedLinkTableMaxLoad = 2;
39
40 VisitedLinkProvider::VisitedLinkProvider(WebContext* context)
41     : m_context(context)
42     , m_visitedLinksPopulated(false)
43     , m_webProcessHasVisitedLinkState(false)
44     , m_keyCount(0)
45     , m_tableSize(0)
46     , m_pendingVisitedLinksTimer(RunLoop::main(), this, &VisitedLinkProvider::pendingVisitedLinksTimerFired)
47 {
48 }
49
50 void VisitedLinkProvider::processDidFinishLaunching()
51 {
52     m_webProcessHasVisitedLinkState = false;
53
54     if (m_keyCount)
55         m_pendingVisitedLinksTimer.startOneShot(0);
56
57     if (m_visitedLinksPopulated)
58         return;
59
60     m_context->populateVisitedLinks();
61
62     m_visitedLinksPopulated = true;
63 }
64
65 void VisitedLinkProvider::addVisitedLink(LinkHash linkHash)
66 {
67     m_pendingVisitedLinks.add(linkHash);
68
69     if (!m_pendingVisitedLinksTimer.isActive())
70         m_pendingVisitedLinksTimer.startOneShot(0);
71 }
72
73 void VisitedLinkProvider::processDidClose()
74 {
75     m_pendingVisitedLinksTimer.stop();
76 }
77
78 static unsigned nextPowerOf2(unsigned v)
79 {
80     // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
81     // Devised by Sean Anderson, Sepember 14, 2001
82     
83     v--;
84     v |= v >> 1;
85     v |= v >> 2;
86     v |= v >> 4;
87     v |= v >> 8;
88     v |= v >> 16;
89     v++;
90     
91     return v;
92 }
93
94 static unsigned tableSizeForKeyCount(unsigned keyCount)
95 {
96     // We want the table to be at least half empty.
97     unsigned tableSize = nextPowerOf2(keyCount * VisitedLinkTableMaxLoad);
98
99     // Ensure that the table size is at least the size of a page.
100     size_t minimumTableSize = SharedMemory::systemPageSize() / sizeof(LinkHash);
101     if (tableSize < minimumTableSize)
102         return minimumTableSize;
103     
104     return tableSize;
105 }
106
107 void VisitedLinkProvider::pendingVisitedLinksTimerFired()
108 {
109     Vector<WebCore::LinkHash> pendingVisitedLinks;
110     copyToVector(m_pendingVisitedLinks, pendingVisitedLinks);
111     m_pendingVisitedLinks.clear();
112
113     unsigned currentTableSize = m_tableSize;
114     unsigned newTableSize = tableSizeForKeyCount(m_keyCount + pendingVisitedLinks.size());
115
116     // Links that were added.
117     Vector<WebCore::LinkHash> addedVisitedLinks;
118
119     if (currentTableSize != newTableSize) {
120         // Create a new table.
121         RefPtr<SharedMemory> newTableMemory = SharedMemory::create(newTableSize * sizeof(LinkHash));
122
123         // We failed to create the shared memory.
124         if (!newTableMemory)
125             return;
126
127         memset(newTableMemory->data(), 0, newTableMemory->size());
128
129         RefPtr<SharedMemory> currentTableMemory = m_table.sharedMemory();
130
131         m_table.setSharedMemory(newTableMemory);
132         m_tableSize = newTableSize;
133
134         if (currentTableMemory) {
135             ASSERT(currentTableMemory->size() == currentTableSize * sizeof(LinkHash));
136
137             // Go through the current hash table and re-add all entries to the new hash table.
138             const LinkHash* currentLinkHashes = static_cast<const LinkHash*>(currentTableMemory->data());
139             for (unsigned i = 0; i < currentTableSize; ++i) {
140                 LinkHash linkHash = currentLinkHashes[i];
141                 
142                 if (!linkHash)
143                     continue;
144
145                 // It should always be possible to add the link hash to a new table.
146                 if (!m_table.addLinkHash(linkHash))
147                     ASSERT_NOT_REACHED();
148             }
149         }
150     }
151
152     for (size_t i = 0; i < pendingVisitedLinks.size(); ++i) {
153         if (m_table.addLinkHash(pendingVisitedLinks[i]))
154             addedVisitedLinks.append(pendingVisitedLinks[i]);
155     }
156
157     m_keyCount += pendingVisitedLinks.size();
158
159     if (!m_webProcessHasVisitedLinkState || currentTableSize != newTableSize) {
160         // Send the new visited link table.
161         
162         SharedMemory::Handle handle;
163         if (!m_table.sharedMemory()->createHandle(handle, SharedMemory::ReadOnly))
164             return;
165
166         // FIXME (Multi-WebProcess): Encoding a handle will null it out so we need to create a new
167         // handle for every process. Maybe the ArgumentEncoder should handle this.
168         m_context->sendToAllProcesses(Messages::WebProcess::SetVisitedLinkTable(handle));
169     }
170     
171     // We now need to let the web process know that we've added links.
172     if (m_webProcessHasVisitedLinkState && addedVisitedLinks.size() <= 20) {
173         m_context->sendToAllProcesses(Messages::WebProcess::VisitedLinkStateChanged(addedVisitedLinks));
174         return;
175     }
176     
177     // Just recalculate all the visited links.
178     m_context->sendToAllProcesses(Messages::WebProcess::AllVisitedLinkStateChanged());
179     m_webProcessHasVisitedLinkState = true;
180 }
181
182 } // namespace WebKit