WebFrameLoaderClient.mm   [plain text]


/*
 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer. 
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution. 
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#import "WebFrameLoaderClient.h"

#import "BackForwardList.h"
#import "DOMElementInternal.h"
#import "DOMHTMLFormElementInternal.h"
#import "WebBackForwardList.h"
#import "WebBasePluginPackage.h"
#import "WebCachedFramePlatformData.h"
#import "WebChromeClient.h"
#import "WebDataSourceInternal.h"
#import "WebDelegateImplementationCaching.h"
#import "WebDocumentInternal.h"
#import "WebDocumentLoaderMac.h"
#import "WebDownload.h"
#import "WebDynamicScrollBarsViewInternal.h"
#import "WebElementDictionary.h"
#import "WebFormDelegate.h"
#import "WebFrameInternal.h"
#import "WebFrameLoadDelegatePrivate.h"
#import "WebFrameNetworkingContext.h"
#import "WebFrameViewInternal.h"
#import "WebHTMLRepresentationPrivate.h"
#import "WebHTMLViewInternal.h"
#import "WebHistoryDelegate.h"
#import "WebHistoryInternal.h"
#import "WebHistoryItemInternal.h"
#import "WebKitErrorsPrivate.h"
#import "WebKitLogging.h"
#import "WebKitNSStringExtras.h"
#import "WebKitVersionChecks.h"
#import "WebNSURLExtras.h"
#import "WebNavigationData.h"
#import "WebNetscapePluginPackage.h"
#import "WebNetscapePluginView.h"
#import "WebPanelAuthenticationHandler.h"
#import "WebPluginController.h"
#import "WebPluginPackage.h"
#import "WebPluginViewFactoryPrivate.h"
#import "WebPolicyDelegate.h"
#import "WebPolicyDelegatePrivate.h"
#import "WebPreferences.h"
#import "WebResourceLoadDelegate.h"
#import "WebResourceLoadDelegatePrivate.h"
#import "WebScriptWorldInternal.h"
#import "WebSecurityOriginInternal.h"
#import "WebUIDelegate.h"
#import "WebUIDelegatePrivate.h"
#import "WebViewInternal.h"
#import <JavaScriptCore/InitializeThreading.h>
#import <JavaScriptCore/JSContextInternal.h>
#import <WebCore/AuthenticationMac.h>
#import <WebCore/BackForwardController.h>
#import <WebCore/BitmapImage.h>
#import <WebCore/CachedFrame.h>
#import <WebCore/Chrome.h>
#import <WebCore/DNS.h>
#import <WebCore/Document.h>
#import <WebCore/DocumentLoader.h>
#import <WebCore/EventHandler.h>
#import <WebCore/EventNames.h>
#import <WebCore/FocusController.h>
#import <WebCore/FormState.h>
#import <WebCore/Frame.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/FrameLoaderStateMachine.h>
#import <WebCore/FrameLoaderTypes.h>
#import <WebCore/FrameTree.h>
#import <WebCore/FrameView.h>
#import <WebCore/HTMLAppletElement.h>
#import <WebCore/HTMLFormElement.h>
#import <WebCore/HTMLFrameElement.h>
#import <WebCore/HTMLFrameOwnerElement.h>
#import <WebCore/HTMLNames.h>
#import <WebCore/HTMLParserIdioms.h>
#import <WebCore/HTMLPlugInElement.h>
#import <WebCore/HistoryController.h>
#import <WebCore/HistoryItem.h>
#import <WebCore/HitTestResult.h>
#import <WebCore/LoaderNSURLExtras.h>
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/MouseEvent.h>
#import <WebCore/Page.h>
#import <WebCore/PluginBlocklist.h>
#import <WebCore/PluginViewBase.h>
#import <WebCore/ProtectionSpace.h>
#import <WebCore/ResourceError.h>
#import <WebCore/ResourceHandle.h>
#import <WebCore/ResourceRequest.h>
#import <WebCore/RuntimeApplicationChecks.h>
#import <WebCore/ScriptController.h>
#import <WebCore/SharedBuffer.h>
#import <WebCore/SubresourceLoader.h>
#import <WebCore/WebCoreObjCExtras.h>
#import <WebCore/WebGLBlocklist.h>
#import <WebCore/WebScriptObjectPrivate.h>
#import <WebCore/Widget.h>
#import <WebKitLegacy/DOMElement.h>
#import <WebKitLegacy/DOMHTMLFormElement.h>
#import <pal/spi/cocoa/NSURLDownloadSPI.h>
#import <pal/spi/cocoa/NSURLFileTypeMappingsSPI.h>
#import <wtf/BlockObjCExceptions.h>
#import <wtf/MainThread.h>
#import <wtf/NakedPtr.h>
#import <wtf/Ref.h>
#import <wtf/RunLoop.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/text/WTFString.h>

#if USE(APPLE_INTERNAL_SDK)
#import <WebKitAdditions/WebFrameLoaderClientAdditions.mm>
#endif

#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
#import "NetscapePluginHostManager.h"
#import "WebHostedNetscapePluginView.h"
#endif

#if PLATFORM(IOS_FAMILY)
#import <WebCore/HTMLPlugInImageElement.h>
#import <WebCore/WAKClipView.h>
#import <WebCore/WAKScrollView.h>
#import <WebCore/WAKWindow.h>
#import <WebCore/WebCoreThreadMessage.h>
#import "WebMailDelegate.h"
#import "WebUIKitDelegate.h"
#endif

#if USE(QUICK_LOOK)
#import <WebCore/LegacyPreviewLoaderClient.h>
#import <WebCore/QuickLook.h>
#import <pal/spi/cocoa/NSFileManagerSPI.h>
#endif

#if HAVE(APP_LINKS)
#import <WebCore/WebCoreThreadRun.h>
#import <pal/spi/cocoa/LaunchServicesSPI.h>
#endif

#if ENABLE(CONTENT_FILTERING)
#import <WebCore/PolicyChecker.h>
#endif

#if PLATFORM(IOS_FAMILY)
@interface WebHTMLView (Init)
- (id)initWithFrame:(CGRect)frame;
@end
#endif

// For backwards compatibility with older WebKit plug-ins.
NSString *WebPluginBaseURLKey = @"WebPluginBaseURL";
NSString *WebPluginAttributesKey = @"WebPluginAttributes";
NSString *WebPluginContainerKey = @"WebPluginContainer";

@interface WebFramePolicyListener : NSObject <WebPolicyDecisionListener, WebFormSubmissionListener> {
    RefPtr<WebCore::Frame> _frame;
    WebCore::PolicyCheckIdentifier _identifier;
    WebCore::FramePolicyFunction _policyFunction;
#if HAVE(APP_LINKS)
    RetainPtr<NSURL> _appLinkURL;
#endif
    WebCore::PolicyAction _defaultPolicy;
}

- (id)initWithFrame:(NakedPtr<WebCore::Frame>)frame identifier:(WebCore::PolicyCheckIdentifier)identifier policyFunction:(WebCore::FramePolicyFunction&&)policyFunction defaultPolicy:(WebCore::PolicyAction)defaultPolicy;
#if HAVE(APP_LINKS)
- (id)initWithFrame:(NakedPtr<WebCore::Frame>)frame identifier:(WebCore::PolicyCheckIdentifier)identifier policyFunction:(WebCore::FramePolicyFunction&&)policyFunction defaultPolicy:(WebCore::PolicyAction)defaultPolicy appLinkURL:(NSURL *)url;
#endif

- (void)invalidate;

@end

WebDataSource *dataSource(WebCore::DocumentLoader* loader)
{
    return loader ? static_cast<WebDocumentLoaderMac*>(loader)->dataSource() : nil;
}

WebFrameLoaderClient::WebFrameLoaderClient(WebFrame *webFrame)
    : m_webFrame(webFrame)
{
}

Optional<WebCore::PageIdentifier> WebFrameLoaderClient::pageID() const
{
    return WTF::nullopt;
}

Optional<WebCore::FrameIdentifier> WebFrameLoaderClient::frameID() const
{
    return WTF::nullopt;
}

WebFrameLoaderClient::~WebFrameLoaderClient()
{
    [m_webFrame.get() _clearCoreFrame];
}

bool WebFrameLoaderClient::hasWebView() const
{
    return [m_webFrame.get() webView] != nil;
}

void WebFrameLoaderClient::makeRepresentation(WebCore::DocumentLoader* loader)
{
    [dataSource(loader) _makeRepresentation];
}

bool WebFrameLoaderClient::hasHTMLView() const
{
    NSView <WebDocumentView> *view = [m_webFrame->_private->webFrameView documentView];
    return [view isKindOfClass:[WebHTMLView class]];
}

#if PLATFORM(IOS_FAMILY)
bool WebFrameLoaderClient::forceLayoutOnRestoreFromBackForwardCache()
{
    NSView <WebDocumentView> *view = [m_webFrame->_private->webFrameView documentView];
    // This gets called to lay out a page restored from the back/forward cache.
    // To work around timing problems with UIKit, restore fixed 
    // layout settings here.
    WebView* webView = getWebView(m_webFrame.get());
    bool isMainFrame = [webView mainFrame] == m_webFrame.get();
    auto* coreFrame = core(m_webFrame.get());
    if (isMainFrame && coreFrame->view()) {
        WebCore::IntSize newSize([webView _fixedLayoutSize]);
        coreFrame->view()->setFixedLayoutSize(newSize);
        coreFrame->view()->setUseFixedLayout(!newSize.isEmpty());
    }
    [view setNeedsLayout:YES];
    [view layout];
    return true;
}
#endif

void WebFrameLoaderClient::forceLayoutForNonHTML()
{
    WebFrameView *thisView = m_webFrame->_private->webFrameView;
    NSView <WebDocumentView> *thisDocumentView = [thisView documentView];
    ASSERT(thisDocumentView != nil);
    
    // Tell the just loaded document to layout.  This may be necessary
    // for non-html content that needs a layout message.
    if (!([[m_webFrame.get() _dataSource] _isDocumentHTML])) {
        [thisDocumentView setNeedsLayout:YES];
        [thisDocumentView layout];
        [thisDocumentView setNeedsDisplay:YES];
    }
}

void WebFrameLoaderClient::setCopiesOnScroll()
{
    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    [[[m_webFrame->_private->webFrameView _scrollView] contentView] setCopiesOnScroll:YES];
    ALLOW_DEPRECATED_DECLARATIONS_END
}

void WebFrameLoaderClient::detachedFromParent2()
{
    //remove any NetScape plugins that are children of this frame because they are about to be detached
    WebView *webView = getWebView(m_webFrame.get());
#if !PLATFORM(IOS_FAMILY)
    [webView removePluginInstanceViewsFor:(m_webFrame.get())];
#endif
    [m_webFrame->_private->webFrameView _setWebFrame:nil]; // needed for now to be compatible w/ old behavior

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didRemoveFrameFromHierarchyFunc)
        CallFrameLoadDelegate(implementations->didRemoveFrameFromHierarchyFunc, webView, @selector(webView:didRemoveFrameFromHierarchy:), m_webFrame.get());
}

void WebFrameLoaderClient::detachedFromParent3()
{
    [m_webFrame->_private->webFrameView release];
    m_webFrame->_private->webFrameView = nil;
}

void WebFrameLoaderClient::convertMainResourceLoadToDownload(WebCore::DocumentLoader* documentLoader, const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response)
{
    WebView *webView = getWebView(m_webFrame.get());
    auto* mainResourceLoader = documentLoader->mainResourceLoader();

    if (!mainResourceLoader) {
        // The resource has already been cached, or the conversion is being attmpted when not calling SubresourceLoader::didReceiveResponse().
        ALLOW_DEPRECATED_DECLARATIONS_BEGIN
        WebDownload *webDownload = [[WebDownload alloc] initWithRequest:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody) delegate:[webView downloadDelegate]];
        ALLOW_DEPRECATED_DECLARATIONS_END
        [webDownload autorelease];
        return;
    }

    auto* handle = mainResourceLoader->handle();

ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    [WebDownload _downloadWithLoadingConnection:handle->connection() request:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody) response:response.nsURLResponse() delegate:[webView downloadDelegate] proxy:nil];
ALLOW_DEPRECATED_DECLARATIONS_END
}

bool WebFrameLoaderClient::dispatchDidLoadResourceFromMemoryCache(WebCore::DocumentLoader* loader, const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, int length)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);
#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidLoadResourceFromMemoryCacheFunc) {
        CallResourceLoadDelegateInWebThread(implementations->webThreadDidLoadResourceFromMemoryCacheFunc, webView, @selector(webThreadWebView:didLoadResourceFromMemoryCache:response:length:fromDataSource:), request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody), response.nsURLResponse(), length, dataSource(loader));
        return true;
    } 
#endif
    if (!implementations->didLoadResourceFromMemoryCacheFunc)
        return false;

    CallResourceLoadDelegate(implementations->didLoadResourceFromMemoryCacheFunc, webView, @selector(webView:didLoadResourceFromMemoryCache:response:length:fromDataSource:), request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody), response.nsURLResponse(), length, dataSource(loader));
    return true;
}

void WebFrameLoaderClient::assignIdentifierToInitialRequest(unsigned long identifier, WebCore::DocumentLoader* loader, const WebCore::ResourceRequest& request)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

    RetainPtr<id> object;

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadIdentifierForRequestFunc) {
        object = CallResourceLoadDelegateInWebThread(implementations->webThreadIdentifierForRequestFunc, webView, @selector(webThreadWebView:identifierForInitialRequest:fromDataSource:), request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody), dataSource(loader));
    } else
#endif
    if (implementations->identifierForRequestFunc)
        object = CallResourceLoadDelegate(implementations->identifierForRequestFunc, webView, @selector(webView:identifierForInitialRequest:fromDataSource:), request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody), dataSource(loader));
    else
        object = adoptNS([[NSObject alloc] init]);

    [webView _addObject:object.get() forIdentifier:identifier];
}

void WebFrameLoaderClient::dispatchWillSendRequest(WebCore::DocumentLoader* loader, unsigned long identifier, WebCore::ResourceRequest& request, const WebCore::ResourceResponse& redirectResponse)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

    if (redirectResponse.isNull())
        static_cast<WebDocumentLoaderMac*>(loader)->increaseLoadCount(identifier);

    NSURLRequest *currentURLRequest = request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);

#if PLATFORM(MAC)
    if (WebCore::MacApplication::isAppleMail() && loader->substituteData().isValid()) {
        // Mail.app checks for this property to detect data / archive loads.
        [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)currentURLRequest];
    }
#endif

    NSURLRequest *newURLRequest = currentURLRequest;
#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadWillSendRequestFunc) {
        newURLRequest = (NSURLRequest *)CallResourceLoadDelegateInWebThread(implementations->webThreadWillSendRequestFunc, webView, @selector(webThreadWebView:resource:willSendRequest:redirectResponse:fromDataSource:), [webView _objectForIdentifier:identifier], currentURLRequest, redirectResponse.nsURLResponse(), dataSource(loader));
    } else
#endif
    if (implementations->willSendRequestFunc)
        newURLRequest = (NSURLRequest *)CallResourceLoadDelegate(implementations->willSendRequestFunc, webView, @selector(webView:resource:willSendRequest:redirectResponse:fromDataSource:), [webView _objectForIdentifier:identifier], currentURLRequest, redirectResponse.nsURLResponse(), dataSource(loader));

    if (newURLRequest != currentURLRequest)
        request.updateFromDelegatePreservingOldProperties(WebCore::ResourceRequest(newURLRequest));
}

bool WebFrameLoaderClient::shouldUseCredentialStorage(WebCore::DocumentLoader* loader, unsigned long identifier)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

    if (implementations->shouldUseCredentialStorageFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            return CallResourceLoadDelegateReturningBoolean(NO, implementations->shouldUseCredentialStorageFunc, webView, @selector(webView:resource:shouldUseCredentialStorageForDataSource:), resource, dataSource(loader));
    }

    return true;
}

void WebFrameLoaderClient::dispatchDidReceiveAuthenticationChallenge(WebCore::DocumentLoader* loader, unsigned long identifier, const WebCore::AuthenticationChallenge& challenge)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

    NSURLAuthenticationChallenge *webChallenge = mac(challenge);

    if (implementations->didReceiveAuthenticationChallengeFunc) {
        if (id resource = [webView _objectForIdentifier:identifier]) {
            CallResourceLoadDelegate(implementations->didReceiveAuthenticationChallengeFunc, webView, @selector(webView:resource:didReceiveAuthenticationChallenge:fromDataSource:), resource, webChallenge, dataSource(loader));
            return;
        }
    }

#if !PLATFORM(IOS_FAMILY)
    NSWindow *window = [webView hostWindow] ? [webView hostWindow] : [webView window];
    [[WebPanelAuthenticationHandler sharedHandler] startAuthentication:webChallenge window:window];
#endif
}

#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
bool WebFrameLoaderClient::canAuthenticateAgainstProtectionSpace(WebCore::DocumentLoader* loader, unsigned long identifier, const WebCore::ProtectionSpace& protectionSpace)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);
    
    NSURLProtectionSpace *webProtectionSpace = protectionSpace.nsSpace();
    
    if (implementations->canAuthenticateAgainstProtectionSpaceFunc) {
        if (id resource = [webView _objectForIdentifier:identifier]) {
            return CallResourceLoadDelegateReturningBoolean(NO, implementations->canAuthenticateAgainstProtectionSpaceFunc, webView, @selector(webView:resource:canAuthenticateAgainstProtectionSpace:forDataSource:), resource, webProtectionSpace, dataSource(loader));
        }
    }

    // If our resource load delegate doesn't handle the question, then only send authentication
    // challenges for pre-iOS-3.0, pre-10.6 protection spaces.  This is the same as the default implementation
    // in CFNetwork.
    return (protectionSpace.authenticationScheme() < WebCore::ProtectionSpaceAuthenticationSchemeClientCertificateRequested);
}
#endif

#if PLATFORM(IOS_FAMILY)
RetainPtr<CFDictionaryRef> WebFrameLoaderClient::connectionProperties(WebCore::DocumentLoader* loader, unsigned long identifier)
{
    WebView *webView = getWebView(m_webFrame.get());
    id resource = [webView _objectForIdentifier:identifier];
    if (!resource)
        return nullptr;

    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);
    if (implementations->connectionPropertiesFunc)
        return (CFDictionaryRef)CallResourceLoadDelegate(implementations->connectionPropertiesFunc, webView, @selector(webView:connectionPropertiesForResource:dataSource:), resource, dataSource(loader));

    return nullptr;
}
#endif

bool WebFrameLoaderClient::shouldPaintBrokenImage(const URL& imageURL) const
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

    if (implementations->shouldPaintBrokenImageForURLFunc) {
        NSURL* url = imageURL;
        return CallResourceLoadDelegateReturningBoolean(YES, implementations->shouldPaintBrokenImageForURLFunc, webView, @selector(webView:shouldPaintBrokenImageForURL:), url);
    }
    return true;
}

void WebFrameLoaderClient::dispatchDidReceiveResponse(WebCore::DocumentLoader* loader, unsigned long identifier, const WebCore::ResourceResponse& response)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidReceiveResponseFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegateInWebThread(implementations->webThreadDidReceiveResponseFunc, webView, @selector(webThreadWebView:resource:didReceiveResponse:fromDataSource:), resource, response.nsURLResponse(), dataSource(loader));
        
    } else
#endif
    if (implementations->didReceiveResponseFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegate(implementations->didReceiveResponseFunc, webView, @selector(webView:resource:didReceiveResponse:fromDataSource:), resource, response.nsURLResponse(), dataSource(loader));
    }
}

void WebFrameLoaderClient::willCacheResponse(WebCore::DocumentLoader* loader, unsigned long identifier, NSCachedURLResponse* response, CompletionHandler<void(NSCachedURLResponse *)>&& completionHandler) const
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadWillCacheResponseFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            return completionHandler(CallResourceLoadDelegateInWebThread(implementations->webThreadWillCacheResponseFunc, webView, @selector(webThreadWebView:resource:willCacheResponse:fromDataSource:), resource, response, dataSource(loader)));
        
    } else
#endif
    if (implementations->willCacheResponseFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            return completionHandler(CallResourceLoadDelegate(implementations->willCacheResponseFunc, webView, @selector(webView:resource:willCacheResponse:fromDataSource:), resource, response, dataSource(loader)));
    }

    completionHandler(response);
}

void WebFrameLoaderClient::dispatchDidReceiveContentLength(WebCore::DocumentLoader* loader, unsigned long identifier, int dataLength)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);
#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidReceiveContentLengthFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegateInWebThread(implementations->webThreadDidReceiveContentLengthFunc, webView, @selector(webThreadWebView:resource:didReceiveContentLength:fromDataSource:), resource, (NSInteger)dataLength, dataSource(loader));
    } else
#endif
    if (implementations->didReceiveContentLengthFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegate(implementations->didReceiveContentLengthFunc, webView, @selector(webView:resource:didReceiveContentLength:fromDataSource:), resource, (NSInteger)dataLength, dataSource(loader));
    }
}

#if ENABLE(DATA_DETECTION)
void WebFrameLoaderClient::dispatchDidFinishDataDetection(NSArray *)
{
}
#endif

void WebFrameLoaderClient::dispatchDidFinishLoading(WebCore::DocumentLoader* loader, unsigned long identifier)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidFinishLoadingFromDataSourceFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegateInWebThread(implementations->webThreadDidFinishLoadingFromDataSourceFunc, webView, @selector(webThreadWebView:resource:didFinishLoadingFromDataSource:), resource, dataSource(loader));
    } else
#endif

    if (implementations->didFinishLoadingFromDataSourceFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegate(implementations->didFinishLoadingFromDataSourceFunc, webView, @selector(webView:resource:didFinishLoadingFromDataSource:), resource, dataSource(loader));
    }

    [webView _removeObjectForIdentifier:identifier];

    static_cast<WebDocumentLoaderMac*>(loader)->decreaseLoadCount(identifier);
}

void WebFrameLoaderClient::dispatchDidFailLoading(WebCore::DocumentLoader* loader, unsigned long identifier, const WebCore::ResourceError& error)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidFailLoadingWithErrorFromDataSourceFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegateInWebThread(implementations->webThreadDidFailLoadingWithErrorFromDataSourceFunc, webView, @selector(webThreadWebView:resource:didFailLoadingWithError:fromDataSource:), resource, (NSError *)error, dataSource(loader));
    } else
#endif
    if (implementations->didFailLoadingWithErrorFromDataSourceFunc) {
        if (id resource = [webView _objectForIdentifier:identifier])
            CallResourceLoadDelegate(implementations->didFailLoadingWithErrorFromDataSourceFunc, webView, @selector(webView:resource:didFailLoadingWithError:fromDataSource:), resource, (NSError *)error, dataSource(loader));
    }

    [webView _removeObjectForIdentifier:identifier];

    static_cast<WebDocumentLoaderMac*>(loader)->decreaseLoadCount(identifier);
}

void WebFrameLoaderClient::dispatchDidDispatchOnloadEvents()
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didHandleOnloadEventsForFrameFunc)
        CallFrameLoadDelegate(implementations->didHandleOnloadEventsForFrameFunc, webView, @selector(webView:didHandleOnloadEventsForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad()
{
    m_webFrame->_private->provisionalURL = core(m_webFrame.get())->loader().provisionalDocumentLoader()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didReceiveServerRedirectForProvisionalLoadForFrameFunc)
        CallFrameLoadDelegate(implementations->didReceiveServerRedirectForProvisionalLoadForFrameFunc, webView, @selector(webView:didReceiveServerRedirectForProvisionalLoadForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidCancelClientRedirect()
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didCancelClientRedirectForFrameFunc)
        CallFrameLoadDelegate(implementations->didCancelClientRedirectForFrameFunc, webView, @selector(webView:didCancelClientRedirectForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchWillPerformClientRedirect(const URL& url, double delay, WallTime fireDate, WebCore::LockBackForwardList)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->willPerformClientRedirectToURLDelayFireDateForFrameFunc) {
        NSURL *cocoaURL = url;
        CallFrameLoadDelegate(implementations->willPerformClientRedirectToURLDelayFireDateForFrameFunc, webView, @selector(webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:), cocoaURL, delay, [NSDate dateWithTimeIntervalSince1970:fireDate.secondsSinceEpoch().seconds()], m_webFrame.get());
    }
}

void WebFrameLoaderClient::dispatchDidChangeLocationWithinPage()
{
    m_webFrame->_private->url = core(m_webFrame.get())->document()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didChangeLocationWithinPageForFrameFunc)
        CallFrameLoadDelegate(implementations->didChangeLocationWithinPageForFrameFunc, webView, @selector(webView:didChangeLocationWithinPageForFrame:), m_webFrame.get());
#if PLATFORM(IOS_FAMILY)
    [[webView _UIKitDelegateForwarder] webView:webView didChangeLocationWithinPageForFrame:m_webFrame.get()];
#endif
}

void WebFrameLoaderClient::dispatchDidPushStateWithinPage()
{
    m_webFrame->_private->url = core(m_webFrame.get())->document()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didPushStateWithinPageForFrameFunc)
        CallFrameLoadDelegate(implementations->didPushStateWithinPageForFrameFunc, webView, @selector(webView:didPushStateWithinPageForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidReplaceStateWithinPage()
{
    m_webFrame->_private->url = core(m_webFrame.get())->document()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didReplaceStateWithinPageForFrameFunc)
        CallFrameLoadDelegate(implementations->didReplaceStateWithinPageForFrameFunc, webView, @selector(webView:didReplaceStateWithinPageForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidPopStateWithinPage()
{
    m_webFrame->_private->url = core(m_webFrame.get())->document()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didPopStateWithinPageForFrameFunc)
        CallFrameLoadDelegate(implementations->didPopStateWithinPageForFrameFunc, webView, @selector(webView:didPopStateWithinPageForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchWillClose()
{
    WebView *webView = getWebView(m_webFrame.get());   
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->willCloseFrameFunc)
        CallFrameLoadDelegate(implementations->willCloseFrameFunc, webView, @selector(webView:willCloseFrame:), m_webFrame.get());
#if PLATFORM(IOS_FAMILY)
    [[webView _UIKitDelegateForwarder] webView:webView willCloseFrame:m_webFrame.get()];
#endif
}

void WebFrameLoaderClient::dispatchDidStartProvisionalLoad()
{
    ASSERT(!m_webFrame->_private->provisionalURL);
    m_webFrame->_private->provisionalURL = core(m_webFrame.get())->loader().provisionalDocumentLoader()->url().string();

    WebView *webView = getWebView(m_webFrame.get());
#if !PLATFORM(IOS_FAMILY)
    [webView _didStartProvisionalLoadForFrame:m_webFrame.get()];
#endif

#if PLATFORM(IOS_FAMILY)
    [[webView _UIKitDelegateForwarder] webView:webView didStartProvisionalLoadForFrame:m_webFrame.get()];
#endif

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didStartProvisionalLoadForFrameFunc)
        CallFrameLoadDelegate(implementations->didStartProvisionalLoadForFrameFunc, webView, @selector(webView:didStartProvisionalLoadForFrame:), m_webFrame.get());
}

static constexpr unsigned maxTitleLength = 1000; // Closest power of 10 above the W3C recommendation for Title length.

void WebFrameLoaderClient::dispatchDidReceiveTitle(const WebCore::StringWithDirection& title)
{
    auto truncatedTitle = truncateFromEnd(title, maxTitleLength);

    WebView *webView = getWebView(m_webFrame.get());   
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didReceiveTitleForFrameFunc) {
        // FIXME: Use direction of title.
        CallFrameLoadDelegate(implementations->didReceiveTitleForFrameFunc, webView, @selector(webView:didReceiveTitle:forFrame:), (NSString *)truncatedTitle.string, m_webFrame.get());
    }
}

void WebFrameLoaderClient::dispatchDidCommitLoad(Optional<WebCore::HasInsecureContent>, Optional<WebCore::UsedLegacyTLS>)
{
    // Tell the client we've committed this URL.
    ASSERT([m_webFrame->_private->webFrameView documentView] != nil);
    
    WebView *webView = getWebView(m_webFrame.get());   
    [webView _didCommitLoadForFrame:m_webFrame.get()];

    m_webFrame->_private->url = m_webFrame->_private->provisionalURL;
    m_webFrame->_private->provisionalURL = nullptr;

#if PLATFORM(IOS_FAMILY)
    [[webView _UIKitDelegateForwarder] webView:webView didCommitLoadForFrame:m_webFrame.get()];
#endif
    
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didCommitLoadForFrameFunc)
        CallFrameLoadDelegate(implementations->didCommitLoadForFrameFunc, webView, @selector(webView:didCommitLoadForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidFailProvisionalLoad(const WebCore::ResourceError& error, WebCore::WillContinueLoading)
{
    m_webFrame->_private->provisionalURL = nullptr;

    WebView *webView = getWebView(m_webFrame.get());
#if !PLATFORM(IOS_FAMILY)
    [webView _didFailProvisionalLoadWithError:error forFrame:m_webFrame.get()];
#endif

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didFailProvisionalLoadWithErrorForFrameFunc)
        CallFrameLoadDelegate(implementations->didFailProvisionalLoadWithErrorForFrameFunc, webView, @selector(webView:didFailProvisionalLoadWithError:forFrame:), (NSError *)error, m_webFrame.get());

    [m_webFrame->_private->internalLoadDelegate webFrame:m_webFrame.get() didFinishLoadWithError:error];
}

void WebFrameLoaderClient::dispatchDidFailLoad(const WebCore::ResourceError& error)
{
    ASSERT(!m_webFrame->_private->provisionalURL);

    WebView *webView = getWebView(m_webFrame.get());
#if !PLATFORM(IOS_FAMILY)
    [webView _didFailLoadWithError:error forFrame:m_webFrame.get()];
#endif

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didFailLoadWithErrorForFrameFunc)
        CallFrameLoadDelegate(implementations->didFailLoadWithErrorForFrameFunc, webView, @selector(webView:didFailLoadWithError:forFrame:), (NSError *)error, m_webFrame.get());
#if PLATFORM(IOS_FAMILY)
    [[webView _UIKitDelegateForwarder] webView:webView didFailLoadWithError:((NSError *)error) forFrame:m_webFrame.get()];
#endif

    [m_webFrame->_private->internalLoadDelegate webFrame:m_webFrame.get() didFinishLoadWithError:error];
}

void WebFrameLoaderClient::dispatchDidFinishDocumentLoad()
{
    WebView *webView = getWebView(m_webFrame.get());

#if PLATFORM(IOS_FAMILY)
    id webThreadDel = [webView _webMailDelegate];
    if ([webThreadDel respondsToSelector:@selector(_webthread_webView:didFinishDocumentLoadForFrame:)]) {
        [webThreadDel _webthread_webView:webView didFinishDocumentLoadForFrame:m_webFrame.get()];
    }
#endif

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didFinishDocumentLoadForFrameFunc)
        CallFrameLoadDelegate(implementations->didFinishDocumentLoadForFrameFunc, webView, @selector(webView:didFinishDocumentLoadForFrame:), m_webFrame.get());
}

void WebFrameLoaderClient::dispatchDidFinishLoad()
{
    ASSERT(!m_webFrame->_private->provisionalURL);

    WebView *webView = getWebView(m_webFrame.get());
#if !PLATFORM(IOS_FAMILY)
    [webView _didFinishLoadForFrame:m_webFrame.get()];
#else
    [[webView _UIKitDelegateForwarder] webView:webView didFinishLoadForFrame:m_webFrame.get()];

    id webThreadDel = [webView _webMailDelegate];
    if ([webThreadDel respondsToSelector:@selector(_webthread_webView:didFinishLoadForFrame:)]) {
        [webThreadDel _webthread_webView:webView didFinishLoadForFrame:m_webFrame.get()];
    }
#endif

    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didFinishLoadForFrameFunc)
        CallFrameLoadDelegate(implementations->didFinishLoadForFrameFunc, webView, @selector(webView:didFinishLoadForFrame:), m_webFrame.get());

    [m_webFrame->_private->internalLoadDelegate webFrame:m_webFrame.get() didFinishLoadWithError:nil];
}

void WebFrameLoaderClient::dispatchDidReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone> milestones)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);

#if PLATFORM(IOS_FAMILY)
    if (implementations->webThreadDidLayoutFunc)
        CallFrameLoadDelegateInWebThread(implementations->webThreadDidLayoutFunc, webView, @selector(webThreadWebView:didLayout:), kitLayoutMilestones(milestones));
#else
    if (implementations->didLayoutFunc)
        CallFrameLoadDelegate(implementations->didLayoutFunc, webView, @selector(webView:didLayout:), kitLayoutMilestones(milestones));
#endif

    if (milestones & WebCore::DidFirstLayout) {
        // FIXME: We should consider removing the old didFirstLayout API since this is doing double duty with the
        // new didLayout API.
        if (implementations->didFirstLayoutInFrameFunc)
            CallFrameLoadDelegate(implementations->didFirstLayoutInFrameFunc, webView, @selector(webView:didFirstLayoutInFrame:), m_webFrame.get());

#if PLATFORM(IOS_FAMILY)
        [[webView _UIKitDelegateForwarder] webView:webView didFirstLayoutInFrame:m_webFrame.get()];
#endif
 
        // See WebFrameLoaderClient::provisionalLoadStarted.
        WebDynamicScrollBarsView *scrollView = [m_webFrame->_private->webFrameView _scrollView];
        if ([getWebView(m_webFrame.get()) drawsBackground])
            [scrollView setDrawsBackground:YES];
#if !PLATFORM(IOS_FAMILY)
        [scrollView setVerticalScrollElasticity:NSScrollElasticityAutomatic];
        [scrollView setHorizontalScrollElasticity:NSScrollElasticityAutomatic];
#endif
    }

    if (milestones & WebCore::DidFirstVisuallyNonEmptyLayout) {
        // FIXME: We should consider removing the old didFirstVisuallyNonEmptyLayoutForFrame API since this is doing
        // double duty with the new didLayout API.
        if (implementations->didFirstVisuallyNonEmptyLayoutInFrameFunc)
            CallFrameLoadDelegate(implementations->didFirstVisuallyNonEmptyLayoutInFrameFunc, webView, @selector(webView:didFirstVisuallyNonEmptyLayoutInFrame:), m_webFrame.get());
#if PLATFORM(IOS_FAMILY)
        if ([webView mainFrame] == m_webFrame.get())
            [[webView _UIKitDelegateForwarder] webView:webView didFirstVisuallyNonEmptyLayoutInFrame:m_webFrame.get()];
#endif
    }
}

WebCore::Frame* WebFrameLoaderClient::dispatchCreatePage(const WebCore::NavigationAction&)
{
    WebView *currentWebView = getWebView(m_webFrame.get());
    NSDictionary *features = [[NSDictionary alloc] init];
    WebView *newWebView = [[currentWebView _UIDelegateForwarder] webView:currentWebView 
                                                createWebViewWithRequest:nil
                                                          windowFeatures:features];
    [features release];
    
#if USE(PLUGIN_HOST_PROCESS) && ENABLE(NETSCAPE_PLUGIN_API)
    if (newWebView)
        WebKit::NetscapePluginHostManager::singleton().didCreateWindow();
#endif
        
    return core([newWebView mainFrame]);
}

void WebFrameLoaderClient::dispatchShow()
{
    WebView *webView = getWebView(m_webFrame.get());
    [[webView _UIDelegateForwarder] webViewShow:webView];
}

void WebFrameLoaderClient::dispatchDecidePolicyForResponse(const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, WebCore::PolicyCheckIdentifier identifier, const String&, WebCore::FramePolicyFunction&& function)
{
    WebView *webView = getWebView(m_webFrame.get());

    [[webView _policyDelegateForwarder] webView:webView
        decidePolicyForMIMEType:response.mimeType()
        request:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody)
        frame:m_webFrame.get()
        decisionListener:setUpPolicyListener(identifier, WTFMove(function), WebCore::PolicyAction::Use).get()];
}


static BOOL shouldTryAppLink(WebView *webView, const WebCore::NavigationAction& action, WebCore::Frame* targetFrame)
{
#if HAVE(APP_LINKS)
    BOOL mainFrameNavigation = !targetFrame || targetFrame->isMainFrame();
    if (!mainFrameNavigation)
        return NO;

    if (!action.processingUserGesture())
        return NO;

    if (targetFrame && targetFrame->document() && targetFrame->document()->url().host() == action.url().host())
        return NO;

    return YES;
#else
    return NO;
#endif
}

void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, WebCore::FormState* formState, const String& frameName, WebCore::PolicyCheckIdentifier identifier, WebCore::FramePolicyFunction&& function)
{
    WebView *webView = getWebView(m_webFrame.get());
    BOOL tryAppLink = shouldTryAppLink(webView, action, nullptr);

    [[webView _policyDelegateForwarder] webView:webView
        decidePolicyForNewWindowAction:actionDictionary(action, formState)
        request:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody)
        newFrameName:frameName
        decisionListener:setUpPolicyListener(identifier, WTFMove(function), WebCore::PolicyAction::Ignore, tryAppLink ? (NSURL *)request.url() : nil).get()];
}

void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction& action, const WebCore::ResourceRequest& request, const WebCore::ResourceResponse&, WebCore::FormState* formState, WebCore::PolicyDecisionMode, WebCore::PolicyCheckIdentifier identifier, WebCore::FramePolicyFunction&& function)
{
    WebView *webView = getWebView(m_webFrame.get());
    BOOL tryAppLink = shouldTryAppLink(webView, action, core(m_webFrame.get()));

    [[webView _policyDelegateForwarder] webView:webView
        decidePolicyForNavigationAction:actionDictionary(action, formState)
        request:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody)
        frame:m_webFrame.get()
        decisionListener:setUpPolicyListener(identifier, WTFMove(function), WebCore::PolicyAction::Ignore, tryAppLink ? (NSURL *)request.url() : nil).get()];
}

void WebFrameLoaderClient::cancelPolicyCheck()
{
    if (!m_policyListener)
        return;

    [m_policyListener invalidate];
    m_policyListener = nil;
}

void WebFrameLoaderClient::dispatchUnableToImplementPolicy(const WebCore::ResourceError& error)
{
    WebView *webView = getWebView(m_webFrame.get());
    [[webView _policyDelegateForwarder] webView:webView unableToImplementPolicyWithError:error frame:m_webFrame.get()];    
}

static NSDictionary *makeFormFieldValuesDictionary(WebCore::FormState& formState)
{
    auto& textFieldValues = formState.textFieldValues();
    size_t size = textFieldValues.size();
    NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:size];
    for (auto& value : textFieldValues)
        [dictionary setObject:value.second forKey:value.first];
    return [dictionary autorelease];
}

void WebFrameLoaderClient::dispatchWillSendSubmitEvent(Ref<WebCore::FormState>&& formState)
{
    id <WebFormDelegate> formDelegate = [getWebView(m_webFrame.get()) _formDelegate];
    if (!formDelegate)
        return;

    DOMHTMLFormElement *formElement = kit(&formState->form());
    NSDictionary *values = makeFormFieldValuesDictionary(formState.get());
    CallFormDelegate(getWebView(m_webFrame.get()), @selector(willSendSubmitEventToForm:inFrame:withValues:), formElement, m_webFrame.get(), values);
}

void WebFrameLoaderClient::dispatchWillSubmitForm(WebCore::FormState& formState, CompletionHandler<void()>&& completionHandler)
{
    id <WebFormDelegate> formDelegate = [getWebView(m_webFrame.get()) _formDelegate];
    if (!formDelegate) {
        completionHandler();
        return;
    }

    NSDictionary *values = makeFormFieldValuesDictionary(formState);
    CallFormDelegate(getWebView(m_webFrame.get()), @selector(frame:sourceFrame:willSubmitForm:withValues:submissionListener:), m_webFrame.get(), kit(formState.sourceDocument().frame()), kit(&formState.form()), values, setUpPolicyListener(WebCore::PolicyCheckIdentifier { },
        [completionHandler = WTFMove(completionHandler)](WebCore::PolicyAction, WebCore::PolicyCheckIdentifier) mutable { completionHandler(); },
        WebCore::PolicyAction::Ignore).get());
}

void WebFrameLoaderClient::revertToProvisionalState(WebCore::DocumentLoader* loader)
{
    [dataSource(loader) _revertToProvisionalState];
}

void WebFrameLoaderClient::setMainDocumentError(WebCore::DocumentLoader* loader, const WebCore::ResourceError& error)
{
    [dataSource(loader) _setMainDocumentError:error];
}

void WebFrameLoaderClient::setMainFrameDocumentReady(bool ready)
{
    [getWebView(m_webFrame.get()) setMainFrameDocumentReady:ready];
}

void WebFrameLoaderClient::startDownload(const WebCore::ResourceRequest& request, const String& /* suggestedName */)
{
    // FIXME: Should download full request.
    [getWebView(m_webFrame.get()) _downloadURL:request.url()];
}

void WebFrameLoaderClient::willChangeTitle(WebCore::DocumentLoader* loader)
{
#if !PLATFORM(IOS_FAMILY)
    // FIXME: Should do this only in main frame case, right?
    [getWebView(m_webFrame.get()) _willChangeValueForKey:_WebMainFrameTitleKey];
#endif
}

void WebFrameLoaderClient::didChangeTitle(WebCore::DocumentLoader* loader)
{
#if !PLATFORM(IOS_FAMILY)
    // FIXME: Should do this only in main frame case, right?
    [getWebView(m_webFrame.get()) _didChangeValueForKey:_WebMainFrameTitleKey];
#endif
}

void WebFrameLoaderClient::didReplaceMultipartContent()
{
#if PLATFORM(IOS_FAMILY)
    if (WebCore::FrameView *view = core(m_webFrame.get())->view())
        view->didReplaceMultipartContent();
#endif
}

void WebFrameLoaderClient::committedLoad(WebCore::DocumentLoader* loader, const char* data, int length)
{
    NSData *nsData = [[NSData alloc] initWithBytesNoCopy:(void*)data length:length freeWhenDone:NO];
    [dataSource(loader) _receivedData:nsData];
    [nsData release];
}

void WebFrameLoaderClient::finishedLoading(WebCore::DocumentLoader* loader)
{
    [dataSource(loader) _finishedLoading];
}

static inline NSString *nilOrNSString(const String& string)
{
    if (string.isNull())
        return nil;
    return string;
}

void WebFrameLoaderClient::updateGlobalHistory()
{
    WebView* view = getWebView(m_webFrame.get());
    auto* loader = core(m_webFrame.get())->loader().documentLoader();
#if PLATFORM(IOS_FAMILY)
    if (loader->urlForHistory() == aboutBlankURL())
        return;
#endif

    if ([view historyDelegate]) {
        WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(view);
        if (implementations->navigatedFunc) {
            WebNavigationData *data = [[WebNavigationData alloc] initWithURLString:loader->url().string()
                title:nilOrNSString(loader->title().string)
                originalRequest:loader->originalRequestCopy().nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody)
                response:loader->response().nsURLResponse()
                hasSubstituteData:loader->substituteData().isValid()
                clientRedirectSource:loader->clientRedirectSourceForHistory()];

            CallHistoryDelegate(implementations->navigatedFunc, view, @selector(webView:didNavigateWithNavigationData:inFrame:), data, m_webFrame.get());
            [data release];
        }
    
        return;
    }

    [[WebHistory optionalSharedHistory] _visitedURL:loader->urlForHistory() withTitle:loader->title().string method:loader->originalRequestCopy().httpMethod() wasFailure:loader->urlForHistoryReflectsFailure()];
}

static void addRedirectURL(WebHistoryItem *item, const String& url)
{
    if (!item->_private->_redirectURLs)
        item->_private->_redirectURLs = makeUnique<Vector<String>>();

    // Our API allows us to store all the URLs in the redirect chain, but for
    // now we only have a use for the final URL.
    item->_private->_redirectURLs->resize(1);
    item->_private->_redirectURLs->at(0) = url;
}

void WebFrameLoaderClient::updateGlobalHistoryRedirectLinks()
{
    WebView* view = getWebView(m_webFrame.get());
    WebHistoryDelegateImplementationCache* implementations = [view historyDelegate] ? WebViewGetHistoryDelegateImplementations(view) : 0;
    
    auto* loader = core(m_webFrame.get())->loader().documentLoader();
    ASSERT(loader->unreachableURL().isEmpty());

    if (!loader->clientRedirectSourceForHistory().isNull()) {
        if (implementations) {
            if (implementations->clientRedirectFunc) {
                CallHistoryDelegate(implementations->clientRedirectFunc, view, @selector(webView:didPerformClientRedirectFromURL:toURL:inFrame:), 
                    m_webFrame->_private->url.get(), loader->clientRedirectDestinationForHistory(), m_webFrame.get());
            }
        } else if (WebHistoryItem *item = [[WebHistory optionalSharedHistory] _itemForURLString:loader->clientRedirectSourceForHistory()])
            addRedirectURL(item, loader->clientRedirectDestinationForHistory());
    }

    if (!loader->serverRedirectSourceForHistory().isNull()) {
        if (implementations) {
            if (implementations->serverRedirectFunc) {
                CallHistoryDelegate(implementations->serverRedirectFunc, view, @selector(webView:didPerformServerRedirectFromURL:toURL:inFrame:), 
                    loader->serverRedirectSourceForHistory(), loader->serverRedirectDestinationForHistory(), m_webFrame.get());
            }
        } else if (WebHistoryItem *item = [[WebHistory optionalSharedHistory] _itemForURLString:loader->serverRedirectSourceForHistory()])
            addRedirectURL(item, loader->serverRedirectDestinationForHistory());
    }
}

bool WebFrameLoaderClient::shouldGoToHistoryItem(WebCore::HistoryItem& item) const
{
    WebView* view = getWebView(m_webFrame.get());
    return [[view _policyDelegateForwarder] webView:view shouldGoToHistoryItem:kit(&item)];
}

void WebFrameLoaderClient::didDisplayInsecureContent()
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didDisplayInsecureContentFunc)
        CallFrameLoadDelegate(implementations->didDisplayInsecureContentFunc, webView, @selector(webViewDidDisplayInsecureContent:));
}

void WebFrameLoaderClient::didRunInsecureContent(WebCore::SecurityOrigin& origin, const URL& insecureURL)
{
    WebView *webView = getWebView(m_webFrame.get());   
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didRunInsecureContentFunc) {
        RetainPtr<WebSecurityOrigin> webSecurityOrigin = adoptNS([[WebSecurityOrigin alloc] _initWithWebCoreSecurityOrigin:&origin]);
        CallFrameLoadDelegate(implementations->didRunInsecureContentFunc, webView, @selector(webView:didRunInsecureContent:), webSecurityOrigin.get());
    }
}

void WebFrameLoaderClient::didDetectXSS(const URL& insecureURL, bool didBlockEntirePage)
{
    WebView *webView = getWebView(m_webFrame.get());   
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
    if (implementations->didDetectXSSFunc) {
        // FIXME: must pass didBlockEntirePage if we want to do more on mac than just pass tests.
        NSURL* insecureNSURL = insecureURL;
        CallFrameLoadDelegate(implementations->didDetectXSSFunc, webView, @selector(webView:didDetectXSS:), insecureNSURL);
    }
}

WebCore::ResourceError WebFrameLoaderClient::cancelledError(const WebCore::ResourceRequest& request) const
{
    return [NSError _webKitErrorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled URL:request.url()];
}
    
WebCore::ResourceError WebFrameLoaderClient::blockedError(const WebCore::ResourceRequest& request) const
{
    return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorCannotUseRestrictedPort URL:request.url()];
}

WebCore::ResourceError WebFrameLoaderClient::blockedByContentBlockerError(const WebCore::ResourceRequest& request) const
{
    RELEASE_ASSERT_NOT_REACHED(); // Content blockers are not enabled in WebKit1.
}

WebCore::ResourceError WebFrameLoaderClient::cannotShowURLError(const WebCore::ResourceRequest& request) const
{
    return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorCannotShowURL URL:request.url()];
}

WebCore::ResourceError WebFrameLoaderClient::interruptedForPolicyChangeError(const WebCore::ResourceRequest& request) const
{
    return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:request.url()];
}

#if ENABLE(CONTENT_FILTERING)
WebCore::ResourceError WebFrameLoaderClient::blockedByContentFilterError(const WebCore::ResourceRequest& request) const
{
    return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadBlockedByContentFilter URL:request.url()];
}
#endif

WebCore::ResourceError WebFrameLoaderClient::cannotShowMIMETypeError(const WebCore::ResourceResponse& response) const
{
    return [NSError _webKitErrorWithDomain:NSURLErrorDomain code:WebKitErrorCannotShowMIMEType URL:response.url()];
}

WebCore::ResourceError WebFrameLoaderClient::fileDoesNotExistError(const WebCore::ResourceResponse& response) const
{
    return [NSError _webKitErrorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist URL:response.url()];    
}

WebCore::ResourceError WebFrameLoaderClient::pluginWillHandleLoadError(const WebCore::ResourceResponse& response) const
{
    NSError *error = [[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInWillHandleLoad
                                                    contentURL:response.url()
                                                 pluginPageURL:nil
                                                    pluginName:nil
                                                      MIMEType:response.mimeType()];
    return [error autorelease];
}

bool WebFrameLoaderClient::shouldFallBack(const WebCore::ResourceError& error) const
{
    // FIXME: Needs to check domain.
    // FIXME: WebKitErrorPlugInWillHandleLoad is a workaround for the cancel we do to prevent
    // loading plugin content twice.  See <rdar://problem/4258008>
    return error.errorCode() != NSURLErrorCancelled && error.errorCode() != WebKitErrorPlugInWillHandleLoad;
}

bool WebFrameLoaderClient::canHandleRequest(const WebCore::ResourceRequest& request) const
{
    return [WebView _canHandleRequest:request.nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody) forMainFrame:core(m_webFrame.get())->isMainFrame()];
}

bool WebFrameLoaderClient::canShowMIMEType(const String& MIMEType) const
{
    return [getWebView(m_webFrame.get()) _canShowMIMEType:MIMEType];
}

bool WebFrameLoaderClient::canShowMIMETypeAsHTML(const String& MIMEType) const
{
    return [WebView canShowMIMETypeAsHTML:MIMEType];
}

bool WebFrameLoaderClient::representationExistsForURLScheme(const String& URLScheme) const
{
    return [WebView _representationExistsForURLScheme:URLScheme];
}

String WebFrameLoaderClient::generatedMIMETypeForURLScheme(const String& URLScheme) const
{
    return [WebView _generatedMIMETypeForURLScheme:URLScheme];
}

void WebFrameLoaderClient::frameLoadCompleted()
{
    // Note: Can be called multiple times.

    // See WebFrameLoaderClient::provisionalLoadStarted.
    if ([getWebView(m_webFrame.get()) drawsBackground])
        [[m_webFrame->_private->webFrameView _scrollView] setDrawsBackground:YES];
}

void WebFrameLoaderClient::saveViewStateToItem(WebCore::HistoryItem& item)
{
#if PLATFORM(IOS_FAMILY)
    // Let UIKit handle the scroll point for the main frame.
    WebFrame *webFrame = m_webFrame.get();
    WebView *webView = getWebView(webFrame);   
    if (webFrame == [webView mainFrame]) {
        [[webView _UIKitDelegateForwarder] webView:webView saveStateToHistoryItem:kit(&item) forFrame:webFrame];
        return;
    }
#endif                    
    
    NSView <WebDocumentView> *docView = [m_webFrame->_private->webFrameView documentView];

    // we might already be detached when this is called from detachFromParent, in which
    // case we don't want to override real data earlier gathered with (0,0)
    if ([docView superview] && [docView conformsToProtocol:@protocol(_WebDocumentViewState)])
        item.setViewState([(id <_WebDocumentViewState>)docView viewState]);
}

void WebFrameLoaderClient::restoreViewState()
{
    WebCore::HistoryItem* currentItem = core(m_webFrame.get())->loader().history().currentItem();
    ASSERT(currentItem);

    // FIXME: As the ASSERT attests, it seems we should always have a currentItem here.
    // One counterexample is <rdar://problem/4917290>
    // For now, to cover this issue in release builds, there is no technical harm to returning
    // early and from a user standpoint - as in the above radar - the previous page load failed 
    // so there *is* no scroll state to restore!
    if (!currentItem)
        return;

#if PLATFORM(IOS_FAMILY)
    // Let UIKit handle the scroll point for the main frame.
    WebFrame *webFrame = m_webFrame.get();
    WebView *webView = getWebView(webFrame);   
    if (webFrame == [webView mainFrame]) {
        [[webView _UIKitDelegateForwarder] webView:webView restoreStateFromHistoryItem:kit(currentItem) forFrame:webFrame force:NO];
        return;
    }
#endif                    
    
    NSView <WebDocumentView> *docView = [m_webFrame->_private->webFrameView documentView];
    if ([docView conformsToProtocol:@protocol(_WebDocumentViewState)]) {        
        id state = currentItem->viewState();
        if (state) {
            [(id <_WebDocumentViewState>)docView setViewState:state];
        }
    }
}

void WebFrameLoaderClient::provisionalLoadStarted()
{    
    // Tell the scroll view not to draw a background so we can leave the contents of
    // the old page showing during the beginning of the loading process.

    // This will stay set to NO until:
    //    1) The load gets far enough along: WebFrameLoader::frameLoadCompleted.
    //    2) The window is resized: -[WebFrameView setFrameSize:].
    // or 3) The view is moved out of the window: -[WebFrameView viewDidMoveToWindow].
    // Please keep the comments in these four functions in agreement with each other.

    WebDynamicScrollBarsView *scrollView = [m_webFrame->_private->webFrameView _scrollView];
    [scrollView setDrawsBackground:NO];
#if !PLATFORM(IOS_FAMILY)
    [scrollView setVerticalScrollElasticity:NSScrollElasticityNone];
    [scrollView setHorizontalScrollElasticity:NSScrollElasticityNone];
#endif
}

void WebFrameLoaderClient::didFinishLoad()
{
    [m_webFrame->_private->internalLoadDelegate webFrame:m_webFrame.get() didFinishLoadWithError:nil];    
}

void WebFrameLoaderClient::prepareForDataSourceReplacement()
{
    m_activeIconLoadCallbackID = 0;

    if (![m_webFrame.get() _dataSource]) {
        ASSERT(!core(m_webFrame.get())->tree().childCount());
        return;
    }
    
#if !PLATFORM(IOS_FAMILY)
    // Make sure that any work that is triggered by resigning first reponder can get done.
    // The main example where this came up is the textDidEndEditing that is sent to the
    // FormsDelegate (3223413). We need to do this before _detachChildren, since that will
    // remove the views as a side-effect of freeing the frame, at which point we can't
    // post the FormDelegate messages.
    //
    // Note that this can also take FirstResponder away from a child of our frameView that
    // is not in a child frame's view.  This is OK because we are in the process
    // of loading new content, which will blow away all editors in this top frame, and if
    // a non-editor is firstReponder it will not be affected by endEditingFor:.
    // Potentially one day someone could write a DocView whose editors were not all
    // replaced by loading new content, but that does not apply currently.
    NSView *frameView = m_webFrame->_private->webFrameView;
    NSWindow *window = [frameView window];
    NSResponder *firstResp = [window firstResponder];
    if ([firstResp isKindOfClass:[NSView class]] && [(NSView *)firstResp isDescendantOf:frameView])
        [window endEditingFor:firstResp];
#endif
}

Ref<WebCore::DocumentLoader> WebFrameLoaderClient::createDocumentLoader(const WebCore::ResourceRequest& request, const WebCore::SubstituteData& substituteData)
{
    auto loader = WebDocumentLoaderMac::create(request, substituteData);

    WebDataSource *dataSource = [[WebDataSource alloc] _initWithDocumentLoader:loader.copyRef()];
    loader->setDataSource(dataSource, getWebView(m_webFrame.get()));
    [dataSource release];

    return WTFMove(loader);
}

void WebFrameLoaderClient::setTitle(const WebCore::StringWithDirection& title, const URL& url)
{
    WebView* view = getWebView(m_webFrame.get());
    
    if ([view historyDelegate]) {
        WebHistoryDelegateImplementationCache* implementations = WebViewGetHistoryDelegateImplementations(view);
        // FIXME: Use direction of title.
        if (implementations->setTitleFunc)
            CallHistoryDelegate(implementations->setTitleFunc, view, @selector(webView:updateHistoryTitle:forURL:inFrame:), (NSString *)title.string, (NSString *)url.string(), m_webFrame.get());
        else if (implementations->deprecatedSetTitleFunc) {
            IGNORE_WARNINGS_BEGIN("undeclared-selector")
            CallHistoryDelegate(implementations->deprecatedSetTitleFunc, view, @selector(webView:updateHistoryTitle:forURL:), (NSString *)title.string, (NSString *)url.string());
            IGNORE_WARNINGS_END
        }
        return;
    }

    NSURL* nsURL = url;
    nsURL = [nsURL _webkit_canonicalize];
    if(!nsURL)
        return;
#if PLATFORM(IOS_FAMILY)
    if ([[nsURL absoluteString] isEqualToString:@"about:blank"])
        return;
#endif
    [[[WebHistory optionalSharedHistory] itemForURL:nsURL] setTitle:title.string];
}

void WebFrameLoaderClient::savePlatformDataToCachedFrame(WebCore::CachedFrame* cachedFrame)
{
    cachedFrame->setCachedFramePlatformData(makeUnique<WebCachedFramePlatformData>(m_webFrame->_private->webFrameView.documentView));

#if PLATFORM(IOS_FAMILY)
    // At this point we know this frame is going to be cached. Stop all plugins.
    WebView *webView = getWebView(m_webFrame.get());
    [webView _stopAllPlugInsForPageCache];
#endif
}

void WebFrameLoaderClient::transitionToCommittedFromCachedFrame(WebCore::CachedFrame* cachedFrame)
{
    WebCachedFramePlatformData* platformData = static_cast<WebCachedFramePlatformData*>(cachedFrame->cachedFramePlatformData());
    NSView <WebDocumentView> *cachedView = platformData->webDocumentView();
    ASSERT(cachedView != nil);
    ASSERT(cachedFrame->documentLoader());
    [cachedView setDataSource:dataSource(cachedFrame->documentLoader())];
    
#if !PLATFORM(IOS_FAMILY)
    // clean up webkit plugin instances before WebHTMLView gets freed.
    WebView *webView = getWebView(m_webFrame.get());
    [webView removePluginInstanceViewsFor:(m_webFrame.get())];
#endif
    
    [m_webFrame->_private->webFrameView _setDocumentView:cachedView];
}

#if PLATFORM(IOS_FAMILY)
void WebFrameLoaderClient::didRestoreFrameHierarchyForCachedFrame()
{
    // When entering the PageCache the Document is detached and the plugin view may
    // have cleaned itself up (removing its webview and layer references). Now, when
    // restoring the page we want to recreate whatever is necessary.
    WebView *webView = getWebView(m_webFrame.get());
    [webView _restorePlugInsFromCache];
}
#endif

void WebFrameLoaderClient::transitionToCommittedForNewPage()
{
    WebDataSource *dataSource = [m_webFrame.get() _dataSource];

#if PLATFORM(IOS_FAMILY)
    bool willProduceHTMLView;
    // Fast path that skips initialization of objc class objects.
    if ([dataSource _documentLoader]->responseMIMEType() == "text/html")
        willProduceHTMLView = true;
    else
        willProduceHTMLView = [m_webFrame->_private->webFrameView _viewClassForMIMEType:[dataSource _responseMIMEType]] == [WebHTMLView class];
#else
    // FIXME (Viewless): I assume we want the equivalent of this optimization for viewless mode too.
    bool willProduceHTMLView = [m_webFrame->_private->webFrameView _viewClassForMIMEType:[dataSource _responseMIMEType]] == [WebHTMLView class];
#endif
    bool canSkipCreation = core(m_webFrame.get())->loader().stateMachine().committingFirstRealLoad() && willProduceHTMLView;
    if (canSkipCreation) {
        [[m_webFrame->_private->webFrameView documentView] setDataSource:dataSource];
        return;
    }

#if !PLATFORM(IOS_FAMILY)
    // Don't suppress scrollbars before the view creation if we're making the view for a non-HTML view.
    if (!willProduceHTMLView)
        [[m_webFrame->_private->webFrameView _scrollView] setScrollBarsSuppressed:NO repaintOnUnsuppress:NO];
    
    // clean up webkit plugin instances before WebHTMLView gets freed.
    WebView *webView = getWebView(m_webFrame.get());
    [webView removePluginInstanceViewsFor:(m_webFrame.get())];
    
    NSView <WebDocumentView> *documentView = [m_webFrame->_private->webFrameView _makeDocumentViewForDataSource:dataSource];
#else
    NSView <WebDocumentView> *documentView = nil;

    // Fast path that skips initialization of objc class objects.
    if (willProduceHTMLView) {
        documentView = [[WebHTMLView alloc] initWithFrame:[m_webFrame->_private->webFrameView bounds]];
        [m_webFrame->_private->webFrameView _setDocumentView:documentView];
        [documentView release];
    } else
        documentView = [m_webFrame->_private->webFrameView _makeDocumentViewForDataSource:dataSource];
#endif
    if (!documentView)
        return;

    // FIXME: Could we skip some of this work for a top-level view that is not a WebHTMLView?

    // If we own the view, delete the old one - otherwise the render m_frame will take care of deleting the view.
    auto* coreFrame = core(m_webFrame.get());
    auto* page = coreFrame->page();
    bool isMainFrame = coreFrame->isMainFrame();
    if (isMainFrame && coreFrame->view())
        coreFrame->view()->setParentVisible(false);
    coreFrame->setView(nullptr);
    auto coreView = WebCore::FrameView::create(*coreFrame);
    coreFrame->setView(coreView.copyRef());

#if PLATFORM(IOS_FAMILY)
    page->setDelegatesScaling(true);
#endif

    [m_webFrame.get() _updateBackgroundAndUpdatesWhileOffscreen];
    [m_webFrame->_private->webFrameView _install];

    if (isMainFrame) {
#if PLATFORM(IOS_FAMILY)
        coreView->setDelegatesScrolling(true);
#endif
        coreView->setParentVisible(true);
    }

    // Call setDataSource on the document view after it has been placed in the view hierarchy.
    // This what we for the top-level view, so should do this for views in subframes as well.
    [documentView setDataSource:dataSource];

    // The following is a no-op for WebHTMLRepresentation, but for custom document types
    // like the ones that Safari uses for bookmarks it is the only way the DocumentLoader
    // will get the proper title.
    if (auto documentLoader = [dataSource _documentLoader])
        documentLoader->setTitle({ [dataSource pageTitle], WebCore::TextDirection::LTR });

    if (auto* ownerElement = coreFrame->ownerElement())
        coreFrame->view()->setCanHaveScrollbars(ownerElement->scrollingMode() != WebCore::ScrollbarAlwaysOff);

    // If the document view implicitly became first responder, make sure to set the focused frame properly.
    if ([[documentView window] firstResponder] == documentView) {
        page->focusController().setFocusedFrame(coreFrame);
        page->focusController().setFocused(true);
    }
}

void WebFrameLoaderClient::didRestoreFromBackForwardCache()
{
#if PLATFORM(IOS_FAMILY)
    WebView *webView = getWebView(m_webFrame.get());
    if ([webView mainFrame] == m_webFrame.get())
        [[webView _UIKitDelegateForwarder] webViewDidRestoreFromPageCache:webView];
#endif
}

RetainPtr<WebFramePolicyListener> WebFrameLoaderClient::setUpPolicyListener(WebCore::PolicyCheckIdentifier identifier, WebCore::FramePolicyFunction&& function, WebCore::PolicyAction defaultPolicy, NSURL *appLinkURL)
{
    // FIXME: <rdar://5634381> We need to support multiple active policy listeners.
    [m_policyListener invalidate];

    RetainPtr<WebFramePolicyListener> policyListener;
#if HAVE(APP_LINKS)
    if (appLinkURL)
        policyListener = adoptNS([[WebFramePolicyListener alloc] initWithFrame:core(m_webFrame.get()) identifier:identifier policyFunction:WTFMove(function) defaultPolicy:defaultPolicy appLinkURL:appLinkURL]);
    else
#endif
        policyListener = adoptNS([[WebFramePolicyListener alloc] initWithFrame:core(m_webFrame.get()) identifier:identifier policyFunction:WTFMove(function) defaultPolicy:defaultPolicy]);

    m_policyListener = policyListener.get();

    return policyListener;
}

String WebFrameLoaderClient::userAgent(const URL& url) const
{
    WebView *webView = getWebView(m_webFrame.get());
    ASSERT(webView);

    // We should never get here with nil for the WebView unless there is a bug somewhere else.
    // But if we do, it's better to return the empty string than just crashing on the spot.
    // Most other call sites are tolerant of nil because of Objective-C behavior, but this one
    // is not because the return value of _userAgentForURL is a const URL&.
    if (!webView)
        return emptyString();

    return [webView _userAgentString];
}

NSDictionary *WebFrameLoaderClient::actionDictionary(const WebCore::NavigationAction& action, WebCore::FormState* formState) const
{
    using namespace WebCore;

    unsigned modifierFlags = 0;
#if !PLATFORM(IOS_FAMILY)
    auto keyStateEventData = action.keyStateEventData();
    if (keyStateEventData && keyStateEventData->isTrusted) {
        if (keyStateEventData->ctrlKey)
            modifierFlags |= NSEventModifierFlagControl;
        if (keyStateEventData->altKey)
            modifierFlags |= NSEventModifierFlagOption;
        if (keyStateEventData->shiftKey)
            modifierFlags |= NSEventModifierFlagShift;
        if (keyStateEventData->metaKey)
            modifierFlags |= NSEventModifierFlagCommand;
    }
#else
    // No modifier flags on iOS right now
    modifierFlags = 0;
#endif

    NSURL *originalURL = action.url();

    NSMutableDictionary *result = [NSMutableDictionary dictionaryWithObjectsAndKeys:
        @(static_cast<int>(action.type())), WebActionNavigationTypeKey,
        @(modifierFlags), WebActionModifierFlagsKey,
        originalURL, WebActionOriginalURLKey,
        nil];

    if (auto mouseEventData = action.mouseEventData()) {
        constexpr OptionSet<HitTestRequest::RequestType> hitType { HitTestRequest::ReadOnly, HitTestRequest::Active, HitTestRequest::DisallowUserAgentShadowContent, HitTestRequest::AllowChildFrameContent };
        WebElementDictionary *element = [[WebElementDictionary alloc]
            initWithHitTestResult:core(m_webFrame.get())->eventHandler().hitTestResultAtPoint(mouseEventData->absoluteLocation, hitType)];
        [result setObject:element forKey:WebActionElementKey];
        [element release];

        if (mouseEventData->isTrusted)
            [result setObject:@(mouseEventData->button) forKey:WebActionButtonKey];
        else
            [result setObject:@(WebCore::NoButton) forKey:WebActionButtonKey];
    }

    if (formState)
        [result setObject:kit(&formState->form()) forKey:WebActionFormKey];

    return result;
}

bool WebFrameLoaderClient::canCachePage() const
{
    // We can only cache HTML pages right now
    if (![[[m_webFrame _dataSource] representation] isKindOfClass:[WebHTMLRepresentation class]])
        return false;
    
    // We only cache pages if the back forward list is enabled and has a non-zero capacity.
    auto* page = core(m_webFrame.get())->page();
    if (!page)
        return false;
    
    auto& backForwardList = static_cast<BackForwardList&>(page->backForward().client());
    if (!backForwardList.enabled())
        return false;
    
    if (!backForwardList.capacity())
        return false;
    
    return true;
}

RefPtr<WebCore::Frame> WebFrameLoaderClient::createFrame(const String& name, WebCore::HTMLFrameOwnerElement& ownerElement)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS
    
    ASSERT(m_webFrame);
    
    WebFrameView *childView = [[WebFrameView alloc] init];
    
    RefPtr<WebCore::Frame> result = [WebFrame _createSubframeWithOwnerElement:&ownerElement frameName:name frameView:childView];
    [childView release];

    WebFrame *newFrame = kit(result.get());

    if ([newFrame _dataSource])
        [[newFrame _dataSource] _documentLoader]->setOverrideEncoding([[m_webFrame.get() _dataSource] _documentLoader]->overrideEncoding());  

    // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
    if (!result->page())
        return nullptr;
 
    return result;

    END_BLOCK_OBJC_EXCEPTIONS

    return nullptr;
}

WebCore::ObjectContentType WebFrameLoaderClient::objectContentType(const URL& url, const String& mimeType)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS

    String type = mimeType;

    if (type.isEmpty()) {
        // Try to guess the MIME type based off the extension.
        NSURL *URL = url;
        NSString *extension = [[URL path] pathExtension];
        if ([extension length] > 0) {
            type = [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:extension];
            if (type.isEmpty()) {
                // If no MIME type is specified, use a plug-in if we have one that can handle the extension.
                if ([getWebView(m_webFrame.get()) _pluginForExtension:extension])
                    return WebCore::ObjectContentType::PlugIn;
            }
        }
    }

    if (type.isEmpty())
        return WebCore::ObjectContentType::Frame; // Go ahead and hope that we can display the content.

    WebCore::ObjectContentType plugInType = WebCore::ObjectContentType::None;
    if ([getWebView(m_webFrame.get()) _pluginForMIMEType:type])
        plugInType = WebCore::ObjectContentType::PlugIn;

    if (WebCore::MIMETypeRegistry::isSupportedImageMIMEType(type))
        return WebCore::ObjectContentType::Image;

    if (plugInType != WebCore::ObjectContentType::None)
        return plugInType;

    if ([m_webFrame->_private->webFrameView _viewClassForMIMEType:type])
        return WebCore::ObjectContentType::Frame;
    
    return WebCore::ObjectContentType::None;

    END_BLOCK_OBJC_EXCEPTIONS

    return WebCore::ObjectContentType::None;
}

static String parameterValue(const Vector<String>& paramNames, const Vector<String>& paramValues, const char* name)
{
    size_t size = paramNames.size();
    ASSERT(size == paramValues.size());
    for (size_t i = 0; i < size; ++i) {
        if (equalIgnoringASCIICase(paramNames[i], name))
            return paramValues[i];
    }
    return String();
}

static NSView *pluginView(WebFrame *frame, WebPluginPackage *pluginPackage,
    NSArray *attributeNames, NSArray *attributeValues, NSURL *baseURL,
    DOMElement *element, BOOL loadManually)
{
    WebHTMLView *docView = (WebHTMLView *)[[frame frameView] documentView];
    ASSERT([docView isKindOfClass:[WebHTMLView class]]);

    WebPluginController *pluginController = [docView _pluginController];

    // Store attributes in a dictionary so they can be passed to WebPlugins.
    NSMutableDictionary *attributes = [[NSMutableDictionary alloc] initWithObjects:attributeValues forKeys:attributeNames];

    [pluginPackage load];
    Class viewFactory = [pluginPackage viewFactory];
    
    NSView *view = nil;
    NSDictionary *arguments = nil;

IGNORE_WARNINGS_BEGIN("undeclared-selector")
    auto oldSelector = @selector(pluginViewWithArguments:);
IGNORE_WARNINGS_END

    if ([viewFactory respondsToSelector:@selector(plugInViewWithArguments:)]) {
        arguments = @{
            WebPlugInBaseURLKey: baseURL,
            WebPlugInAttributesKey: attributes,
            WebPlugInContainerKey: pluginController,
            WebPlugInModeKey: @(loadManually ? WebPlugInModeFull : WebPlugInModeEmbed),
            WebPlugInShouldLoadMainResourceKey: [NSNumber numberWithBool:!loadManually],
            WebPlugInContainingElementKey: element,
        };
        LOG(Plugins, "arguments:\n%@", arguments);
    } else if ([viewFactory respondsToSelector:oldSelector]) {
        arguments = @{
            WebPluginBaseURLKey: baseURL,
            WebPluginAttributesKey: attributes,
            WebPluginContainerKey: pluginController,
            WebPlugInContainingElementKey: element,
        };
        LOG(Plugins, "arguments:\n%@", arguments);
    }

    view = [pluginController plugInViewWithArguments:arguments fromPluginPackage:pluginPackage];
    [attributes release];

    return view;
}

class PluginWidget : public WebCore::PluginViewBase {
public:
    PluginWidget(NSView *view = nil)
        : WebCore::PluginViewBase(view)
    {
    }
    
private:
    virtual void invalidateRect(const WebCore::IntRect& rect)
    {
        [platformWidget() setNeedsDisplayInRect:rect];
    }
};

#if PLATFORM(IOS_FAMILY)
@interface WAKView (UIKitSecretsWebKitKnowsAboutSeeUIWebPlugInView)
- (PlatformLayer *)pluginLayer;
- (BOOL)willProvidePluginLayer;
- (void)attachPluginLayer;
- (void)detachPluginLayer;
@end

class PluginWidgetIOS : public PluginWidget {
public:
    PluginWidgetIOS(WAKView *view)
        : PluginWidget(view)
    {
    }

    virtual PlatformLayer* platformLayer() const
    {
        if (![platformWidget() respondsToSelector:@selector(pluginLayer)])
            return nullptr;

        return [platformWidget() pluginLayer];   
    }

    virtual bool willProvidePluginLayer() const
    {
        return [platformWidget() respondsToSelector:@selector(willProvidePluginLayer)]
            && [platformWidget() willProvidePluginLayer];
    }

    virtual void attachPluginLayer()
    {
        if ([platformWidget() respondsToSelector:@selector(attachPluginLayer)])
            [platformWidget() attachPluginLayer];
    }

    virtual void detachPluginLayer()
    {
        if ([platformWidget() respondsToSelector:@selector(detachPluginLayer)])
            [platformWidget() detachPluginLayer];
    }
};
#endif // PLATFORM(IOS_FAMILY)

#if ENABLE(NETSCAPE_PLUGIN_API)

class NetscapePluginWidget : public PluginWidget {
public:
    NetscapePluginWidget(WebBaseNetscapePluginView *view)
        : PluginWidget(view)
    {
    }

    virtual PlatformLayer* platformLayer() const
    {
        return [(WebBaseNetscapePluginView *)platformWidget() pluginLayer];
    }

    virtual bool getFormValue(String& value)
    {
        NSString* nsValue = 0;
        if ([(WebBaseNetscapePluginView *)platformWidget() getFormValue:&nsValue]) {
            if (!nsValue)
                return false;
            value = String(nsValue);
            [nsValue release];
            return true;
        }
        return false;
    }

    virtual void handleEvent(WebCore::Event& event)
    {
        auto* frame = WebCore::Frame::frameForWidget(*this);
        if (!frame)
            return;
        
        NSEvent* currentNSEvent = frame->eventHandler().currentNSEvent();
        if (event.type() == WebCore::eventNames().mousemoveEvent)
            [(WebBaseNetscapePluginView *)platformWidget() handleMouseMoved:currentNSEvent];
        else if (event.type() == WebCore::eventNames().mouseoverEvent)
            [(WebBaseNetscapePluginView *)platformWidget() handleMouseEntered:currentNSEvent];
        else if (event.type() == WebCore::eventNames().mouseoutEvent)
            [(WebBaseNetscapePluginView *)platformWidget() handleMouseExited:currentNSEvent];
        else if (event.type() == WebCore::eventNames().contextmenuEvent)
            event.setDefaultHandled(); // We don't know if the plug-in has handled mousedown event by displaying a context menu, so we never want WebKit to show a default one.
    }

    virtual void clipRectChanged()
    {
        // Changing the clip rect doesn't affect the view hierarchy, so the plugin must be told about the change directly.
        [(WebBaseNetscapePluginView *)platformWidget() updateAndSetWindow];
    }

private:
    virtual void notifyWidget(WebCore::WidgetNotification notification)
    {
        switch (notification) {
        case WebCore::WillPaintFlattened: {
            BEGIN_BLOCK_OBJC_EXCEPTIONS
            [(WebBaseNetscapePluginView *)platformWidget() cacheSnapshot];
            END_BLOCK_OBJC_EXCEPTIONS
            break;
        }
        case WebCore::DidPaintFlattened: {
            BEGIN_BLOCK_OBJC_EXCEPTIONS
            [(WebBaseNetscapePluginView *)platformWidget() clearCachedSnapshot];
            END_BLOCK_OBJC_EXCEPTIONS
            break;
        }
        }
    }
};

#if USE(PLUGIN_HOST_PROCESS)
#define NETSCAPE_PLUGIN_VIEW WebHostedNetscapePluginView
#else
#define NETSCAPE_PLUGIN_VIEW WebNetscapePluginView
#endif

#endif // ENABLE(NETSCAPE_PLUGIN_API)

static bool shouldBlockPlugin(WebBasePluginPackage *pluginPackage)
{
#if PLATFORM(MAC)
    auto loadPolicy = WebCore::PluginBlocklist::loadPolicyForPluginVersion(pluginPackage.bundleIdentifier, pluginPackage.bundleVersion);
    return loadPolicy == WebCore::PluginBlocklist::LoadPolicy::BlockedForSecurity || loadPolicy == WebCore::PluginBlocklist::LoadPolicy::BlockedForCompatibility;
#else
    UNUSED_PARAM(pluginPackage);
    return false;
#endif
}

RefPtr<WebCore::Widget> WebFrameLoaderClient::createPlugin(const WebCore::IntSize& size, WebCore::HTMLPlugInElement& element, const URL& url,
    const Vector<String>& paramNames, const Vector<String>& paramValues, const String& mimeType, bool loadManually)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS

    ASSERT(paramNames.size() == paramValues.size());

    int errorCode = 0;

    WebView *webView = getWebView(m_webFrame.get());
    auto* document = core(m_webFrame.get())->document();
    NSURL *baseURL = document->baseURL();
    NSURL *pluginURL = url;
    auto attributeKeys = createNSArray(paramNames);

#if PLATFORM(MAC)
    SEL selector = @selector(webView:plugInViewWithArguments:);
    if ([[webView UIDelegate] respondsToSelector:selector]) {
        NSDictionary *attributes = [NSDictionary dictionaryWithObjects:createNSArray(paramValues).get() forKeys:attributeKeys.get()];
        NSDictionary *arguments = [[NSDictionary alloc] initWithObjectsAndKeys:
            attributes, WebPlugInAttributesKey,
            @(loadManually ? WebPlugInModeFull : WebPlugInModeEmbed), WebPlugInModeKey,
            [NSNumber numberWithBool:!loadManually], WebPlugInShouldLoadMainResourceKey,
            kit(&element), WebPlugInContainingElementKey,
            // FIXME: We should be passing base URL, see <https://bugs.webkit.org/show_bug.cgi?id=35215>.
            pluginURL, WebPlugInBaseURLKey, // pluginURL might be nil, so add it last
            nil];

        NSView *view = CallUIDelegate(webView, selector, arguments);

        [arguments release];

        if (view)
            return adoptRef(*new PluginWidget(view));
    }
#endif

    NSString *MIMEType;
    WebBasePluginPackage *pluginPackage;
    if (mimeType.isEmpty()) {
        MIMEType = nil;
        pluginPackage = nil;
    } else {
        MIMEType = mimeType;
        pluginPackage = [webView _pluginForMIMEType:mimeType];
    }

    NSString *extension = [[pluginURL path] pathExtension];

#if PLATFORM(IOS_FAMILY)
    if (!pluginPackage && [extension length])
#else
    if (!pluginPackage && [extension length] && ![MIMEType length])
#endif
    {
        pluginPackage = [webView _pluginForExtension:extension];
        if (pluginPackage) {
            NSString *newMIMEType = [pluginPackage MIMETypeForExtension:extension];
            if ([newMIMEType length] != 0)
                MIMEType = newMIMEType;
        }
    }

    NSView *view = nil;

    if (pluginPackage) {
        if (shouldBlockPlugin(pluginPackage)) {
            errorCode = WebKitErrorBlockedPlugInVersion;
            if (is<WebCore::RenderEmbeddedObject>(element.renderer()))
                downcast<WebCore::RenderEmbeddedObject>(*element.renderer()).setPluginUnavailabilityReason(WebCore::RenderEmbeddedObject::InsecurePluginVersion);
        } else {
            if ([pluginPackage isKindOfClass:[WebPluginPackage class]])
                view = pluginView(m_webFrame.get(), (WebPluginPackage *)pluginPackage, attributeKeys.get(), createNSArray(paramValues).get(), baseURL, kit(&element), loadManually);
#if ENABLE(NETSCAPE_PLUGIN_API)
            else if ([pluginPackage isKindOfClass:[WebNetscapePluginPackage class]]) {
                WebBaseNetscapePluginView *pluginView = [[[NETSCAPE_PLUGIN_VIEW alloc]
                    initWithFrame:NSMakeRect(0, 0, size.width(), size.height())
                    pluginPackage:(WebNetscapePluginPackage *)pluginPackage
                    URL:pluginURL
                    baseURL:baseURL
                    MIMEType:MIMEType
                    attributeKeys:attributeKeys.get()
                    attributeValues:createNSArray(paramValues).get()
                    loadManually:loadManually
                    element:&element] autorelease];

                return adoptRef(new NetscapePluginWidget(pluginView));
            }
#endif
        }
    } else
        errorCode = WebKitErrorCannotFindPlugIn;

    if (!errorCode && !view)
        errorCode = WebKitErrorCannotLoadPlugIn;
    
    if (errorCode && m_webFrame) {
        WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(webView);
        if (implementations->plugInFailedWithErrorFunc) {
            URL pluginPageURL = document->completeURL(WebCore::stripLeadingAndTrailingHTMLSpaces(parameterValue(paramNames, paramValues, "pluginspage")));
            if (!pluginPageURL.protocolIsInHTTPFamily())
                pluginPageURL = URL();
            NSString *pluginName = pluginPackage ? (NSString *)[pluginPackage pluginInfo].name : nil;

            NSError *error = [[NSError alloc] _initWithPluginErrorCode:errorCode
                                                            contentURL:pluginURL pluginPageURL:pluginPageURL pluginName:pluginName MIMEType:MIMEType];
            CallResourceLoadDelegate(implementations->plugInFailedWithErrorFunc, [m_webFrame.get() webView],
                                     @selector(webView:plugInFailedWithError:dataSource:), error, [m_webFrame.get() _dataSource]);
            [error release];
        }

        return nullptr;
    }
    
    ASSERT(view);
#if PLATFORM(IOS_FAMILY)
    return adoptRef(new PluginWidgetIOS(view));
#else
    return adoptRef(new PluginWidget(view));
#endif

    END_BLOCK_OBJC_EXCEPTIONS

    return nullptr;
}

void WebFrameLoaderClient::redirectDataToPlugin(WebCore::Widget& pluginWidget)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS

    WebHTMLRepresentation *representation = (WebHTMLRepresentation *)[[m_webFrame.get() _dataSource] representation];

    NSView *pluginView = pluginWidget.platformWidget();

#if ENABLE(NETSCAPE_PLUGIN_API)
    if ([pluginView isKindOfClass:[NETSCAPE_PLUGIN_VIEW class]])
        [representation _redirectDataToManualLoader:(NETSCAPE_PLUGIN_VIEW *)pluginView forPluginView:pluginView];
    else
#endif
    {
        WebHTMLView *documentView = (WebHTMLView *)[[m_webFrame.get() frameView] documentView];
        ASSERT([documentView isKindOfClass:[WebHTMLView class]]);
        [representation _redirectDataToManualLoader:[documentView _pluginController] forPluginView:pluginView];
    }

    END_BLOCK_OBJC_EXCEPTIONS
}
    
RefPtr<WebCore::Widget> WebFrameLoaderClient::createJavaAppletWidget(const WebCore::IntSize& size, WebCore::HTMLAppletElement& element, const URL& baseURL,
    const Vector<String>& paramNames, const Vector<String>& paramValues)
{
    BEGIN_BLOCK_OBJC_EXCEPTIONS

    NSView *view = nil;

    NSString *MIMEType = @"application/x-java-applet";
    
    WebView *webView = getWebView(m_webFrame.get());

    WebBasePluginPackage *pluginPackage = [webView _pluginForMIMEType:MIMEType];

    int errorCode = WebKitErrorJavaUnavailable;

    if (pluginPackage) {
        if (shouldBlockPlugin(pluginPackage)) {
            errorCode = WebKitErrorBlockedPlugInVersion;
            if (is<WebCore::RenderEmbeddedObject>(element.renderer()))
                downcast<WebCore::RenderEmbeddedObject>(*element.renderer()).setPluginUnavailabilityReason(WebCore::RenderEmbeddedObject::InsecurePluginVersion);
        } else {
#if ENABLE(NETSCAPE_PLUGIN_API)
            if ([pluginPackage isKindOfClass:[WebNetscapePluginPackage class]]) {
                view = [[[NETSCAPE_PLUGIN_VIEW alloc] initWithFrame:NSMakeRect(0, 0, size.width(), size.height())
                    pluginPackage:(WebNetscapePluginPackage *)pluginPackage
                    URL:nil
                    baseURL:baseURL
                    MIMEType:MIMEType
                    attributeKeys:createNSArray(paramNames).get()
                    attributeValues:createNSArray(paramValues).get()
                    loadManually:NO
                    element:&element] autorelease];
                if (view)
                    return adoptRef(new NetscapePluginWidget(static_cast<WebBaseNetscapePluginView *>(view)));
            }
#endif
        }
    }

    if (!view) {
        WebResourceDelegateImplementationCache* implementations = WebViewGetResourceLoadDelegateImplementations(getWebView(m_webFrame.get()));
        if (implementations->plugInFailedWithErrorFunc) {
            NSString *pluginName = pluginPackage ? (NSString *)[pluginPackage pluginInfo].name : nil;
            NSError *error = [[NSError alloc] _initWithPluginErrorCode:errorCode contentURL:nil pluginPageURL:nil pluginName:pluginName MIMEType:MIMEType];
            CallResourceLoadDelegate(implementations->plugInFailedWithErrorFunc, [m_webFrame.get() webView],
                                     @selector(webView:plugInFailedWithError:dataSource:), error, [m_webFrame.get() _dataSource]);
            [error release];
        }
    }

    END_BLOCK_OBJC_EXCEPTIONS

    return 0;
}

void WebFrameLoaderClient::sendH2Ping(const URL& url, CompletionHandler<void(Expected<Seconds, WebCore::ResourceError>&&)>&& completionHandler)
{
    ASSERT_NOT_REACHED();
    completionHandler(makeUnexpected(WebCore::internalError(url)));
}

String WebFrameLoaderClient::overrideMediaType() const
{
    NSString* overrideType = [getWebView(m_webFrame.get()) mediaStyle];
    if (overrideType)
        return overrideType;
    return String();
}

#if ENABLE(WEBGL)
static bool shouldBlockWebGL()
{
#if PLATFORM(MAC)
    return WebCore::WebGLBlocklist::shouldBlockWebGL();
#else
    return false;
#endif
}

WebCore::WebGLLoadPolicy WebFrameLoaderClient::webGLPolicyForURL(const URL&) const
{
    return shouldBlockWebGL() ? WebCore::WebGLLoadPolicy::WebGLBlockCreation : WebCore::WebGLLoadPolicy::WebGLAllowCreation;
}

WebCore::WebGLLoadPolicy WebFrameLoaderClient::resolveWebGLPolicyForURL(const URL&) const
{
    return shouldBlockWebGL() ? WebCore::WebGLLoadPolicy::WebGLBlockCreation : WebCore::WebGLLoadPolicy::WebGLAllowCreation;
}
#endif // ENABLE(WEBGL)

void WebFrameLoaderClient::dispatchDidClearWindowObjectInWorld(WebCore::DOMWrapperWorld& world)
{
    WebView *webView = getWebView(m_webFrame.get());
    WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);

    if (implementations->didClearWindowObjectForFrameInScriptWorldFunc) {
        CallFrameLoadDelegate(implementations->didClearWindowObjectForFrameInScriptWorldFunc,
            webView, @selector(webView:didClearWindowObjectForFrame:inScriptWorld:), m_webFrame.get(), [WebScriptWorld findOrCreateWorld:world]);
        return;
    }

    if (&world != &WebCore::mainThreadNormalWorld())
        return;

    auto* frame = core(m_webFrame.get());
    auto& script = frame->script();

#if JSC_OBJC_API_ENABLED
    if (implementations->didCreateJavaScriptContextForFrameFunc) {
        CallFrameLoadDelegate(implementations->didCreateJavaScriptContextForFrameFunc, webView, @selector(webView:didCreateJavaScriptContext:forFrame:),
            script.javaScriptContext(), m_webFrame.get());
    } else
#endif
    if (implementations->didClearWindowObjectForFrameFunc) {
        CallFrameLoadDelegate(implementations->didClearWindowObjectForFrameFunc, webView, @selector(webView:didClearWindowObject:forFrame:),
            script.windowScriptObject(), m_webFrame.get());
    } else if (implementations->windowScriptObjectAvailableFunc) {
        CallFrameLoadDelegate(implementations->windowScriptObjectAvailableFunc, webView, @selector(webView:windowScriptObjectAvailable:),
            script.windowScriptObject());
    }

    if ([webView scriptDebugDelegate]) {
        [m_webFrame.get() _detachScriptDebugger];
        [m_webFrame.get() _attachScriptDebugger];
    }
}

Ref< WebCore::FrameNetworkingContext> WebFrameLoaderClient::createNetworkingContext()
{
    return WebFrameNetworkingContext::create(core(m_webFrame.get()));
}

#if PLATFORM(IOS_FAMILY)
bool WebFrameLoaderClient::shouldLoadMediaElementURL(const URL& url) const 
{
    WebView *webView = getWebView(m_webFrame.get());
    
    if (id policyDelegate = [webView policyDelegate]) {
        if ([policyDelegate respondsToSelector:@selector(webView:shouldLoadMediaURL:inFrame:)])
            return [policyDelegate webView:webView shouldLoadMediaURL:url inFrame:m_webFrame.get()];
    }
    return true;
}
#endif

#if USE(QUICK_LOOK)
RefPtr<WebCore::LegacyPreviewLoaderClient> WebFrameLoaderClient::createPreviewLoaderClient(const String& fileName, const String& uti)
{
    class QuickLookDocumentWriter : public WebCore::LegacyPreviewLoaderClient {
    public:
        explicit QuickLookDocumentWriter(NSString *filePath)
            : m_filePath { filePath }
            , m_fileHandle { [NSFileHandle fileHandleForWritingAtPath:filePath] }
        {
            ASSERT(filePath.length);
        }

        ~QuickLookDocumentWriter()
        {
            [[NSFileManager defaultManager] _web_removeFileOnlyAtPath:m_filePath.get()];
        }

    private:
        RetainPtr<NSString> m_filePath;
        RetainPtr<NSFileHandle> m_fileHandle;

        void didReceiveBuffer(const WebCore::SharedBuffer& buffer) override
        {
            auto dataArray = buffer.createNSDataArray();
            for (NSData *data in dataArray.get())
                [m_fileHandle writeData:data];
        }

        void didFinishLoading() override
        {
            [m_fileHandle closeFile];
        }

        void didFail() override
        {
            [m_fileHandle closeFile];
        }
    };

    if (![m_webFrame webView].preferences.quickLookDocumentSavingEnabled)
        return nullptr;

    NSString *filePath = WebCore::createTemporaryFileForQuickLook(fileName);
    if (!filePath)
        return nullptr;

    auto documentWriter = adoptRef(*new QuickLookDocumentWriter(filePath));

    [m_webFrame provisionalDataSource]._quickLookContent = @{ WebQuickLookFileNameKey : filePath, WebQuickLookUTIKey : uti };
    [m_webFrame provisionalDataSource]._quickLookPreviewLoaderClient = documentWriter.ptr();
    return documentWriter;
}
#endif

#if ENABLE(CONTENT_FILTERING)
void WebFrameLoaderClient::contentFilterDidBlockLoad(WebCore::ContentFilterUnblockHandler unblockHandler)
{
    core(m_webFrame.get())->loader().policyChecker().setContentFilterUnblockHandler(WTFMove(unblockHandler));
}
#endif

void WebFrameLoaderClient::prefetchDNS(const String& hostname)
{
    WebCore::prefetchDNS(hostname);
}

void WebFrameLoaderClient::getLoadDecisionForIcons(const Vector<std::pair<WebCore::LinkIcon&, uint64_t>>& icons)
{
    auto* frame = core(m_webFrame.get());
    auto* documentLoader = frame->loader().documentLoader();
    ASSERT(documentLoader);

#if PLATFORM(MAC)
    if (!WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_DEFAULT_ICON_LOADING)) {
        for (auto& icon : icons)
            documentLoader->didGetLoadDecisionForIcon(false, icon.second, 0);

        return;
    }
#endif

    bool disallowedDueToImageLoadSettings = false;
    if (!frame->settings().loadsImagesAutomatically() && !frame->settings().loadsSiteIconsIgnoringImageLoadingSetting())
        disallowedDueToImageLoadSettings = true;

    if (disallowedDueToImageLoadSettings || !frame->isMainFrame() || !documentLoader->url().protocolIsInHTTPFamily() || ![WebView _isIconLoadingEnabled]) {
        for (auto& icon : icons)
            documentLoader->didGetLoadDecisionForIcon(false, icon.second, 0);

        return;
    }

    ASSERT(!m_activeIconLoadCallbackID);

#if !PLATFORM(IOS_FAMILY)
    // WebKit 1, which only supports one icon per page URL, traditionally has preferred the last icon in case of multiple icons listed.
    // To preserve that behavior we walk the list backwards.
    for (auto icon = icons.rbegin(); icon != icons.rend(); ++icon) {
        if (icon->first.type != WebCore::LinkIconType::Favicon || m_activeIconLoadCallbackID) {
            documentLoader->didGetLoadDecisionForIcon(false, icon->second, 0);
            continue;
        }

        m_activeIconLoadCallbackID = 1;
        documentLoader->didGetLoadDecisionForIcon(true, icon->second, m_activeIconLoadCallbackID);
    }
#else
    // No WebCore icon loading on iOS
    for (auto& icon : icons)
        documentLoader->didGetLoadDecisionForIcon(false, icon.second, 0);
#endif
}

#if !PLATFORM(IOS_FAMILY)
static NSImage *webGetNSImage(WebCore::Image* image, NSSize size)
{
    ASSERT(size.width);
    ASSERT(size.height);

    // FIXME: We're doing the resize here for now because WebCore::Image doesn't yet support resizing/multiple representations
    // This makes it so there's effectively only one size of a particular icon in the system at a time. We should move this
    // to WebCore::Image at some point.
    if (!image)
        return nil;
    NSImage* nsImage = image->nsImage();
    if (!nsImage)
        return nil;
    if (!NSEqualSizes([nsImage size], size)) {
        ALLOW_DEPRECATED_DECLARATIONS_BEGIN
        [nsImage setScalesWhenResized:YES];
        ALLOW_DEPRECATED_DECLARATIONS_END
        [nsImage setSize:size];
    }
    return nsImage;
}
#endif // !PLATFORM(IOS_FAMILY)

void WebFrameLoaderClient::finishedLoadingIcon(uint64_t callbackID, WebCore::SharedBuffer* iconData)
{
#if !PLATFORM(IOS_FAMILY)
    ASSERT(m_activeIconLoadCallbackID);
    ASSERT(callbackID = m_activeIconLoadCallbackID);
    m_activeIconLoadCallbackID = 0;

    WebView *webView = getWebView(m_webFrame.get());
    if (!webView)
        return;

    auto image = WebCore::BitmapImage::create();
    if (image->setData(iconData, true) < WebCore::EncodedDataStatus::SizeAvailable)
        return;

    NSImage *icon = webGetNSImage(image.ptr(), NSMakeSize(16, 16));
    if (icon)
        [webView _setMainFrameIcon:icon];
#else
    UNUSED_PARAM(callbackID);
    UNUSED_PARAM(iconData);
#endif
}

@implementation WebFramePolicyListener

+ (void)initialize
{
#if !PLATFORM(IOS_FAMILY)
    JSC::initialize();
    WTF::initializeMainThread();
#endif
}

- (id)initWithFrame:(NakedPtr<WebCore::Frame>)frame identifier:(WebCore::PolicyCheckIdentifier)identifier policyFunction:(WebCore::FramePolicyFunction&&)policyFunction defaultPolicy:(WebCore::PolicyAction)defaultPolicy
{
    self = [self init];
    if (!self)
        return nil;

    _identifier = identifier;
    _frame = frame;
    _policyFunction = WTFMove(policyFunction);
    _defaultPolicy = defaultPolicy;

    return self;
}

#if HAVE(APP_LINKS)
- (id)initWithFrame:(NakedPtr<WebCore::Frame>)frame identifier:(WebCore::PolicyCheckIdentifier)identifier policyFunction:(WebCore::FramePolicyFunction&&)policyFunction defaultPolicy:(WebCore::PolicyAction)defaultPolicy appLinkURL:(NSURL *)appLinkURL
{
    self = [self initWithFrame:frame identifier:identifier policyFunction:WTFMove(policyFunction) defaultPolicy:defaultPolicy];
    if (!self)
        return nil;

    _appLinkURL = appLinkURL;

    return self;
}
#endif

- (void)invalidate
{
    _frame = nullptr;
    if (auto policyFunction = std::exchange(_policyFunction, nullptr))
        policyFunction(WebCore::PolicyAction::Ignore, _identifier);
}

- (void)dealloc
{
    if (WebCoreObjCScheduleDeallocateOnMainThread([WebFramePolicyListener class], self))
        return;

    // If the app did not respond before the listener is destroyed, then we use the default policy ("Use" for navigation
    // response policy decision, "Ignore" for other policy decisions).
    _frame = nullptr;
    if (auto policyFunction = std::exchange(_policyFunction, nullptr)) {
        RELEASE_LOG_ERROR(Loading, "Client application failed to make a policy decision via WebPolicyDecisionListener, using defaultPolicy %hhu", _defaultPolicy);
        policyFunction(_defaultPolicy, _identifier);
    }

    [super dealloc];
}

- (void)receivedPolicyDecision:(WebCore::PolicyAction)action
{
    auto frame = WTFMove(_frame);
    if (!frame)
        return;

    if (auto policyFunction = std::exchange(_policyFunction, nullptr))
        policyFunction(action, _identifier);
}

- (void)ignore
{
    [self receivedPolicyDecision:WebCore::PolicyAction::Ignore];
}

- (void)download
{
    [self receivedPolicyDecision:WebCore::PolicyAction::Download];
}

- (void)use
{
#if HAVE(APP_LINKS)
    if (_appLinkURL && _frame) {
        [LSAppLink openWithURL:_appLinkURL.get() completionHandler:^(BOOL success, NSError *) {
#if USE(WEB_THREAD)
            WebThreadRun(^{
#else
            dispatch_async(dispatch_get_main_queue(), ^{
#endif
                if (success)
                    [self receivedPolicyDecision:WebCore::PolicyAction::Ignore];
                else
                    [self receivedPolicyDecision:WebCore::PolicyAction::Use];
            });
        }];
        return;
    }
#endif

    [self receivedPolicyDecision:WebCore::PolicyAction::Use];
}

- (void)continue
{
    [self receivedPolicyDecision:WebCore::PolicyAction::Use];
}

@end