Changeset 201924 in webkit


Ignore:
Timestamp:
Jun 10, 2016 6:26:30 AM (8 years ago)
Author:
youenn.fablet@crf.canon.fr
Message:

Move preflight check code outside of DocumentThreadableLoader
https://bugs.webkit.org/show_bug.cgi?id=158425

Reviewed by Darin Adler.

Moving preflight check code in its own class.
This allows code to be easier to read, use/reuse and update.

Behavior should be the same as before except in the case of a preflight response
being a 3XX redirect response.
Before this patch, the 3XX response was directly passed to the code processing regular responses.
To keep compatibility with existing tests, a didFailRedirectCheck callback is called.
This should be change to a preflight failure.

Covered by existing tests.

  • CMakeLists.txt:
  • WebCore.xcodeproj/project.pbxproj:
  • loader/CrossOriginPreflightChecker.cpp: Added.

(WebCore::CrossOriginPreflightChecker::CrossOriginPreflightChecker):
(WebCore::CrossOriginPreflightChecker::~CrossOriginPreflightChecker):
(WebCore::CrossOriginPreflightChecker::handleLoadingFailure):
(WebCore::CrossOriginPreflightChecker::validatePreflightResponse):
(WebCore::CrossOriginPreflightChecker::notifyFinished):
(WebCore::CrossOriginPreflightChecker::startPreflight):
(WebCore::CrossOriginPreflightChecker::doPreflight):
(WebCore::CrossOriginPreflightChecker::redirectReceived):
(WebCore::CrossOriginPreflightChecker::setDefersLoading):
(WebCore::CrossOriginPreflightChecker::isXMLHttpRequest):

  • loader/CrossOriginPreflightChecker.h: Added.
  • loader/DocumentThreadableLoader.cpp:

(WebCore::DocumentThreadableLoader::create):
(WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequest):
(WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight):
(WebCore::DocumentThreadableLoader::setDefersLoading):
(WebCore::DocumentThreadableLoader::clearResource):
(WebCore::DocumentThreadableLoader::didReceiveResponse):
(WebCore::DocumentThreadableLoader::didReceiveData):
(WebCore::DocumentThreadableLoader::notifyFinished):
(WebCore::DocumentThreadableLoader::didFinishLoading):
(WebCore::DocumentThreadableLoader::didFail):
(WebCore::DocumentThreadableLoader::preflightSuccess):
(WebCore::DocumentThreadableLoader::preflightFailure):
(WebCore::DocumentThreadableLoader::loadRequest):
(WebCore::DocumentThreadableLoader::responseReceived): Deleted.
(WebCore::DocumentThreadableLoader::dataReceived): Deleted.
(WebCore::DocumentThreadableLoader::isAllowedByContentSecurityPolicy): Deleted.

  • loader/DocumentThreadableLoader.h:

(WebCore::DocumentThreadableLoader::options):
(WebCore::DocumentThreadableLoader::isLoading):
(WebCore::DocumentThreadableLoader::document):

Location:
trunk/Source/WebCore
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/CMakeLists.txt

    r201898 r201924  
    19011901    loader/CookieJar.cpp
    19021902    loader/CrossOriginAccessControl.cpp
     1903    loader/CrossOriginPreflightChecker.cpp
    19031904    loader/CrossOriginPreflightResultCache.cpp
    19041905    loader/DocumentLoadTiming.cpp
  • trunk/Source/WebCore/ChangeLog

    r201920 r201924  
     12016-06-10  Youenn Fablet  <youenn.fablet@crf.canon.fr>
     2
     3        Move preflight check code outside of DocumentThreadableLoader
     4        https://bugs.webkit.org/show_bug.cgi?id=158425
     5
     6        Reviewed by Darin Adler.
     7
     8        Moving preflight check code in its own class.
     9        This allows code to be easier to read, use/reuse and update.
     10
     11        Behavior should be the same as before except in the case of a preflight response
     12        being a 3XX redirect response.
     13        Before this patch, the 3XX response was directly passed to the code processing regular responses.
     14        To keep compatibility with existing tests, a didFailRedirectCheck callback is called.
     15        This should be change to a preflight failure.
     16
     17        Covered by existing tests.
     18
     19        * CMakeLists.txt:
     20        * WebCore.xcodeproj/project.pbxproj:
     21        * loader/CrossOriginPreflightChecker.cpp: Added.
     22        (WebCore::CrossOriginPreflightChecker::CrossOriginPreflightChecker):
     23        (WebCore::CrossOriginPreflightChecker::~CrossOriginPreflightChecker):
     24        (WebCore::CrossOriginPreflightChecker::handleLoadingFailure):
     25        (WebCore::CrossOriginPreflightChecker::validatePreflightResponse):
     26        (WebCore::CrossOriginPreflightChecker::notifyFinished):
     27        (WebCore::CrossOriginPreflightChecker::startPreflight):
     28        (WebCore::CrossOriginPreflightChecker::doPreflight):
     29        (WebCore::CrossOriginPreflightChecker::redirectReceived):
     30        (WebCore::CrossOriginPreflightChecker::setDefersLoading):
     31        (WebCore::CrossOriginPreflightChecker::isXMLHttpRequest):
     32        * loader/CrossOriginPreflightChecker.h: Added.
     33        * loader/DocumentThreadableLoader.cpp:
     34        (WebCore::DocumentThreadableLoader::create):
     35        (WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequest):
     36        (WebCore::DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight):
     37        (WebCore::DocumentThreadableLoader::setDefersLoading):
     38        (WebCore::DocumentThreadableLoader::clearResource):
     39        (WebCore::DocumentThreadableLoader::didReceiveResponse):
     40        (WebCore::DocumentThreadableLoader::didReceiveData):
     41        (WebCore::DocumentThreadableLoader::notifyFinished):
     42        (WebCore::DocumentThreadableLoader::didFinishLoading):
     43        (WebCore::DocumentThreadableLoader::didFail):
     44        (WebCore::DocumentThreadableLoader::preflightSuccess):
     45        (WebCore::DocumentThreadableLoader::preflightFailure):
     46        (WebCore::DocumentThreadableLoader::loadRequest):
     47        (WebCore::DocumentThreadableLoader::responseReceived): Deleted.
     48        (WebCore::DocumentThreadableLoader::dataReceived): Deleted.
     49        (WebCore::DocumentThreadableLoader::isAllowedByContentSecurityPolicy): Deleted.
     50        * loader/DocumentThreadableLoader.h:
     51        (WebCore::DocumentThreadableLoader::options):
     52        (WebCore::DocumentThreadableLoader::isLoading):
     53        (WebCore::DocumentThreadableLoader::document):
     54
    1552016-06-10  Adam Bergkvist  <adam.bergkvist@ericsson.com>
    256
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r201898 r201924  
    15701570                41A3D58E101C152D00316D07 /* DedicatedWorkerThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41A3D58C101C152D00316D07 /* DedicatedWorkerThread.cpp */; };
    15711571                41A3D58F101C152D00316D07 /* DedicatedWorkerThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 41A3D58D101C152D00316D07 /* DedicatedWorkerThread.h */; };
     1572                41ABE67B1D0580DB006D862D /* CrossOriginPreflightChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 41ABE67A1D0580D5006D862D /* CrossOriginPreflightChecker.h */; };
     1573                41ABE67C1D0580E0006D862D /* CrossOriginPreflightChecker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41ABE6791D0580D5006D862D /* CrossOriginPreflightChecker.cpp */; };
    15721574                41AD753A1CEF6BD100A31486 /* FetchOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 41AD75391CEF6BCE00A31486 /* FetchOptions.h */; settings = {ATTRIBUTES = (Private, ); }; };
    15731575                41BF700C0FE86F49005E8DEC /* MessagePortChannel.h in Headers */ = {isa = PBXBuildFile; fileRef = 41BF700A0FE86F49005E8DEC /* MessagePortChannel.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    90849086                41A3D58C101C152D00316D07 /* DedicatedWorkerThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DedicatedWorkerThread.cpp; sourceTree = "<group>"; };
    90859087                41A3D58D101C152D00316D07 /* DedicatedWorkerThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DedicatedWorkerThread.h; sourceTree = "<group>"; };
     9088                41ABE6791D0580D5006D862D /* CrossOriginPreflightChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CrossOriginPreflightChecker.cpp; sourceTree = "<group>"; };
     9089                41ABE67A1D0580D5006D862D /* CrossOriginPreflightChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CrossOriginPreflightChecker.h; sourceTree = "<group>"; };
    90869090                41AD75391CEF6BCE00A31486 /* FetchOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FetchOptions.h; sourceTree = "<group>"; };
    90879091                41BF700A0FE86F49005E8DEC /* MessagePortChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessagePortChannel.h; sourceTree = "<group>"; };
     
    2276122765                                E1C416160F6563180092D2FB /* CrossOriginAccessControl.cpp */,
    2276222766                                E1C416110F6562FD0092D2FB /* CrossOriginAccessControl.h */,
     22767                                41ABE6791D0580D5006D862D /* CrossOriginPreflightChecker.cpp */,
     22768                                41ABE67A1D0580D5006D862D /* CrossOriginPreflightChecker.h */,
    2276322769                                E1C415DD0F655D7C0092D2FB /* CrossOriginPreflightResultCache.cpp */,
    2276422770                                E1C415D90F655D6F0092D2FB /* CrossOriginPreflightResultCache.h */,
     
    2559925605                                26A807851B18F97700E219BE /* DFACombiner.h in Headers */,
    2560025606                                26A517FE1AB92238006335DF /* DFAMinimizer.h in Headers */,
     25607                                41ABE67B1D0580DB006D862D /* CrossOriginPreflightChecker.h in Headers */,
    2560125608                                267725FF1A5B3AD9003C24DD /* DFANode.h in Headers */,
    2560225609                                CD19A2681A13E700008D650E /* DiagnosticLoggingClient.h in Headers */,
     
    2970329710                                BC1A37BF097C715F0019F3D8 /* DOMUtility.mm in Sources */,
    2970429711                                15C770A5100D41CD005BA267 /* DOMValidityState.mm in Sources */,
     29712                                41ABE67C1D0580E0006D862D /* CrossOriginPreflightChecker.cpp in Sources */,
    2970529713                                31C0FF4A0E4CEFDD007D6FE5 /* DOMWebKitAnimationEvent.mm in Sources */,
    2970629714                                3106037A143281CD00ABF4BA /* DOMWebKitCSSFilterValue.mm in Sources */,
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp

    r201856 r201924  
    3838#include "ContentSecurityPolicy.h"
    3939#include "CrossOriginAccessControl.h"
     40#include "CrossOriginPreflightChecker.h"
    4041#include "CrossOriginPreflightResultCache.h"
    4142#include "Document.h"
     
    7172{
    7273    RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, request, options, WTFMove(contentSecurityPolicy)));
    73     if (!loader->m_resource)
     74    if (!loader->isLoading())
    7475        loader = nullptr;
    7576    return loader;
     
    112113    ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
    113114
    114     auto crossOriginRequest = std::make_unique<ResourceRequest>(request);
    115     updateRequestForAccessControl(*crossOriginRequest, securityOrigin(), m_options.allowCredentials());
    116 
    117     if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(crossOriginRequest->httpMethod(), crossOriginRequest->httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight)
    118         makeSimpleCrossOriginAccessRequest(*crossOriginRequest);
     115    auto crossOriginRequest = request;
     116    updateRequestForAccessControl(crossOriginRequest, securityOrigin(), m_options.allowCredentials());
     117
     118    if ((m_options.preflightPolicy == ConsiderPreflight && isSimpleCrossOriginAccessRequest(crossOriginRequest.httpMethod(), crossOriginRequest.httpHeaderFields())) || m_options.preflightPolicy == PreventPreflight)
     119        makeSimpleCrossOriginAccessRequest(crossOriginRequest);
    119120    else {
    120121        m_simpleRequest = false;
    121         m_actualRequest = WTFMove(crossOriginRequest);
    122 
    123         if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin()->toString(), m_actualRequest->url(), m_options.allowCredentials(), m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields()))
    124             preflightSuccess();
     122        if (CrossOriginPreflightResultCache::singleton().canSkipPreflight(securityOrigin()->toString(), crossOriginRequest.url(), m_options.allowCredentials(), crossOriginRequest.httpMethod(), crossOriginRequest.httpHeaderFields()))
     123            preflightSuccess(WTFMove(crossOriginRequest));
    125124        else
    126             makeCrossOriginAccessRequestWithPreflight(*m_actualRequest);
     125            makeCrossOriginAccessRequestWithPreflight(WTFMove(crossOriginRequest));
    127126    }
    128127}
     
    142141}
    143142
    144 void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request)
    145 {
    146     ResourceRequest preflightRequest = createAccessControlPreflightRequest(request, securityOrigin());
    147     loadRequest(preflightRequest, DoSecurityCheck);
     143void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
     144{
     145    if (m_async) {
     146        m_preflightChecker = CrossOriginPreflightChecker(*this, WTFMove(request));
     147        m_preflightChecker->startPreflight();
     148        return;
     149    }
     150    CrossOriginPreflightChecker::doPreflight(*this, WTFMove(request));
    148151}
    149152
     
    172175    if (m_resource)
    173176        m_resource->setDefersLoading(value);
     177    if (m_preflightChecker)
     178        m_preflightChecker->setDefersLoading(value);
    174179}
    175180
     
    184189        resource->removeClient(this);
    185190    }
     191    if (m_preflightChecker)
     192        m_preflightChecker = Nullopt;
    186193}
    187194
     
    261268
    262269    String accessControlErrorDescription;
    263     if (m_actualRequest) {
    264         DocumentLoader* loader = m_document.frame()->loader().documentLoader();
    265         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_document.frame());
    266         InspectorInstrumentation::didReceiveResourceResponse(cookie, identifier, loader, response, 0);
    267 
     270    if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
    268271        if (!passesAccessControlCheck(response, m_options.allowCredentials(), securityOrigin(), accessControlErrorDescription)) {
    269             preflightFailure(identifier, response.url(), accessControlErrorDescription);
     272            m_client->didFailAccessControlCheck(ResourceError(errorDomainWebKitInternal, 0, response.url(), accessControlErrorDescription));
    270273            return;
    271274        }
    272 
    273         StoredCredentials allowCredentials = m_options.allowCredentials();
    274         auto preflightResult = std::make_unique<CrossOriginPreflightResultCacheItem>(allowCredentials);
    275         if (!preflightResult->parse(response, accessControlErrorDescription)
    276             || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription)
    277             || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) {
    278             preflightFailure(identifier, response.url(), accessControlErrorDescription);
    279             return;
    280         }
    281 
    282         CrossOriginPreflightResultCache::singleton().appendEntry(securityOrigin()->toString(), m_actualRequest->url(), WTFMove(preflightResult));
    283     } else {
    284         if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) {
    285             if (!passesAccessControlCheck(response, m_options.allowCredentials(), securityOrigin(), accessControlErrorDescription)) {
    286                 m_client->didFailAccessControlCheck(ResourceError(errorDomainWebKitInternal, 0, response.url(), accessControlErrorDescription));
    287                 return;
    288             }
    289         }
    290 
    291         m_client->didReceiveResponse(identifier, response);
    292     }
     275    }
     276
     277    m_client->didReceiveResponse(identifier, response);
    293278}
    294279
     
    299284}
    300285
    301 void DocumentThreadableLoader::didReceiveData(unsigned long identifier, const char* data, int dataLength)
    302 {
    303     ASSERT(m_client);
    304 
    305     // Preflight data should be invisible to clients.
    306     if (m_actualRequest) {
    307         InspectorInstrumentation::didReceiveData(m_document.frame(), identifier, 0, 0, dataLength);
    308         return;
    309     }
     286void DocumentThreadableLoader::didReceiveData(unsigned long, const char* data, int dataLength)
     287{
     288    ASSERT(m_client);
    310289
    311290    m_client->didReceiveData(data, dataLength);
     
    316295    ASSERT(m_client);
    317296    ASSERT_UNUSED(resource, resource == m_resource);
    318        
     297
    319298    if (m_resource->errorOccurred())
    320299        didFail(m_resource->identifier(), m_resource->resourceError());
     
    325304void DocumentThreadableLoader::didFinishLoading(unsigned long identifier, double finishTime)
    326305{
    327     if (m_actualRequest) {
    328         InspectorInstrumentation::didFinishLoading(m_document.frame(), m_document.frame()->loader().documentLoader(), identifier, finishTime);
    329 
    330         ASSERT(!m_sameOriginRequest);
    331         ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl);
    332         preflightSuccess();
    333     } else
    334         m_client->didFinishLoading(identifier, finishTime);
    335 }
    336 
    337 void DocumentThreadableLoader::didFail(unsigned long identifier, const ResourceError& error)
    338 {
    339     if (m_actualRequest)
    340         InspectorInstrumentation::didFailLoading(m_document.frame(), m_document.frame()->loader().documentLoader(), identifier, error);
    341 
     306    ASSERT(m_client);
     307    m_client->didFinishLoading(identifier, finishTime);
     308}
     309
     310void DocumentThreadableLoader::didFail(unsigned long, const ResourceError& error)
     311{
     312    ASSERT(m_client);
    342313    m_client->didFail(error);
    343314}
    344315
    345 void DocumentThreadableLoader::preflightSuccess()
    346 {
    347     std::unique_ptr<ResourceRequest> actualRequest;
    348     actualRequest.swap(m_actualRequest);
    349 
    350     actualRequest->setHTTPOrigin(securityOrigin()->toString());
    351 
    352     clearResource();
     316void DocumentThreadableLoader::preflightSuccess(ResourceRequest&& request)
     317{
     318    ResourceRequest actualRequest(WTFMove(request));
     319    actualRequest.setHTTPOrigin(securityOrigin()->toString());
     320
     321    m_preflightChecker = Nullopt;
    353322
    354323    // It should be ok to skip the security check since we already asked about the preflight request.
    355     loadRequest(*actualRequest, SkipSecurityCheck);
    356 }
    357 
    358 void DocumentThreadableLoader::preflightFailure(unsigned long identifier, const URL& url, const String& errorDescription)
    359 {
    360     ResourceError error(errorDomainWebKitInternal, 0, url, errorDescription);
    361     if (m_actualRequest)
    362         InspectorInstrumentation::didFailLoading(m_document.frame(), m_document.frame()->loader().documentLoader(), identifier, error);
    363 
    364     m_actualRequest = nullptr; // Prevent didFinishLoading() from bypassing access check.
     324    loadRequest(actualRequest, SkipSecurityCheck);
     325}
     326
     327void DocumentThreadableLoader::preflightFailure(unsigned long identifier, const ResourceError& error)
     328{
     329    m_preflightChecker = Nullopt;
     330
     331    InspectorInstrumentation::didFailLoading(m_document.frame(), m_document.frame()->loader().documentLoader(), identifier, error);
     332
     333    ASSERT(m_client);
    365334    m_client->didFailAccessControlCheck(error);
    366335}
     
    377346        ThreadableLoaderOptions options = m_options;
    378347        options.setClientCredentialPolicy(DoNotAskClientForCrossOriginCredentials);
    379         if (m_actualRequest) {
    380             // Don't sniff content or send load callbacks for the preflight request.
    381             options.setSendLoadCallbacks(DoNotSendCallbacks);
    382             options.setSniffContent(DoNotSniffContent);
    383             // Keep buffering the data for the preflight request.
    384             options.setDataBufferingPolicy(BufferData);
    385         }
    386348
    387349        CachedResourceRequest newRequest(request, options);
     
    395357        return;
    396358    }
    397    
     359
    398360    // FIXME: ThreadableLoaderOptions.sniffContent is not supported for synchronous requests.
    399361    RefPtr<SharedBuffer> data;
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.h

    r201414 r201924  
    3232#define DocumentThreadableLoader_h
    3333
    34 #include "CachedRawResourceClient.h"
    35 #include "CachedResourceHandle.h"
     34#include "CrossOriginPreflightChecker.h"
    3635#include "ThreadableLoader.h"
    3736
     
    4039    class ContentSecurityPolicy;
    4140    class Document;
    42     class URL;
    43     class ResourceRequest;
    4441    class SecurityOrigin;
    4542    class ThreadableLoaderClient;
     
    5855        void cancel() override;
    5956        virtual void setDefersLoading(bool);
     57
     58        friend CrossOriginPreflightChecker;
    6059
    6160        using RefCounted<DocumentThreadableLoader>::ref;
     
    8887        void didFail(unsigned long identifier, const ResourceError&);
    8988        void makeCrossOriginAccessRequest(const ResourceRequest&);
    90         void makeSimpleCrossOriginAccessRequest(const ResourceRequest& request);
    91         void makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request);
    92         void preflightSuccess();
    93         void preflightFailure(unsigned long identifier, const URL&, const String& errorDescription);
     89        void makeSimpleCrossOriginAccessRequest(const ResourceRequest&);
     90        void makeCrossOriginAccessRequestWithPreflight(ResourceRequest&&);
     91        void preflightSuccess(ResourceRequest&&);
     92        void preflightFailure(unsigned long identifier, const ResourceError&);
    9493
    9594        void loadRequest(const ResourceRequest&, SecurityCheckPolicy);
     
    102101        const ContentSecurityPolicy& contentSecurityPolicy() const;
    103102
     103        Document& document() { return m_document; }
     104        const ThreadableLoaderOptions& options() const { return m_options; }
     105        bool isLoading() { return m_resource || m_preflightChecker; }
     106
    104107        CachedResourceHandle<CachedRawResource> m_resource;
    105108        ThreadableLoaderClient* m_client;
     
    109112        bool m_simpleRequest;
    110113        bool m_async;
    111         std::unique_ptr<ResourceRequest> m_actualRequest; // non-null during Access Control preflight checks
    112114        std::unique_ptr<ContentSecurityPolicy> m_contentSecurityPolicy;
     115        Optional<CrossOriginPreflightChecker> m_preflightChecker;
    113116    };
    114117
Note: See TracChangeset for help on using the changeset viewer.