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 183