/* * Copyright (C) 2012 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 "config.h" #import "WKWebProcessPlugInBrowserContextControllerInternal.h" #if WK_API_ENABLED #import "APIData.h" #import "RemoteObjectRegistry.h" #import "RemoteObjectRegistryMessages.h" #import "WKBrowsingContextHandleInternal.h" #import "WKBundleAPICast.h" #import "WKBundlePage.h" #import "WKBundlePagePrivate.h" #import "WKDOMInternals.h" #import "WKNSDictionary.h" #import "WKNSError.h" #import "WKRenderingProgressEventsInternal.h" #import "WKRetainPtr.h" #import "WKURLRequestNS.h" #import "WKWebProcessPluginFrameInternal.h" #import "WKWebProcessPlugInInternal.h" #import "WKWebProcessPlugInFormDelegatePrivate.h" #import "WKWebProcessPlugInLoadDelegate.h" #import "WKWebProcessPlugInNodeHandleInternal.h" #import "WKWebProcessPlugInPageGroupInternal.h" #import "WKWebProcessPlugInScriptWorldInternal.h" #import "WeakObjCPtr.h" #import "WebPage.h" #import "WebProcess.h" #import "_WKRemoteObjectRegistryInternal.h" #import #import #import #import using namespace WebCore; using namespace WebKit; @protocol WKWebProcessPlugInFormDelegatePrivateDeprecated - (NSObject *)_webProcessPlugInBrowserContextController:(WKWebProcessPlugInBrowserContextController *)controller newWillSubmitForm:(WKWebProcessPlugInNodeHandle *)form toFrame:(WKWebProcessPlugInFrame *)frame fromFrame:(WKWebProcessPlugInFrame *)sourceFrame withValues:(NSDictionary *)values; @end @implementation WKWebProcessPlugInBrowserContextController { API::ObjectStorage _page; WeakObjCPtr> _loadDelegate; WeakObjCPtr> _formDelegate; RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry; } static void didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userDataRef, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didStartProvisionalLoadForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didStartProvisionalLoadForFrame:wrapper(*toImpl(frame))]; } static void didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFinishLoadForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFinishLoadForFrame:wrapper(*toImpl(frame))]; } static void globalObjectIsAvailableForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef scriptWorld, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:globalObjectIsAvailableForFrame:inScriptWorld:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController globalObjectIsAvailableForFrame:wrapper(*toImpl(frame)) inScriptWorld:wrapper(*toImpl(scriptWorld))]; } static void didRemoveFrameFromHierarchy(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didRemoveFrameFromHierarchy:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didRemoveFrameFromHierarchy:wrapper(*toImpl(frame))]; } static void didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didCommitLoadForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didCommitLoadForFrame:wrapper(*toImpl(frame))]; } static void didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFinishDocumentLoadForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFinishDocumentLoadForFrame:wrapper(*toImpl(frame))]; } static void didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef wkError, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFailLoadWithErrorForFrame:error:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFailLoadWithErrorForFrame:wrapper(*toImpl(frame)) error:wrapper(*toImpl(wkError))]; } static void didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didSameDocumentNavigationForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didSameDocumentNavigationForFrame:wrapper(*toImpl(frame))]; } static void didLayoutForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didLayoutForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didLayoutForFrame:wrapper(*toImpl(frame))]; } static void didLayout(WKBundlePageRef page, WKLayoutMilestones milestones, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:renderingProgressDidChange:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController renderingProgressDidChange:renderingProgressEvents(milestones)]; } static void didFirstVisuallyNonEmptyLayoutForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFirstVisuallyNonEmptyLayoutForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFirstVisuallyNonEmptyLayoutForFrame:wrapper(*toImpl(frame))]; } static void didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didHandleOnloadEventsForFrame:)]) [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didHandleOnloadEventsForFrame:wrapper(*toImpl(frame))]; } static void setUpPageLoaderClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page) { WKBundlePageLoaderClientV7 client; memset(&client, 0, sizeof(client)); client.base.version = 7; client.base.clientInfo = contextController; client.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame; client.didCommitLoadForFrame = didCommitLoadForFrame; client.didFinishDocumentLoadForFrame = didFinishDocumentLoadForFrame; client.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame; client.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame; client.didFinishLoadForFrame = didFinishLoadForFrame; client.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrame; client.didRemoveFrameFromHierarchy = didRemoveFrameFromHierarchy; client.didHandleOnloadEventsForFrame = didHandleOnloadEventsForFrame; client.didFirstVisuallyNonEmptyLayoutForFrame = didFirstVisuallyNonEmptyLayoutForFrame; client.didLayoutForFrame = didLayoutForFrame; client.didLayout = didLayout; page.initializeInjectedBundleLoaderClient(&client.base); } static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef frame, uint64_t, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:willSendRequest:redirectResponse:)]) { NSURLRequest *originalRequest = toImpl(request)->resourceRequest().nsURLRequest(DoNotUpdateHTTPBody); RetainPtr substituteRequest = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) willSendRequest:originalRequest redirectResponse:toImpl(redirectResponse)->resourceResponse().nsURLResponse()]; if (substituteRequest != originalRequest) return substituteRequest ? WKURLRequestCreateWithNSURLRequest(substituteRequest.get()) : nullptr; } WKRetain(request); return request; } static void didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, bool, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didInitiateLoadForResource:request:)]) { [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didInitiateLoadForResource:resourceIdentifier request:toImpl(request)->resourceRequest().nsURLRequest(DoNotUpdateHTTPBody)]; } } static void didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFinishLoadForResource:)]) { [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didFinishLoadForResource:resourceIdentifier]; } } static void didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKErrorRef error, const void* clientInfo) { WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo; auto loadDelegate = pluginContextController->_loadDelegate.get(); if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFailLoadForResource:error:)]) { [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didFailLoadForResource:resourceIdentifier error:wrapper(*toImpl(error))]; } } static void setUpResourceLoadClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page) { WKBundlePageResourceLoadClientV1 client; memset(&client, 0, sizeof(client)); client.base.version = 1; client.base.clientInfo = contextController; client.willSendRequestForFrame = willSendRequestForFrame; client.didInitiateLoadForResource = didInitiateLoadForResource; client.didFinishLoadForResource = didFinishLoadForResource; client.didFailLoadForResource = didFailLoadForResource; page.initializeInjectedBundleResourceLoadClient(&client.base); } - (id )loadDelegate { return _loadDelegate.getAutoreleased(); } - (void)setLoadDelegate:(id )loadDelegate { _loadDelegate = loadDelegate; if (loadDelegate) { setUpPageLoaderClient(self, *_page); setUpResourceLoadClient(self, *_page); } else { _page->initializeInjectedBundleLoaderClient(nullptr); _page->initializeInjectedBundleResourceLoadClient(nullptr); } } - (void)dealloc { _page->~WebPage(); [super dealloc]; } - (WKDOMDocument *)mainFrameDocument { Frame* webCoreMainFrame = _page->mainFrame(); if (!webCoreMainFrame) return nil; return toWKDOMDocument(webCoreMainFrame->document()); } - (WKDOMRange *)selectedRange { RefPtr range = _page->currentSelectionAsRange(); if (!range) return nil; return toWKDOMRange(range.get()); } - (WKWebProcessPlugInFrame *)mainFrame { WebFrame *webKitMainFrame = _page->mainWebFrame(); if (!webKitMainFrame) return nil; return wrapper(*webKitMainFrame); } - (WKWebProcessPlugInPageGroup *)pageGroup { return wrapper(*_page->pageGroup()); } #pragma mark WKObject protocol implementation - (API::Object&)_apiObject { return *_page; } @end @implementation WKWebProcessPlugInBrowserContextController (WKPrivate) - (WKBundlePageRef)_bundlePageRef { return toAPI(_page.get()); } - (WKBrowsingContextHandle *)handle { return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease]; } + (instancetype)lookUpBrowsingContextFromHandle:(WKBrowsingContextHandle *)handle { WebPage* webPage = WebProcess::shared().webPage(handle.pageID); if (!webPage) return nil; return wrapper(*webPage); } - (_WKRemoteObjectRegistry *)_remoteObjectRegistry { if (!_remoteObjectRegistry) { _remoteObjectRegistry = [[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]; WebProcess::shared().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]); } return _remoteObjectRegistry.get(); } - (id )_formDelegate { return _formDelegate.getAutoreleased(); } - (void)_setFormDelegate:(id )formDelegate { _formDelegate = formDelegate; class FormClient : public API::InjectedBundle::FormClient { public: explicit FormClient(WKWebProcessPlugInBrowserContextController *controller) : m_controller(controller) { } virtual ~FormClient() { } virtual void didFocusTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame) override { auto formDelegate = m_controller->_formDelegate.get(); if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:didFocusTextField:inFrame:)]) [formDelegate _webProcessPlugInBrowserContextController:m_controller didFocusTextField:wrapper(*InjectedBundleNodeHandle::getOrCreate(inputElement).get()) inFrame:wrapper(*frame)]; } virtual void willSendSubmitEvent(WebPage*, HTMLFormElement* formElement, WebFrame* targetFrame, WebFrame* sourceFrame, const Vector>& values) override { auto formDelegate = m_controller->_formDelegate.get(); if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSendSubmitEventToForm:inFrame:targetFrame:values:)]) { auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]); for (const auto& pair : values) [valueMap setObject:pair.second forKey:pair.first]; [formDelegate _webProcessPlugInBrowserContextController:m_controller willSendSubmitEventToForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get()) inFrame:wrapper(*sourceFrame) targetFrame:wrapper(*targetFrame) values:valueMap.get()]; } } static void encodeUserObject(NSObject *userObject, RefPtr& userData) { if (!userObject) return; auto data = adoptNS([[NSMutableData alloc] init]); auto archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]); [archiver setRequiresSecureCoding:YES]; @try { [archiver encodeObject:userObject forKey:@"userObject"]; } @catch (NSException *exception) { LOG_ERROR("Failed to encode user object: %@", exception); return; } [archiver finishEncoding]; userData = API::Data::createWithoutCopying((const unsigned char*)[data bytes], [data length], releaseNSData, data.leakRef()).leakRef(); } virtual void willSubmitForm(WebPage*, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector>& values, RefPtr& userData) override { auto formDelegate = m_controller->_formDelegate.get(); if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:newWillSubmitForm:toFrame:fromFrame:withValues:)]) { auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]); for (const auto& pair : values) [valueMap setObject:pair.second forKey:pair.first]; NSObject *userObject = [(id)formDelegate _webProcessPlugInBrowserContextController:m_controller newWillSubmitForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get()) toFrame:wrapper(*frame) fromFrame:wrapper(*sourceFrame) withValues:valueMap.get()]; encodeUserObject(userObject, userData); } else if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSubmitForm:toFrame:fromFrame:withValues:)]) { auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]); for (const auto& pair : values) [valueMap setObject:pair.second forKey:pair.first]; NSObject *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willSubmitForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get()) toFrame:wrapper(*frame) fromFrame:wrapper(*sourceFrame) withValues:valueMap.get()]; encodeUserObject(userObject, userData); } } virtual void textDidChangeInTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame, bool initiatedByUserTyping) override { auto formDelegate = m_controller->_formDelegate.get(); if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:textDidChangeInTextField:inFrame:initiatedByUserTyping:)]) [formDelegate _webProcessPlugInBrowserContextController:m_controller textDidChangeInTextField:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(inputElement)) inFrame:wrapper(*frame) initiatedByUserTyping:initiatedByUserTyping]; } static void releaseNSData(unsigned char*, const void* untypedData) { [(NSData *)untypedData release]; } virtual void willBeginInputSession(WebPage*, Element* element, WebFrame* frame, RefPtr& userData) override { auto formDelegate = m_controller->_formDelegate.get(); if (![formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willBeginInputSessionForElement:inFrame:)]) return; NSObject *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willBeginInputSessionForElement:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element)) inFrame:wrapper(*frame)]; encodeUserObject(userObject, userData); } private: WKWebProcessPlugInBrowserContextController *m_controller; }; if (formDelegate) _page->setInjectedBundleFormClient(std::make_unique(self)); else _page->setInjectedBundleFormClient(nullptr); } - (BOOL)_defersLoading { return _page->defersLoading(); } - (void)_setDefersLoading:(BOOL)defersLoading { _page->setDefersLoading(defersLoading); } @end #endif // WK_API_ENABLED