Changeset 112396 in webkit


Ignore:
Timestamp:
Mar 28, 2012 7:45:45 AM (12 years ago)
Author:
sergio@webkit.org
Message:

[Soup] DNS prefetching spams resolver, shoots self in the foot
https://bugs.webkit.org/show_bug.cgi?id=41630

Reviewed by Martin Robinson.

.:

Bump libsoup and glib dependencies.

  • Source/cmake/OptionsEfl.cmake:
  • configure.ac:

Source/WebCore:

Added generic DNSResolveQueue class to throttle DNS
prefetches. It's an abstract refactoring of CFNET's
DNSResolveQueue. Platform specific methods implemented for soup
and CFNET backends.

No new tests required as we're just refactoring existing code to
be used by two different ports.

  • CMakeLists.txt: added new file.
  • GNUmakefile.list.am: ditto.
  • WebCore.vcproj/WebCore.vcproj: ditto.
  • WebCore.xcodeproj/project.pbxproj: ditto.
  • platform/network/DNSResolveQueue.cpp: Added.

(WebCore):
(WebCore::DNSResolveQueue::add): adds a new host to be prefetched.
(WebCore::DNSResolveQueue::fired): by using a delay we coalesce
several prefetch requests and try to resolve them all here.

  • platform/network/DNSResolveQueue.h: Added.

(WebCore):
(DNSResolveQueue): class that implements DNS prefetch
throttling using a template pattern.
(WebCore::DNSResolveQueue::shared):
(WebCore::DNSResolveQueue::decrementRequestCount):

  • platform/network/cf/DNSCFNet.cpp:

(WebCore::DNSResolveQueue::platformProxyIsEnabledInSystemPreferences):
(WebCore::DNSResolveQueue::platformResolve):

  • platform/network/soup/DNSSoup.cpp:

(WebCore):
(WebCore::DNSResolveQueue::platformProxyIsEnabledInSystemPreferences):
(WebCore::resolvedCallback):
(WebCore::DNSResolveQueue::platformResolve):
(WebCore::prefetchDNS):

Tools:

Bump libsoup and glib dependencies.

  • efl/jhbuild.modules:
  • gtk/jhbuild.modules:
Location:
trunk
Files:
1 added
13 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/ChangeLog

    r112393 r112396  
     12012-03-28  Sergio Villar Senin  <svillar@igalia.com>
     2
     3        [Soup] DNS prefetching spams resolver, shoots self in the foot
     4        https://bugs.webkit.org/show_bug.cgi?id=41630
     5
     6        Reviewed by Martin Robinson.
     7
     8        Bump libsoup and glib dependencies.
     9
     10        * Source/cmake/OptionsEfl.cmake:
     11        * configure.ac:
     12
    1132012-03-28  Xan Lopez  <xlopez@igalia.com>
    214
  • trunk/Source/WebCore/CMakeLists.txt

    r112299 r112396  
    12221222    platform/network/ContentTypeParser.cpp
    12231223    platform/network/DataURL.cpp
     1224    platform/network/DNSResolveQueue.cpp
    12241225    platform/network/FormDataBuilder.cpp
    12251226    platform/network/FormData.cpp
  • trunk/Source/WebCore/ChangeLog

    r112395 r112396  
     12012-03-28  Sergio Villar Senin  <svillar@igalia.com>
     2
     3        [Soup] DNS prefetching spams resolver, shoots self in the foot
     4        https://bugs.webkit.org/show_bug.cgi?id=41630
     5
     6        Reviewed by Martin Robinson.
     7
     8        Added generic DNSResolveQueue class to throttle DNS
     9        prefetches. It's an abstract refactoring of CFNET's
     10        DNSResolveQueue. Platform specific methods implemented for soup
     11        and CFNET backends.
     12
     13        No new tests required as we're just refactoring existing code to
     14        be used by two different ports.
     15
     16        * CMakeLists.txt: added new file.
     17        * GNUmakefile.list.am: ditto.
     18        * WebCore.vcproj/WebCore.vcproj: ditto.
     19        * WebCore.xcodeproj/project.pbxproj: ditto.
     20        * platform/network/DNSResolveQueue.cpp: Added.
     21        (WebCore):
     22        (WebCore::DNSResolveQueue::add): adds a new host to be prefetched.
     23        (WebCore::DNSResolveQueue::fired): by using a delay we coalesce
     24        several prefetch requests and try to resolve them all here.
     25        * platform/network/DNSResolveQueue.h: Added.
     26        (WebCore):
     27        (DNSResolveQueue): class that implements DNS prefetch
     28        throttling using a template pattern.
     29        (WebCore::DNSResolveQueue::shared):
     30        (WebCore::DNSResolveQueue::decrementRequestCount):
     31        * platform/network/cf/DNSCFNet.cpp:
     32        (WebCore::DNSResolveQueue::platformProxyIsEnabledInSystemPreferences):
     33        (WebCore::DNSResolveQueue::platformResolve):
     34        * platform/network/soup/DNSSoup.cpp:
     35        (WebCore):
     36        (WebCore::DNSResolveQueue::platformProxyIsEnabledInSystemPreferences):
     37        (WebCore::resolvedCallback):
     38        (WebCore::DNSResolveQueue::platformResolve):
     39        (WebCore::prefetchDNS):
     40
    1412012-03-28  Eugene Girard  <girard@chromium.org>
    242
  • trunk/Source/WebCore/GNUmakefile.list.am

    r112310 r112396  
    34013401        Source/WebCore/platform/network/CredentialStorage.h \
    34023402        Source/WebCore/platform/network/DNS.h \
     3403        Source/WebCore/platform/network/DNSResolveQueue.cpp \
     3404        Source/WebCore/platform/network/DNSResolveQueue.h \
    34033405        Source/WebCore/platform/network/FormDataBuilder.cpp \
    34043406        Source/WebCore/platform/network/FormDataBuilder.h \
  • trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj

    r112310 r112396  
    3125031250                                </File>
    3125131251                                <File
     31252                                        RelativePath="..\platform\network\DNSResolveQueue.cpp"
     31253                                        >
     31254                                </File>
     31255                                <File
     31256                                        RelativePath="..\platform\network\DNSResolveQueue.h"
     31257                                        >
     31258                                </File>
     31259                                <File
    3125231260                                        RelativePath="..\platform\network\FormData.cpp"
    3125331261                                        >
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r112310 r112396  
    59825982                EDE3A5000C7A430600956A37 /* ColorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = EDE3A4FF0C7A430600956A37 /* ColorMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
    59835983                EDEC98030AED7E170059137F /* WebCorePrefix.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEC98020AED7E170059137F /* WebCorePrefix.h */; };
     5984                F293B27E56C112F373FFF27E /* DNSResolveQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C60128060078BB70E367A95 /* DNSResolveQueue.cpp */; };
    59845985                F316396B1329481A00A649CB /* InjectedScriptManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F31639691329481A00A649CB /* InjectedScriptManager.cpp */; };
    59855986                F316396C1329481A00A649CB /* InjectedScriptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F316396A1329481A00A649CB /* InjectedScriptManager.h */; };
     
    88738874                7AFD4A8A1131C2760035B883 /* ScriptBreakpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptBreakpoint.h; sourceTree = "<group>"; };
    88748875                7AFD4FF3113277B60035B883 /* ScriptDebugListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptDebugListener.h; sourceTree = "<group>"; };
     8876                7C60128060078BB70E367A95 /* DNSResolveQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DNSResolveQueue.cpp; sourceTree = "<group>"; };
    88758877                7E33CD00127F340D00BE8F17 /* PurgePriority.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PurgePriority.h; sourceTree = "<group>"; };
    88768878                7E37EF2D1339208800B29250 /* SubresourceLoaderCF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SubresourceLoaderCF.cpp; path = cf/SubresourceLoaderCF.cpp; sourceTree = "<group>"; };
     
    1325313255                FA654A691108ABED002615E0 /* MathMLTextElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathMLTextElement.cpp; sourceTree = "<group>"; };
    1325413256                FA654A6A1108ABED002615E0 /* MathMLTextElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathMLTextElement.h; sourceTree = "<group>"; };
     13257                FA6E466FCD0418A9966A5B60 /* DNSResolveQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSResolveQueue.h; sourceTree = "<group>"; };
    1325513258                FABE72ED1059C1EB00D999DD /* MathMLElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathMLElement.cpp; sourceTree = "<group>"; };
    1325613259                FABE72EE1059C1EB00D999DD /* MathMLElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathMLElement.h; sourceTree = "<group>"; };
     
    1505615059                                51A052311058774F00CC9E95 /* CredentialStorage.h */,
    1505715060                                B2F34FE50E82F81400F627CD /* DNS.h */,
     15061                                7C60128060078BB70E367A95 /* DNSResolveQueue.cpp */,
     15062                                FA6E466FCD0418A9966A5B60 /* DNSResolveQueue.h */,
    1505815063                                514C765A0CE923A1007EF3CD /* FormData.cpp */,
    1505915064                                514C765B0CE923A1007EF3CD /* FormData.h */,
     
    2529025295                                A1E1154413015C3D0054AC8C /* DistantLightSource.cpp in Sources */,
    2529125296                                B2F34FE90E82F82700F627CD /* DNSCFNet.cpp in Sources */,
     25297                                F293B27E56C112F373FFF27E /* DNSResolveQueue.cpp in Sources */,
    2529225298                                A8185F3C09765766005826D9 /* Document.cpp in Sources */,
    2529325299                                A3BB59F31457A40D00AC56FE /* DocumentEventQueue.cpp in Sources */,
  • trunk/Source/WebCore/platform/network/DNSResolveQueue.h

    r112395 r112396  
    11/*
    2  * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
    3  * Copyright (C) 2009, 2012 Igalia S.L.
     2 * Copyright (C) 2009 Apple Inc. All Rights Reserved.
     3 * Copyright (C) 2012 Igalia S.L.
    44 *
    55 * Redistribution and use in source and binary forms, with or without
     
    2222 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    2323 * (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. 
     24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2525 */
    2626
    27 #include "config.h"
    28 #include "DNS.h"
     27#ifndef DNSResolveQueue_h
     28#define DNSResolveQueue_h
    2929
    30 #include "GOwnPtrSoup.h"
    31 #include "ResourceHandle.h"
    32 #include <wtf/text/CString.h>
     30#include "Timer.h"
     31#include <wtf/Forward.h>
     32#include <wtf/HashSet.h>
     33#include <wtf/text/StringHash.h>
    3334
    3435namespace WebCore {
    3536
    36 void prefetchDNS(const String& hostname)
    37 {
    38     if (hostname.isEmpty())
    39         return;
     37class DNSResolveQueue : public TimerBase {
    4038
    41     String uri = "http://" + hostname;
    42     GOwnPtr<SoupURI> soupURI(soup_uri_new(uri.utf8().data()));
    43     if (!soupURI)
    44         return;
     39public:
     40    static DNSResolveQueue& shared()
     41    {
     42      DEFINE_STATIC_LOCAL(DNSResolveQueue, queue, ());
     43      return queue;
     44    }
    4545
    46     soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get());
     46    void add(const String& hostname);
     47    void decrementRequestCount()
     48    {
     49      atomicDecrement(&m_requestsInFlight);
     50    }
     51
     52private:
     53    bool platformProxyIsEnabledInSystemPreferences();
     54    void platformResolve(const String&);
     55
     56    void fired();
     57
     58    HashSet<String> m_names;
     59    int m_requestsInFlight;
     60};
     61
    4762}
    4863
    49 }
     64#endif // DNSResolveQueue_h
  • trunk/Source/WebCore/platform/network/cf/DNSCFNet.cpp

    r96547 r112396  
    22 * Copyright (C) 2008 Collin Jackson  <collinj@webkit.org>
    33 * Copyright (C) 2009 Apple Inc. All Rights Reserved.
     4 * Copyright (C) 2012 Igalia S.L.
    45 *
    56 * Redistribution and use in source and binary forms, with or without
     
    2728#include "config.h"
    2829#include "DNS.h"
     30#include "DNSResolveQueue.h"
    2931
    3032#include "KURL.h"
     
    4850namespace WebCore {
    4951
    50 // When resolve queue is empty, we fire async resolution requests immediately (which is important if the prefetch is triggered by hovering).
    51 // But during page parsing, we should coalesce identical requests to avoid stressing out CFHost.
    52 const int namesToResolveImmediately = 4;
    53 
    54 // Coalesce prefetch requests for this long before sending them out.
    55 const double coalesceDelayInSeconds = 1.0;
    56 
    57 // Sending many DNS requests at once can overwhelm some gateways. CFHost doesn't currently throttle for us, see <rdar://8105550>.
    58 const int maxSimultaneousRequests = 8;
    59 
    60 // 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
    61 // to negatively affect other applications' performance by pushing their cached entries out.
    62 // If we end up with lots of names to prefetch, some will be dropped.
    63 const int maxRequestsToQueue = 64;
    64 
    65 // If there were queued names that couldn't be sent simultaneously, check the state of resolvers after this delay.
    66 const double retryResolvingInSeconds = 0.1;
    67 
    68 static bool proxyIsEnabledInSystemPreferences()
     52bool DNSResolveQueue::platformProxyIsEnabledInSystemPreferences()
    6953{
    7054    // Don't do DNS prefetch if proxies are involved. For many proxy types, the user agent is never exposed
     
    9781}
    9882
    99 class DNSResolveQueue : public TimerBase {
    100 public:
    101     static DNSResolveQueue& shared();
    102     void add(const String&);
    103     void decrementRequestCount();
    104 
    105 private:
    106     DNSResolveQueue();
    107 
    108     void resolve(const String&);
    109     virtual void fired();
    110     HashSet<String> m_names;
    111     int m_requestsInFlight;
    112 };
    113 
    114 DNSResolveQueue::DNSResolveQueue()
    115     : m_requestsInFlight(0)
    116 {
    117 }
    118 
    119 DNSResolveQueue& DNSResolveQueue::shared()
    120 {
    121     DEFINE_STATIC_LOCAL(DNSResolveQueue, names, ());
    122     return names;
    123 }
    124 
    125 void DNSResolveQueue::add(const String& name)
    126 {
    127     // If there are no names queued, and few enough are in flight, resolve immediately (the mouse may be over a link).
    128     if (!m_names.size()) {
    129         if (proxyIsEnabledInSystemPreferences())
    130             return;
    131 
    132         if (atomicIncrement(&m_requestsInFlight) <= namesToResolveImmediately) {
    133             resolve(name);
    134             return;
    135         }
    136         atomicDecrement(&m_requestsInFlight);
    137     }
    138 
    139     // It's better to not prefetch some names than to clog the queue.
    140     // Dropping the newest names, because on a single page, these are likely to be below oldest ones.
    141     if (m_names.size() < maxRequestsToQueue) {
    142         m_names.add(name);
    143         if (!isActive())
    144             startOneShot(coalesceDelayInSeconds);
    145     }
    146 }
    147 
    148 void DNSResolveQueue::decrementRequestCount()
    149 {
    150     atomicDecrement(&m_requestsInFlight);
    151 }
    152 
    153 void DNSResolveQueue::fired()
    154 {
    155     if (proxyIsEnabledInSystemPreferences()) {
    156         m_names.clear();
    157         return;
    158     }
    159 
    160     int requestsAllowed = maxSimultaneousRequests - m_requestsInFlight;
    161 
    162     for (; !m_names.isEmpty() && requestsAllowed > 0; --requestsAllowed) {
    163         atomicIncrement(&m_requestsInFlight);
    164         HashSet<String>::iterator currentName = m_names.begin();
    165         resolve(*currentName);
    166         m_names.remove(currentName);
    167     }
    168 
    169     if (!m_names.isEmpty())
    170         startOneShot(retryResolvingInSeconds);
    171 }
    172 
    17383static void clientCallback(CFHostRef theHost, CFHostInfoType, const CFStreamError*, void*)
    17484{
     
    17787}
    17888
    179 void DNSResolveQueue::resolve(const String& hostname)
     89void DNSResolveQueue::platformResolve(const String& hostname)
    18090{
    18191    ASSERT(isMainThread());
     
    18494    RetainPtr<CFHostRef> host(AdoptCF, CFHostCreateWithName(0, hostnameCF.get()));
    18595    if (!host) {
    186         atomicDecrement(&m_requestsInFlight);
     96        decrementRequestCount();
    18797        return;
    18898    }
  • trunk/Source/WebCore/platform/network/soup/DNSSoup.cpp

    r110697 r112396  
    2727#include "config.h"
    2828#include "DNS.h"
     29#include "DNSResolveQueue.h"
    2930
    3031#include "GOwnPtrSoup.h"
    3132#include "ResourceHandle.h"
     33#include <wtf/MainThread.h>
    3234#include <wtf/text/CString.h>
    3335
    3436namespace WebCore {
    3537
     38// There is no current reliable way to know if we're behind a proxy at
     39// this level. We'll have to implement it in
     40// SoupSession/SoupProxyURIResolver/GProxyResolver
     41bool DNSResolveQueue::platformProxyIsEnabledInSystemPreferences()
     42{
     43    return false;
     44}
     45
     46static void resolvedCallback(SoupAddress* soupAddress, guint status, void* userData)
     47{
     48    DNSResolveQueue::shared().decrementRequestCount();
     49}
     50
     51void DNSResolveQueue::platformResolve(const String& hostname)
     52{
     53    ASSERT(isMainThread());
     54
     55    soup_session_prefetch_dns(ResourceHandle::defaultSession(), hostname.utf8().data(), 0, resolvedCallback, 0);
     56}
     57
    3658void prefetchDNS(const String& hostname)
    3759{
     60    ASSERT(isMainThread());
    3861    if (hostname.isEmpty())
    3962        return;
    4063
    41     String uri = "http://" + hostname;
    42     GOwnPtr<SoupURI> soupURI(soup_uri_new(uri.utf8().data()));
    43     if (!soupURI)
    44         return;
    45 
    46     soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get());
     64    DNSResolveQueue::shared().add(hostname);
    4765}
    4866
  • trunk/Source/cmake/OptionsEfl.cmake

    r110991 r112396  
    3333FIND_PACKAGE(ZLIB REQUIRED)
    3434
    35 FIND_PACKAGE(Glib REQUIRED)
     35FIND_PACKAGE(Glib 2.31.8 REQUIRED)
    3636FIND_PACKAGE(Gthread REQUIRED)
    37 FIND_PACKAGE(LibSoup2 2.37.2.1 REQUIRED)
     37FIND_PACKAGE(LibSoup2 2.37.92 REQUIRED)
    3838SET(ENABLE_GLIB_SUPPORT ON)
    3939
  • trunk/Tools/ChangeLog

    r112392 r112396  
     12012-03-28  Sergio Villar Senin  <svillar@igalia.com>
     2
     3        [Soup] DNS prefetching spams resolver, shoots self in the foot
     4        https://bugs.webkit.org/show_bug.cgi?id=41630
     5
     6        Reviewed by Martin Robinson.
     7
     8        Bump libsoup and glib dependencies.
     9
     10        * efl/jhbuild.modules:
     11        * gtk/jhbuild.modules:
     12
    1132012-03-28  Gustavo Noronha Silva  <gns@gnome.org>
    214
  • trunk/Tools/efl/jhbuild.modules

    r112392 r112396  
    110110      <dep package="libffi"/>
    111111    </dependencies>
    112     <branch module="/pub/GNOME/sources/glib/2.31/glib-2.31.2.tar.xz" version="2.31.2"
     112    <branch module="/pub/GNOME/sources/glib/2.31/glib-2.31.8.tar.xz" version="2.31.8"
    113113            repo="ftp.gnome.org"
    114             hash="sha256:19d7921671a487c3c5759a57df7b8508afdbadd7764d62a47a82fff7b399032b"
    115             md5sum="1cbdf314d7c87916a0c3dce83ac0285f"/>
     114            hash="sha256:1ce3d275189000e1c50e92efcdb6447bc260b1e5c41699b7a1959e3e1928fbaa"
     115            md5sum="6909664f29fae2f00cc3181c8c6a6aa7"/>
    116116  </autotools>
    117117
     
    143143      <dep package="glib-networking"/>
    144144    </dependencies>
    145     <branch module="libsoup" version="2.37.2.1+git"
     145    <branch module="libsoup" version="2.37.92"
    146146            repo="git.gnome.org"
    147             tag="5cbfc48caf76ced2e28ee06c9e40523273601dc6"/>
     147            tag="afcff7115a6c36ca3de5bc88994174f5a0e01956"/>
    148148  </autotools>
    149149
  • trunk/Tools/gtk/jhbuild.modules

    r112392 r112396  
    120120      <dep package="libffi"/>
    121121    </dependencies>
    122     <branch module="/pub/GNOME/sources/glib/2.31/glib-2.31.2.tar.xz" version="2.31.2"
     122    <branch module="/pub/GNOME/sources/glib/2.31/glib-2.31.8.tar.xz" version="2.31.8"
    123123            repo="ftp.gnome.org"
    124             hash="sha256:19d7921671a487c3c5759a57df7b8508afdbadd7764d62a47a82fff7b399032b"
    125             md5sum="1cbdf314d7c87916a0c3dce83ac0285f"/>
     124            hash="sha256:1ce3d275189000e1c50e92efcdb6447bc260b1e5c41699b7a1959e3e1928fbaa"
     125            md5sum="6909664f29fae2f00cc3181c8c6a6aa7"/>
    126126  </autotools>
    127127
     
    149149      <dep package="glib-networking"/>
    150150    </dependencies>
    151     <branch module="libsoup" version="2.37.2.1+git"
     151    <branch module="libsoup" version="2.37.92"
    152152            repo="git.gnome.org"
    153             tag="5cbfc48caf76ced2e28ee06c9e40523273601dc6"/>
     153            tag="afcff7115a6c36ca3de5bc88994174f5a0e01956"/>
    154154  </autotools>
    155155
  • trunk/configure.ac

    r110311 r112396  
    378378FONTCONFIG_REQUIRED_VERSION=2.4
    379379FREETYPE2_REQUIRED_VERSION=9.0
    380 GLIB_REQUIRED_VERSION=2.31.2
    381 LIBSOUP_REQUIRED_VERSION=2.37.2.1
     380GLIB_REQUIRED_VERSION=2.31.8
     381LIBSOUP_REQUIRED_VERSION=2.37.92
    382382LIBXML_REQUIRED_VERSION=2.6
    383383PANGO_REQUIRED_VERSION=1.21.0
Note: See TracChangeset for help on using the changeset viewer.