void VisitedLinkProvider::pendingVisitedLinksTimerFired() { Vector<WebCore::LinkHash> pendingVisitedLinks; copyToVector(m_pendingVisitedLinks, pendingVisitedLinks); m_pendingVisitedLinks.clear(); unsigned currentTableSize = m_tableSize; // Upper bound on needed size - some of the links may be duplicates, in which case we could have done with less. unsigned newTableSize = tableSizeForKeyCount(m_keyCount + pendingVisitedLinks.size()); // Never decrease table size when adding to it, to avoid unneeded churn. newTableSize = std::max(currentTableSize, newTableSize); // Links that were added. Vector<WebCore::LinkHash> addedVisitedLinks; // VisitedLinkTable remains internally consistent when adding, so it's OK to modify it in place // even if a web process is accessing it at the same time. if (currentTableSize != newTableSize) { RefPtr<SharedMemory> newTableMemory = SharedMemory::create(newTableSize * sizeof(LinkHash)); // We failed to create the shared memory. if (!newTableMemory) { LOG_ERROR("Could not allocate shared memory for visited link table"); return; } memset(newTableMemory->data(), 0, newTableMemory->size()); RefPtr<SharedMemory> currentTableMemory = m_table.sharedMemory(); m_table.setSharedMemory(newTableMemory); m_tableSize = newTableSize; if (currentTableMemory) { ASSERT(currentTableMemory->size() == currentTableSize * sizeof(LinkHash)); // Go through the current hash table and re-add all entries to the new hash table. const LinkHash* currentLinkHashes = static_cast<const LinkHash*>(currentTableMemory->data()); for (unsigned i = 0; i < currentTableSize; ++i) { LinkHash linkHash = currentLinkHashes[i]; if (!linkHash) continue; // It should always be possible to add the link hash to a new table. if (!m_table.addLinkHash(linkHash)) ASSERT_NOT_REACHED(); } } } for (size_t i = 0; i < pendingVisitedLinks.size(); ++i) { if (m_table.addLinkHash(pendingVisitedLinks[i])) addedVisitedLinks.append(pendingVisitedLinks[i]); } m_keyCount += pendingVisitedLinks.size(); for (HashSet<WebProcessProxy*>::iterator iter = m_processesWithVisitedLinkState.begin(); iter != m_processesWithVisitedLinkState.end(); ++iter) { WebProcessProxy* process = *iter; if (currentTableSize != newTableSize) { // In the rare case of needing to resize the table, we'll bypass the VisitedLinkStateChanged optimization, // and unconditionally use AllVisitedLinkStateChanged for the process. m_processesWithoutVisitedLinkState.add(process); continue; } if (addedVisitedLinks.size() <= 20) process->send(Messages::WebProcess::VisitedLinkStateChanged(addedVisitedLinks), 0); else process->send(Messages::WebProcess::AllVisitedLinkStateChanged(), 0); } for (HashSet<WebProcessProxy*>::iterator iter = m_processesWithoutVisitedLinkState.begin(); iter != m_processesWithoutVisitedLinkState.end(); ++iter) { WebProcessProxy* process = *iter; SharedMemory::Handle handle; if (!m_table.sharedMemory()->createHandle(handle, SharedMemory::ReadOnly)) return; process->send(Messages::WebProcess::SetVisitedLinkTable(handle), 0); process->send(Messages::WebProcess::AllVisitedLinkStateChanged(), 0); m_processesWithVisitedLinkState.add(process); } m_processesWithoutVisitedLinkState.clear(); }