Home | History | Annotate | Download | only in cf
      1 /*
      2  * Copyright (C) 2008 Collin Jackson  <collinj (at) webkit.org>
      3  * Copyright (C) 2009 Apple Inc. All Rights Reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "DNS.h"
     29 
     30 #include "StringHash.h"
     31 #include "Timer.h"
     32 #include <wtf/HashSet.h>
     33 #include <wtf/RetainPtr.h>
     34 #include <wtf/StdLibExtras.h>
     35 
     36 #if PLATFORM(WIN)
     37 #include "LoaderRunLoopCF.h"
     38 #endif
     39 
     40 #ifdef BUILDING_ON_TIGER
     41 // This function is available on Tiger, but not declared in the CFRunLoop.h header on Tiger.
     42 extern "C" CFRunLoopRef CFRunLoopGetMain();
     43 #endif
     44 
     45 namespace WebCore {
     46 
     47 // When resolve queue is empty, we fire async resolution requests immediately (which is important if the prefetch is triggered by hovering).
     48 // But during page parsing, we should coalesce identical requests to avoid stressing out CFHost.
     49 const int namesToResolveImmediately = 4;
     50 
     51 // Coalesce prefetch requests for this long before sending them out.
     52 const double coalesceDelay = 1.0;
     53 
     54 // For a page has links to many outside sites, it is likely that the system DNS resolver won't be able to cache them all anyway, and we don't want
     55 // to negatively affect other appications' performance, by pushing their cached entries out, too.
     56 // If we end up with lots of names to prefetch, some will be dropped.
     57 const int maxRequestsToSend = 64;
     58 
     59 class DNSResolveQueue : public TimerBase {
     60 public:
     61     static DNSResolveQueue& shared();
     62     void add(const String&);
     63     void decrementRequestCount();
     64 
     65 private:
     66     DNSResolveQueue();
     67 
     68     void resolve(const String&);
     69     virtual void fired();
     70     HashSet<String> m_names;
     71     int m_requestsInFlight;
     72 };
     73 
     74 DNSResolveQueue::DNSResolveQueue()
     75     : m_requestsInFlight(0)
     76 {
     77 }
     78 
     79 DNSResolveQueue& DNSResolveQueue::shared()
     80 {
     81     DEFINE_STATIC_LOCAL(DNSResolveQueue, names, ());
     82     return names;
     83 }
     84 
     85 void DNSResolveQueue::add(const String& name)
     86 {
     87     // If there are no names queued, and few enough are in flight, resolve immediately (the mouse may be over a link).
     88     if (!m_names.size()) {
     89         if (atomicIncrement(&m_requestsInFlight) <= namesToResolveImmediately) {
     90             resolve(name);
     91             return;
     92         }
     93         atomicDecrement(&m_requestsInFlight);
     94     }
     95     m_names.add(name);
     96     if (!isActive())
     97         startOneShot(coalesceDelay);
     98 }
     99 
    100 void DNSResolveQueue::decrementRequestCount()
    101 {
    102     atomicDecrement(&m_requestsInFlight);
    103 }
    104 
    105 void DNSResolveQueue::fired()
    106 {
    107     int requestsAllowed = maxRequestsToSend - m_requestsInFlight;
    108 
    109     for (HashSet<String>::iterator iter = m_names.begin(); iter != m_names.end() && requestsAllowed > 0; ++iter, --requestsAllowed) {
    110         atomicIncrement(&m_requestsInFlight);
    111         resolve(*iter);
    112     }
    113 
    114     // It's better to skip some names than to clog the queue.
    115     m_names.clear();
    116 }
    117 
    118 static void clientCallback(CFHostRef theHost, CFHostInfoType, const CFStreamError*, void*)
    119 {
    120     DNSResolveQueue::shared().decrementRequestCount(); // It's ok to call shared() from a secondary thread, the static variable has already been initialized by now.
    121     CFRelease(theHost);
    122 }
    123 
    124 void DNSResolveQueue::resolve(const String& hostname)
    125 {
    126     ASSERT(isMainThread());
    127 
    128     RetainPtr<CFStringRef> hostnameCF(AdoptCF, hostname.createCFString());
    129     RetainPtr<CFHostRef> host(AdoptCF, CFHostCreateWithName(0, hostnameCF.get()));
    130     if (!host) {
    131         atomicDecrement(&m_requestsInFlight);
    132         return;
    133     }
    134     CFHostClientContext context = { 0, 0, 0, 0, 0 };
    135     Boolean result = CFHostSetClient(host.get(), clientCallback, &context);
    136     ASSERT_UNUSED(result, result);
    137 #if !PLATFORM(WIN)
    138     CFHostScheduleWithRunLoop(host.get(), CFRunLoopGetMain(), kCFRunLoopCommonModes);
    139 #else
    140     // On Windows, we run a separate thread with CFRunLoop, which is where clientCallback will be called.
    141     CFHostScheduleWithRunLoop(host.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
    142 #endif
    143     CFHostStartInfoResolution(host.get(), kCFHostAddresses, 0);
    144     host.releaseRef(); // The host will be released from clientCallback().
    145 }
    146 
    147 void prefetchDNS(const String& hostname)
    148 {
    149     ASSERT(isMainThread());
    150     if (hostname.isEmpty())
    151         return;
    152     DNSResolveQueue::shared().add(hostname);
    153 }
    154 
    155 }
    156