Add API::InjectedBundle::ResourceLoadClient
[WebKit-https.git] / Source / WebKit2 / WebProcess / InjectedBundle / API / mac / WKWebProcessPlugInBrowserContextController.mm
1 /*
2  * Copyright (C) 2012 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKWebProcessPlugInBrowserContextControllerInternal.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIData.h"
32 #import "RemoteObjectRegistry.h"
33 #import "RemoteObjectRegistryMessages.h"
34 #import "WKBrowsingContextHandleInternal.h"
35 #import "WKBundleAPICast.h"
36 #import "WKBundlePage.h"
37 #import "WKBundlePagePrivate.h"
38 #import "WKDOMInternals.h"
39 #import "WKNSDictionary.h"
40 #import "WKNSError.h"
41 #import "WKNSString.h"
42 #import "WKNSURL.h"
43 #import "WKNSURLRequest.h"
44 #import "WKRetainPtr.h"
45 #import "WKStringCF.h"
46 #import "WKURLRequestNS.h"
47 #import "WKWebProcessPlugInEditingDelegate.h"
48 #import "WKWebProcessPlugInFrameInternal.h"
49 #import "WKWebProcessPlugInInternal.h"
50 #import "WKWebProcessPlugInFormDelegatePrivate.h"
51 #import "WKWebProcessPlugInLoadDelegate.h"
52 #import "WKWebProcessPlugInNodeHandleInternal.h"
53 #import "WKWebProcessPlugInPageGroupInternal.h"
54 #import "WKWebProcessPlugInRangeHandleInternal.h"
55 #import "WKWebProcessPlugInScriptWorldInternal.h"
56 #import "WeakObjCPtr.h"
57 #import "WebPage.h"
58 #import "WebProcess.h"
59 #import "_WKRemoteObjectRegistryInternal.h"
60 #import "_WKRenderingProgressEventsInternal.h"
61 #import "_WKSameDocumentNavigationTypeInternal.h"
62 #import <WebCore/Document.h>
63 #import <WebCore/DocumentFragment.h>
64 #import <WebCore/Frame.h>
65 #import <WebCore/HTMLFormElement.h>
66 #import <WebCore/HTMLInputElement.h>
67 #import <WebCore/MainFrame.h>
68
69 using namespace WebCore;
70 using namespace WebKit;
71
72 @interface NSObject (WKDeprecatedDelegateMethods)
73 - (void)webProcessPlugInBrowserContextController:(WKWebProcessPlugInBrowserContextController *)controller didSameDocumentNavigationForFrame:(WKWebProcessPlugInFrame *)frame;
74 @end
75
76 @implementation WKWebProcessPlugInBrowserContextController {
77     API::ObjectStorage<WebPage> _page;
78     WeakObjCPtr<id <WKWebProcessPlugInLoadDelegate>> _loadDelegate;
79     WeakObjCPtr<id <WKWebProcessPlugInFormDelegatePrivate>> _formDelegate;
80     WeakObjCPtr<id <WKWebProcessPlugInEditingDelegate>> _editingDelegate;
81     
82     RetainPtr<_WKRemoteObjectRegistry> _remoteObjectRegistry;
83 }
84
85 static void didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userDataRef, const void *clientInfo)
86 {
87     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
88     auto loadDelegate = pluginContextController->_loadDelegate.get();
89
90     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didStartProvisionalLoadForFrame:)])
91         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didStartProvisionalLoadForFrame:wrapper(*toImpl(frame))];
92 }
93
94 static void didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef *userDataRef, const void *clientInfo)
95 {
96     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
97     auto loadDelegate = pluginContextController->_loadDelegate.get();
98
99     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didReceiveServerRedirectForProvisionalLoadForFrame:)])
100         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didReceiveServerRedirectForProvisionalLoadForFrame:wrapper(*toImpl(frame))];
101 }
102
103 static void didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
104 {
105     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
106     auto loadDelegate = pluginContextController->_loadDelegate.get();
107
108     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFinishLoadForFrame:)])
109         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFinishLoadForFrame:wrapper(*toImpl(frame))];
110 }
111
112 static void globalObjectIsAvailableForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef scriptWorld, const void* clientInfo)
113 {
114     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
115     auto loadDelegate = pluginContextController->_loadDelegate.get();
116
117     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:globalObjectIsAvailableForFrame:inScriptWorld:)])
118         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController globalObjectIsAvailableForFrame:wrapper(*toImpl(frame)) inScriptWorld:wrapper(*toImpl(scriptWorld))];
119 }
120
121 static void didRemoveFrameFromHierarchy(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void* clientInfo)
122 {
123     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
124     auto loadDelegate = pluginContextController->_loadDelegate.get();
125
126     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didRemoveFrameFromHierarchy:)])
127         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didRemoveFrameFromHierarchy:wrapper(*toImpl(frame))];
128 }
129
130 static void didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
131 {
132     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
133     auto loadDelegate = pluginContextController->_loadDelegate.get();
134
135     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didCommitLoadForFrame:)])
136         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didCommitLoadForFrame:wrapper(*toImpl(frame))];
137 }
138
139 static void didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
140 {
141     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
142     auto loadDelegate = pluginContextController->_loadDelegate.get();
143
144     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFinishDocumentLoadForFrame:)])
145         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFinishDocumentLoadForFrame:wrapper(*toImpl(frame))];
146 }
147
148 static void didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef wkError, WKTypeRef* userData, const void *clientInfo)
149 {
150     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
151     auto loadDelegate = pluginContextController->_loadDelegate.get();
152
153     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFailProvisionalLoadWithErrorForFrame:error:)])
154         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFailProvisionalLoadWithErrorForFrame:wrapper(*toImpl(frame)) error:wrapper(*toImpl(wkError))];
155 }
156
157 static void didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef wkError, WKTypeRef* userData, const void *clientInfo)
158 {
159     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
160     auto loadDelegate = pluginContextController->_loadDelegate.get();
161
162     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFailLoadWithErrorForFrame:error:)])
163         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFailLoadWithErrorForFrame:wrapper(*toImpl(frame)) error:wrapper(*toImpl(wkError))];
164 }
165
166 static void didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef* userData, const void *clientInfo)
167 {
168     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
169     auto loadDelegate = pluginContextController->_loadDelegate.get();
170
171     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didSameDocumentNavigation:forFrame:)])
172         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didSameDocumentNavigation:toWKSameDocumentNavigationType(toSameDocumentNavigationType(type)) forFrame:wrapper(*toImpl(frame))];
173     else {
174         // FIXME: Remove this once clients switch to implementing the above delegate method.
175         if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didSameDocumentNavigationForFrame:)])
176             [(NSObject *)loadDelegate webProcessPlugInBrowserContextController:pluginContextController didSameDocumentNavigationForFrame:wrapper(*toImpl(frame))];
177     }
178 }
179
180 static void didLayoutForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
181 {
182     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
183     auto loadDelegate = pluginContextController->_loadDelegate.get();
184
185     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didLayoutForFrame:)])
186         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didLayoutForFrame:wrapper(*toImpl(frame))];
187 }
188
189 static void didReachLayoutMilestone(WKBundlePageRef page, WKLayoutMilestones milestones, WKTypeRef* userData, const void *clientInfo)
190 {
191     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
192     auto loadDelegate = pluginContextController->_loadDelegate.get();
193
194     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:renderingProgressDidChange:)])
195         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController renderingProgressDidChange:renderingProgressEvents(toLayoutMilestones(milestones))];
196 }
197
198 static void didFirstVisuallyNonEmptyLayoutForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef* userData, const void *clientInfo)
199 {
200     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
201     auto loadDelegate = pluginContextController->_loadDelegate.get();
202
203     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didFirstVisuallyNonEmptyLayoutForFrame:)])
204         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didFirstVisuallyNonEmptyLayoutForFrame:wrapper(*toImpl(frame))];
205 }
206
207 static void didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
208 {
209     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
210     auto loadDelegate = pluginContextController->_loadDelegate.get();
211
212     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:didHandleOnloadEventsForFrame:)])
213         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController didHandleOnloadEventsForFrame:wrapper(*toImpl(frame))];
214 }
215
216 static WKStringRef userAgentForURL(WKBundleFrameRef frame, WKURLRef url, const void* clientInfo)
217 {
218     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
219     auto loadDelegate = pluginContextController->_loadDelegate.get();
220     
221     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:userAgentForURL:)]) {
222         WKWebProcessPlugInFrame *newFrame = wrapper(*toImpl(frame));
223         NSString *string = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:newFrame userAgentForURL:wrapper(*toImpl(url))];
224         if (!string)
225             return nullptr;
226
227         WKStringRef wkString = WKStringCreateWithCFString((CFStringRef)string);
228         return wkString;
229     }
230     
231     return nullptr;
232 }
233
234 static void setUpPageLoaderClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page)
235 {
236     WKBundlePageLoaderClientV8 client;
237     memset(&client, 0, sizeof(client));
238
239     client.base.version = 8;
240     client.base.clientInfo = contextController;
241     client.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
242     client.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
243     client.didCommitLoadForFrame = didCommitLoadForFrame;
244     client.didFinishDocumentLoadForFrame = didFinishDocumentLoadForFrame;
245     client.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
246     client.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
247     client.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
248     client.didFinishLoadForFrame = didFinishLoadForFrame;
249     client.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrame;
250     client.didRemoveFrameFromHierarchy = didRemoveFrameFromHierarchy;
251     client.didHandleOnloadEventsForFrame = didHandleOnloadEventsForFrame;
252     client.didFirstVisuallyNonEmptyLayoutForFrame = didFirstVisuallyNonEmptyLayoutForFrame;
253     client.userAgentForURL = userAgentForURL;
254
255     client.didLayoutForFrame = didLayoutForFrame;
256     client.didLayout = didReachLayoutMilestone;
257
258     WKBundlePageSetPageLoaderClient(toAPI(&page), &client.base);
259 }
260
261 static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
262 {
263     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
264     auto loadDelegate = pluginContextController->_loadDelegate.get();
265
266     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:willSendRequestForResource:request:redirectResponse:)]) {
267         NSURLRequest *originalRequest = wrapper(*toImpl(request));
268         RetainPtr<NSURLRequest> substituteRequest = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) willSendRequestForResource:resourceIdentifier
269             request:originalRequest redirectResponse:toImpl(redirectResponse)->resourceResponse().nsURLResponse()];
270
271         if (substituteRequest != originalRequest)
272             return substituteRequest ? WKURLRequestCreateWithNSURLRequest(substituteRequest.get()) : nullptr;
273     } else if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:willSendRequest:redirectResponse:)]) {
274         NSURLRequest *originalRequest = wrapper(*toImpl(request));
275         RetainPtr<NSURLRequest> substituteRequest = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) willSendRequest:originalRequest
276             redirectResponse:toImpl(redirectResponse)->resourceResponse().nsURLResponse()];
277
278         if (substituteRequest != originalRequest)
279             return substituteRequest ? WKURLRequestCreateWithNSURLRequest(substituteRequest.get()) : nullptr;
280     }
281
282     WKRetain(request);
283     return request;
284 }
285
286 static void didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, bool pageIsProvisionallyLoading, const void* clientInfo)
287 {
288     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
289     auto loadDelegate = pluginContextController->_loadDelegate.get();
290
291     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didInitiateLoadForResource:request:pageIsProvisionallyLoading:)]) {
292         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didInitiateLoadForResource:resourceIdentifier request:wrapper(*toImpl(request))
293             pageIsProvisionallyLoading:pageIsProvisionallyLoading];
294     } else if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didInitiateLoadForResource:request:)]) {
295         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didInitiateLoadForResource:resourceIdentifier request:wrapper(*toImpl(request))];
296     }
297 }
298
299 static void didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, const void* clientInfo)
300 {
301     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
302     auto loadDelegate = pluginContextController->_loadDelegate.get();
303
304     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFinishLoadForResource:)]) {
305         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController
306                                                          frame:wrapper(*toImpl(frame))
307                                       didFinishLoadForResource:resourceIdentifier];
308     }
309 }
310
311 static void didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKErrorRef error, const void* clientInfo)
312 {
313     WKWebProcessPlugInBrowserContextController *pluginContextController = (WKWebProcessPlugInBrowserContextController *)clientInfo;
314     auto loadDelegate = pluginContextController->_loadDelegate.get();
315
316     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFailLoadForResource:error:)]) {
317         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController
318                                                          frame:wrapper(*toImpl(frame))
319                                         didFailLoadForResource:resourceIdentifier
320                                                          error:wrapper(*toImpl(error))];
321     }
322 }
323
324 static void setUpResourceLoadClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page)
325 {
326     WKBundlePageResourceLoadClientV1 client;
327     memset(&client, 0, sizeof(client));
328
329     client.base.version = 1;
330     client.base.clientInfo = contextController;
331     client.willSendRequestForFrame = willSendRequestForFrame;
332     client.didInitiateLoadForResource = didInitiateLoadForResource;
333     client.didFinishLoadForResource = didFinishLoadForResource;
334     client.didFailLoadForResource = didFailLoadForResource;
335
336     WKBundlePageSetResourceLoadClient(toAPI(&page), &client.base);
337 }
338
339 - (id <WKWebProcessPlugInLoadDelegate>)loadDelegate
340 {
341     return _loadDelegate.getAutoreleased();
342 }
343
344 - (void)setLoadDelegate:(id <WKWebProcessPlugInLoadDelegate>)loadDelegate
345 {
346     _loadDelegate = loadDelegate;
347
348     if (loadDelegate) {
349         setUpPageLoaderClient(self, *_page);
350         setUpResourceLoadClient(self, *_page);
351     } else {
352         WKBundlePageSetPageLoaderClient(toAPI(_page.get()), nullptr);
353         WKBundlePageSetResourceLoadClient(toAPI(_page.get()), nullptr);
354     }
355 }
356
357 - (void)dealloc
358 {
359     if (_remoteObjectRegistry) {
360         WebProcess::singleton().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
361         [_remoteObjectRegistry _invalidate];
362     }
363
364     _page->~WebPage();
365
366     [super dealloc];
367 }
368
369 - (WKDOMDocument *)mainFrameDocument
370 {
371     Frame* webCoreMainFrame = _page->mainFrame();
372     if (!webCoreMainFrame)
373         return nil;
374
375     return toWKDOMDocument(webCoreMainFrame->document());
376 }
377
378 - (WKDOMRange *)selectedRange
379 {
380     RefPtr<Range> range = _page->currentSelectionAsRange();
381     if (!range)
382         return nil;
383
384     return toWKDOMRange(range.get());
385 }
386
387 - (WKWebProcessPlugInFrame *)mainFrame
388 {
389     WebFrame *webKitMainFrame = _page->mainWebFrame();
390     if (!webKitMainFrame)
391         return nil;
392
393     return wrapper(*webKitMainFrame);
394 }
395
396 - (WKWebProcessPlugInPageGroup *)pageGroup
397 {
398     return wrapper(*_page->pageGroup());
399 }
400
401 #pragma mark WKObject protocol implementation
402
403 - (API::Object&)_apiObject
404 {
405     return *_page;
406 }
407
408 @end
409
410 @implementation WKWebProcessPlugInBrowserContextController (WKPrivate)
411
412 - (WKBundlePageRef)_bundlePageRef
413 {
414     return toAPI(_page.get());
415 }
416
417 - (WKBrowsingContextHandle *)handle
418 {
419     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
420 }
421
422 + (instancetype)lookUpBrowsingContextFromHandle:(WKBrowsingContextHandle *)handle
423 {
424     WebPage* webPage = WebProcess::singleton().webPage(handle.pageID);
425     if (!webPage)
426         return nil;
427
428     return wrapper(*webPage);
429 }
430
431 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
432 {
433     if (!_remoteObjectRegistry) {
434         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithMessageSender:*_page]);
435         WebProcess::singleton().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
436     }
437
438     return _remoteObjectRegistry.get();
439 }
440
441 - (id <WKWebProcessPlugInFormDelegatePrivate>)_formDelegate
442 {
443     return _formDelegate.getAutoreleased();
444 }
445
446 - (void)_setFormDelegate:(id <WKWebProcessPlugInFormDelegatePrivate>)formDelegate
447 {
448     _formDelegate = formDelegate;
449
450     class FormClient : public API::InjectedBundle::FormClient {
451     public:
452         explicit FormClient(WKWebProcessPlugInBrowserContextController *controller)
453             : m_controller(controller)
454         {
455         }
456
457         virtual ~FormClient() { }
458
459         void didFocusTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame) override
460         {
461             auto formDelegate = m_controller->_formDelegate.get();
462
463             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:didFocusTextField:inFrame:)])
464                 [formDelegate _webProcessPlugInBrowserContextController:m_controller didFocusTextField:wrapper(*InjectedBundleNodeHandle::getOrCreate(inputElement).get()) inFrame:wrapper(*frame)];
465         }
466
467         void willSendSubmitEvent(WebPage*, HTMLFormElement* formElement, WebFrame* targetFrame, WebFrame* sourceFrame, const Vector<std::pair<String, String>>& values) override
468         {
469             auto formDelegate = m_controller->_formDelegate.get();
470
471             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSendSubmitEventToForm:inFrame:targetFrame:values:)]) {
472                 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]);
473                 for (const auto& pair : values)
474                     [valueMap setObject:pair.second forKey:pair.first];
475
476                 [formDelegate _webProcessPlugInBrowserContextController:m_controller willSendSubmitEventToForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get())
477                     inFrame:wrapper(*sourceFrame) targetFrame:wrapper(*targetFrame) values:valueMap.get()];
478             }
479         }
480
481         static void encodeUserObject(NSObject <NSSecureCoding> *userObject, RefPtr<API::Object>& userData)
482         {
483             if (!userObject)
484                 return;
485
486             auto data = adoptNS([[NSMutableData alloc] init]);
487             auto archiver = adoptNS([[NSKeyedArchiver alloc] initForWritingWithMutableData:data.get()]);
488             [archiver setRequiresSecureCoding:YES];
489             @try {
490                 [archiver encodeObject:userObject forKey:@"userObject"];
491             } @catch (NSException *exception) {
492                 LOG_ERROR("Failed to encode user object: %@", exception);
493                 return;
494             }
495             [archiver finishEncoding];
496
497             userData = API::Data::createWithoutCopying(WTFMove(data));
498         }
499
500         void willSubmitForm(WebPage*, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& values, RefPtr<API::Object>& userData) override
501         {
502             auto formDelegate = m_controller->_formDelegate.get();
503
504             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSubmitForm:toFrame:fromFrame:withValues:)]) {
505                 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]);
506                 for (const auto& pair : values)
507                     [valueMap setObject:pair.second forKey:pair.first];
508
509                 NSObject <NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willSubmitForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get()) toFrame:wrapper(*frame) fromFrame:wrapper(*sourceFrame) withValues:valueMap.get()];
510                 encodeUserObject(userObject, userData);
511             }
512         }
513
514         void textDidChangeInTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame, bool initiatedByUserTyping) override
515         {
516             auto formDelegate = m_controller->_formDelegate.get();
517
518             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:textDidChangeInTextField:inFrame:initiatedByUserTyping:)])
519                 [formDelegate _webProcessPlugInBrowserContextController:m_controller textDidChangeInTextField:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(inputElement)) inFrame:wrapper(*frame) initiatedByUserTyping:initiatedByUserTyping];
520         }
521
522         void willBeginInputSession(WebPage*, Element* element, WebFrame* frame, RefPtr<API::Object>& userData, bool userIsInteracting) override
523         {
524             auto formDelegate = m_controller->_formDelegate.get();
525
526             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willBeginInputSessionForElement:inFrame:userIsInteracting:)]) {
527                 NSObject<NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willBeginInputSessionForElement:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element)) inFrame:wrapper(*frame) userIsInteracting:userIsInteracting];
528                 encodeUserObject(userObject, userData);
529             } else if (userIsInteracting && [formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willBeginInputSessionForElement:inFrame:)]) {
530                 // FIXME: We check userIsInteracting so that we don't begin an input session for a
531                 // programmatic focus that doesn't cause the keyboard to appear. But this misses the case of
532                 // a programmatic focus happening while the keyboard is already shown. Once we have a way to
533                 // know the keyboard state in the Web Process, we should refine the condition.
534                 NSObject<NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willBeginInputSessionForElement:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element)) inFrame:wrapper(*frame)];
535                 encodeUserObject(userObject, userData);
536             }
537         }
538
539         bool shouldNotifyOnFormChanges(WebKit::WebPage*) override
540         {
541             auto formDelegate = m_controller->_formDelegate.get();
542
543             if (![formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerShouldNotifyOnFormChanges:)])
544                 return false;
545
546             return [formDelegate _webProcessPlugInBrowserContextControllerShouldNotifyOnFormChanges:m_controller];
547         }
548
549         void didAssociateFormControls(WebKit::WebPage*, const Vector<RefPtr<WebCore::Element>>& elements) override
550         {
551             auto formDelegate = m_controller->_formDelegate.get();
552
553             if (![formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:didAssociateFormControls:)])
554                 return;
555
556             auto controls = adoptNS([[NSMutableArray alloc] initWithCapacity:elements.size()]);
557             for (const auto& element : elements)
558                 [controls addObject:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element.get()))];
559             return [formDelegate _webProcessPlugInBrowserContextController:m_controller didAssociateFormControls:controls.get()];
560         }
561
562     private:
563         WKWebProcessPlugInBrowserContextController *m_controller;
564     };
565
566     if (formDelegate)
567         _page->setInjectedBundleFormClient(std::make_unique<FormClient>(self));
568     else
569         _page->setInjectedBundleFormClient(nullptr);
570 }
571
572 - (id <WKWebProcessPlugInEditingDelegate>)_editingDelegate
573 {
574     return _editingDelegate.getAutoreleased();
575 }
576
577 static inline WKEditorInsertAction toWK(EditorInsertAction action)
578 {
579     switch (action) {
580     case EditorInsertAction::Typed:
581         return WKEditorInsertActionTyped;
582     case EditorInsertAction::Pasted:
583         return WKEditorInsertActionPasted;
584     case EditorInsertAction::Dropped:
585         return WKEditorInsertActionDropped;
586     }
587 }
588
589 - (void)_setEditingDelegate:(id <WKWebProcessPlugInEditingDelegate>)editingDelegate
590 {
591     _editingDelegate = editingDelegate;
592
593     class Client final : public API::InjectedBundle::EditorClient {
594     public:
595         explicit Client(WKWebProcessPlugInBrowserContextController *controller)
596             : m_controller { controller }
597             , m_delegateMethods { m_controller->_editingDelegate.get() }
598         {
599         }
600
601     private:
602         bool shouldInsertText(WebPage&, StringImpl* text, Range* rangeToReplace, EditorInsertAction action) final
603         {
604             if (!m_delegateMethods.shouldInsertText)
605                 return true;
606
607             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller shouldInsertText:String(text) replacingRange:wrapper(*InjectedBundleRangeHandle::getOrCreate(rangeToReplace)) givenAction:toWK(action)];
608         }
609
610         bool shouldChangeSelectedRange(WebPage&, Range* fromRange, Range* toRange, EAffinity affinity, bool stillSelecting) final
611         {
612             if (!m_delegateMethods.shouldChangeSelectedRange)
613                 return true;
614
615             auto apiFromRange = fromRange ? adoptNS([[WKDOMRange alloc] _initWithImpl:fromRange]) : nil;
616             auto apiToRange = toRange ? adoptNS([[WKDOMRange alloc] _initWithImpl:toRange]) : nil;
617 #if PLATFORM(IOS)
618             UITextStorageDirection apiAffinity = affinity == UPSTREAM ? UITextStorageDirectionBackward : UITextStorageDirectionForward;
619 #else
620             NSSelectionAffinity apiAffinity = affinity == UPSTREAM ? NSSelectionAffinityUpstream : NSSelectionAffinityDownstream;
621 #endif
622
623             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller shouldChangeSelectedRange:apiFromRange.get() toRange:apiToRange.get() affinity:apiAffinity stillSelecting:stillSelecting];
624         }
625
626         void didChange(WebKit::WebPage&, StringImpl*) final
627         {
628             if (!m_delegateMethods.didChange)
629                 return;
630
631             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextControllerDidChangeByEditing:m_controller];
632         }
633
634         void willWriteToPasteboard(WebKit::WebPage&, WebCore::Range* range) final
635         {
636             if (!m_delegateMethods.willWriteToPasteboard)
637                 return;
638
639             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller willWriteRangeToPasteboard:wrapper(*InjectedBundleRangeHandle::getOrCreate(range).get())];
640         }
641
642         void getPasteboardDataForRange(WebKit::WebPage&, WebCore::Range* range, Vector<String>& pasteboardTypes, Vector<RefPtr<WebCore::SharedBuffer>>& pasteboardData) final
643         {
644             if (!m_delegateMethods.getPasteboardDataForRange)
645                 return;
646
647             auto dataByType = [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller pasteboardDataForRange:wrapper(*InjectedBundleRangeHandle::getOrCreate(range).get())];
648             for (NSString *type in dataByType) {
649                 pasteboardTypes.append(type);
650                 pasteboardData.append(SharedBuffer::create(dataByType[type]));
651             };
652         }
653
654         void didWriteToPasteboard(WebKit::WebPage&) final
655         {
656             if (!m_delegateMethods.didWriteToPasteboard)
657                 return;
658
659             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextControllerDidWriteToPasteboard:m_controller];
660         }
661
662         bool performTwoStepDrop(WebKit::WebPage&, WebCore::DocumentFragment& fragment, WebCore::Range& range, bool isMove) final
663         {
664             if (!m_delegateMethods.performTwoStepDrop)
665                 return false;
666
667             auto rangeHandle = InjectedBundleRangeHandle::getOrCreate(&range);
668             auto nodeHandle = InjectedBundleNodeHandle::getOrCreate(&fragment);
669             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller performTwoStepDrop:wrapper(*nodeHandle) atDestination:wrapper(*rangeHandle) isMove:isMove];
670         }
671
672         WKWebProcessPlugInBrowserContextController *m_controller;
673         const struct DelegateMethods {
674             DelegateMethods(RetainPtr<id <WKWebProcessPlugInEditingDelegate>> delegate)
675                 : shouldInsertText([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:shouldInsertText:replacingRange:givenAction:)])
676                 , shouldChangeSelectedRange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:shouldChangeSelectedRange:toRange:affinity:stillSelecting:)])
677                 , didChange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerDidChangeByEditing:)])
678                 , willWriteToPasteboard([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willWriteRangeToPasteboard:)])
679                 , getPasteboardDataForRange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:pasteboardDataForRange:)])
680                 , didWriteToPasteboard([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerDidWriteToPasteboard:)])
681                 , performTwoStepDrop([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:performTwoStepDrop:atDestination:isMove:)])
682             {
683             }
684
685             bool shouldInsertText;
686             bool shouldChangeSelectedRange;
687             bool didChange;
688             bool willWriteToPasteboard;
689             bool getPasteboardDataForRange;
690             bool didWriteToPasteboard;
691             bool performTwoStepDrop;
692         } m_delegateMethods;
693     };
694
695     if (editingDelegate)
696         _page->setInjectedBundleEditorClient(std::make_unique<Client>(self));
697     else
698         _page->setInjectedBundleEditorClient(nullptr);
699 }
700
701 - (BOOL)_defersLoading
702 {
703     return _page->defersLoading();
704 }
705
706 - (void)_setDefersLoading:(BOOL)defersLoading
707 {
708     _page->setDefersLoading(defersLoading);
709 }
710
711 - (BOOL)_usesNonPersistentWebsiteDataStore
712 {
713     return _page->usesEphemeralSession();
714 }
715
716 @end
717
718 #endif // WK_API_ENABLED