28dc0669df5dd20e0bc1e0667ece9f7e5394c492
[WebKit-https.git] / Source / WebKit / 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 "WebPage.h"
57 #import "WebProcess.h"
58 #import "_WKRemoteObjectRegistryInternal.h"
59 #import "_WKRenderingProgressEventsInternal.h"
60 #import "_WKSameDocumentNavigationTypeInternal.h"
61 #import <WebCore/Document.h>
62 #import <WebCore/DocumentFragment.h>
63 #import <WebCore/Frame.h>
64 #import <WebCore/HTMLFormElement.h>
65 #import <WebCore/HTMLInputElement.h>
66 #import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
67 #import <wtf/WeakObjCPtr.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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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     auto pluginContextController = (__bridge 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         return WKStringCreateWithCFString((__bridge CFStringRef)string);
228     }
229     
230     return nullptr;
231 }
232
233 static void setUpPageLoaderClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page)
234 {
235     WKBundlePageLoaderClientV9 client;
236     memset(&client, 0, sizeof(client));
237
238     client.base.version = 8;
239     client.base.clientInfo = (__bridge void*)contextController;
240     client.didStartProvisionalLoadForFrame = didStartProvisionalLoadForFrame;
241     client.didReceiveServerRedirectForProvisionalLoadForFrame = didReceiveServerRedirectForProvisionalLoadForFrame;
242     client.didCommitLoadForFrame = didCommitLoadForFrame;
243     client.didFinishDocumentLoadForFrame = didFinishDocumentLoadForFrame;
244     client.didFailProvisionalLoadWithErrorForFrame = didFailProvisionalLoadWithErrorForFrame;
245     client.didFailLoadWithErrorForFrame = didFailLoadWithErrorForFrame;
246     client.didSameDocumentNavigationForFrame = didSameDocumentNavigationForFrame;
247     client.didFinishLoadForFrame = didFinishLoadForFrame;
248     client.globalObjectIsAvailableForFrame = globalObjectIsAvailableForFrame;
249     client.didRemoveFrameFromHierarchy = didRemoveFrameFromHierarchy;
250     client.didHandleOnloadEventsForFrame = didHandleOnloadEventsForFrame;
251     client.didFirstVisuallyNonEmptyLayoutForFrame = didFirstVisuallyNonEmptyLayoutForFrame;
252     client.userAgentForURL = userAgentForURL;
253
254     client.didLayoutForFrame = didLayoutForFrame;
255     client.didLayout = didReachLayoutMilestone;
256
257     WKBundlePageSetPageLoaderClient(toAPI(&page), &client.base);
258 }
259
260 static WKURLRequestRef willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
261 {
262     auto pluginContextController = (__bridge WKWebProcessPlugInBrowserContextController *)clientInfo;
263     auto loadDelegate = pluginContextController->_loadDelegate.get();
264
265     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:willSendRequestForResource:request:redirectResponse:)]) {
266         NSURLRequest *originalRequest = wrapper(*toImpl(request));
267         RetainPtr<NSURLRequest> substituteRequest = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) willSendRequestForResource:resourceIdentifier
268             request:originalRequest redirectResponse:toImpl(redirectResponse)->resourceResponse().nsURLResponse()];
269
270         if (substituteRequest != originalRequest)
271             return substituteRequest ? WKURLRequestCreateWithNSURLRequest(substituteRequest.get()) : nullptr;
272     } else if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:willSendRequest:redirectResponse:)]) {
273         NSURLRequest *originalRequest = wrapper(*toImpl(request));
274         RetainPtr<NSURLRequest> substituteRequest = [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) willSendRequest:originalRequest
275             redirectResponse:toImpl(redirectResponse)->resourceResponse().nsURLResponse()];
276
277         if (substituteRequest != originalRequest)
278             return substituteRequest ? WKURLRequestCreateWithNSURLRequest(substituteRequest.get()) : nullptr;
279     }
280
281     WKRetain(request);
282     return request;
283 }
284
285 static void didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKURLRequestRef request, bool pageIsProvisionallyLoading, const void* clientInfo)
286 {
287     auto pluginContextController = (__bridge WKWebProcessPlugInBrowserContextController *)clientInfo;
288     auto loadDelegate = pluginContextController->_loadDelegate.get();
289
290     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didInitiateLoadForResource:request:pageIsProvisionallyLoading:)]) {
291         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didInitiateLoadForResource:resourceIdentifier request:wrapper(*toImpl(request))
292             pageIsProvisionallyLoading:pageIsProvisionallyLoading];
293     } else if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didInitiateLoadForResource:request:)]) {
294         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController frame:wrapper(*toImpl(frame)) didInitiateLoadForResource:resourceIdentifier request:wrapper(*toImpl(request))];
295     }
296 }
297
298 static void didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, const void* clientInfo)
299 {
300     auto pluginContextController = (__bridge WKWebProcessPlugInBrowserContextController *)clientInfo;
301     auto loadDelegate = pluginContextController->_loadDelegate.get();
302
303     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFinishLoadForResource:)]) {
304         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController
305                                                          frame:wrapper(*toImpl(frame))
306                                       didFinishLoadForResource:resourceIdentifier];
307     }
308 }
309
310 static void didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef frame, uint64_t resourceIdentifier, WKErrorRef error, const void* clientInfo)
311 {
312     auto pluginContextController = (__bridge WKWebProcessPlugInBrowserContextController *)clientInfo;
313     auto loadDelegate = pluginContextController->_loadDelegate.get();
314
315     if ([loadDelegate respondsToSelector:@selector(webProcessPlugInBrowserContextController:frame:didFailLoadForResource:error:)]) {
316         [loadDelegate webProcessPlugInBrowserContextController:pluginContextController
317                                                          frame:wrapper(*toImpl(frame))
318                                         didFailLoadForResource:resourceIdentifier
319                                                          error:wrapper(*toImpl(error))];
320     }
321 }
322
323 static void setUpResourceLoadClient(WKWebProcessPlugInBrowserContextController *contextController, WebPage& page)
324 {
325     WKBundlePageResourceLoadClientV1 client;
326     memset(&client, 0, sizeof(client));
327
328     client.base.version = 1;
329     client.base.clientInfo = (__bridge void*) contextController;
330     client.willSendRequestForFrame = willSendRequestForFrame;
331     client.didInitiateLoadForResource = didInitiateLoadForResource;
332     client.didFinishLoadForResource = didFinishLoadForResource;
333     client.didFailLoadForResource = didFailLoadForResource;
334
335     WKBundlePageSetResourceLoadClient(toAPI(&page), &client.base);
336 }
337
338 - (id <WKWebProcessPlugInLoadDelegate>)loadDelegate
339 {
340     return _loadDelegate.getAutoreleased();
341 }
342
343 - (void)setLoadDelegate:(id <WKWebProcessPlugInLoadDelegate>)loadDelegate
344 {
345     _loadDelegate = loadDelegate;
346
347     if (loadDelegate) {
348         setUpPageLoaderClient(self, *_page);
349         setUpResourceLoadClient(self, *_page);
350     } else {
351         WKBundlePageSetPageLoaderClient(toAPI(_page.get()), nullptr);
352         WKBundlePageSetResourceLoadClient(toAPI(_page.get()), nullptr);
353     }
354 }
355
356 - (void)dealloc
357 {
358     if (_remoteObjectRegistry) {
359         WebProcess::singleton().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID());
360         [_remoteObjectRegistry _invalidate];
361     }
362
363     _page->~WebPage();
364
365     [super dealloc];
366 }
367
368 - (WKDOMDocument *)mainFrameDocument
369 {
370     Frame* webCoreMainFrame = _page->mainFrame();
371     if (!webCoreMainFrame)
372         return nil;
373
374     return toWKDOMDocument(webCoreMainFrame->document());
375 }
376
377 - (WKDOMRange *)selectedRange
378 {
379     RefPtr<Range> range = _page->currentSelectionAsRange();
380     if (!range)
381         return nil;
382
383     return toWKDOMRange(range.get());
384 }
385
386 - (WKWebProcessPlugInFrame *)mainFrame
387 {
388     return wrapper(_page->mainWebFrame());
389 }
390
391 - (WKWebProcessPlugInPageGroup *)pageGroup
392 {
393     return wrapper(*_page->pageGroup());
394 }
395
396 #pragma mark WKObject protocol implementation
397
398 - (API::Object&)_apiObject
399 {
400     return *_page;
401 }
402
403 @end
404
405 @implementation WKWebProcessPlugInBrowserContextController (WKPrivate)
406
407 - (WKBundlePageRef)_bundlePageRef
408 {
409     return toAPI(_page.get());
410 }
411
412 - (WKBrowsingContextHandle *)handle
413 {
414     return [[[WKBrowsingContextHandle alloc] _initWithPageID:_page->pageID()] autorelease];
415 }
416
417 + (instancetype)lookUpBrowsingContextFromHandle:(WKBrowsingContextHandle *)handle
418 {
419     return wrapper(WebProcess::singleton().webPage(handle.pageID));
420 }
421
422 - (_WKRemoteObjectRegistry *)_remoteObjectRegistry
423 {
424     if (!_remoteObjectRegistry) {
425         _remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithWebPage:*_page]);
426         WebProcess::singleton().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->pageID(), [_remoteObjectRegistry remoteObjectRegistry]);
427     }
428
429     return _remoteObjectRegistry.get();
430 }
431
432 - (id <WKWebProcessPlugInFormDelegatePrivate>)_formDelegate
433 {
434     return _formDelegate.getAutoreleased();
435 }
436
437 - (void)_setFormDelegate:(id <WKWebProcessPlugInFormDelegatePrivate>)formDelegate
438 {
439     _formDelegate = formDelegate;
440
441     class FormClient : public API::InjectedBundle::FormClient {
442     public:
443         explicit FormClient(WKWebProcessPlugInBrowserContextController *controller)
444             : m_controller(controller)
445         {
446         }
447
448         virtual ~FormClient() { }
449
450         void didFocusTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame) override
451         {
452             auto formDelegate = m_controller->_formDelegate.get();
453
454             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:didFocusTextField:inFrame:)])
455                 [formDelegate _webProcessPlugInBrowserContextController:m_controller didFocusTextField:wrapper(*InjectedBundleNodeHandle::getOrCreate(inputElement).get()) inFrame:wrapper(*frame)];
456         }
457
458         void willSendSubmitEvent(WebPage*, HTMLFormElement* formElement, WebFrame* targetFrame, WebFrame* sourceFrame, const Vector<std::pair<String, String>>& values) override
459         {
460             auto formDelegate = m_controller->_formDelegate.get();
461
462             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSendSubmitEventToForm:inFrame:targetFrame:values:)]) {
463                 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]);
464                 for (const auto& pair : values)
465                     [valueMap setObject:pair.second forKey:pair.first];
466
467                 [formDelegate _webProcessPlugInBrowserContextController:m_controller willSendSubmitEventToForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get())
468                     inFrame:wrapper(*sourceFrame) targetFrame:wrapper(*targetFrame) values:valueMap.get()];
469             }
470         }
471
472         static void encodeUserObject(NSObject <NSSecureCoding> *userObject, RefPtr<API::Object>& userData)
473         {
474             if (!userObject)
475                 return;
476
477             auto archiver = secureArchiver();
478             @try {
479                 [archiver encodeObject:userObject forKey:@"userObject"];
480             } @catch (NSException *exception) {
481                 LOG_ERROR("Failed to encode user object: %@", exception);
482                 return;
483             }
484             [archiver finishEncoding];
485
486             userData = API::Data::createWithoutCopying(archiver.get().encodedData);
487         }
488
489         void willSubmitForm(WebPage*, HTMLFormElement* formElement, WebFrame* frame, WebFrame* sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& values, RefPtr<API::Object>& userData) override
490         {
491             auto formDelegate = m_controller->_formDelegate.get();
492
493             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willSubmitForm:toFrame:fromFrame:withValues:)]) {
494                 auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:values.size()]);
495                 for (const auto& pair : values)
496                     [valueMap setObject:pair.second forKey:pair.first];
497
498                 NSObject <NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willSubmitForm:wrapper(*InjectedBundleNodeHandle::getOrCreate(formElement).get()) toFrame:wrapper(*frame) fromFrame:wrapper(*sourceFrame) withValues:valueMap.get()];
499                 encodeUserObject(userObject, userData);
500             }
501         }
502
503         void textDidChangeInTextField(WebPage*, HTMLInputElement* inputElement, WebFrame* frame, bool initiatedByUserTyping) override
504         {
505             auto formDelegate = m_controller->_formDelegate.get();
506
507             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:textDidChangeInTextField:inFrame:initiatedByUserTyping:)])
508                 [formDelegate _webProcessPlugInBrowserContextController:m_controller textDidChangeInTextField:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(inputElement)) inFrame:wrapper(*frame) initiatedByUserTyping:initiatedByUserTyping];
509         }
510
511         void willBeginInputSession(WebPage*, Element* element, WebFrame* frame, bool userIsInteracting, RefPtr<API::Object>& userData) override
512         {
513             auto formDelegate = m_controller->_formDelegate.get();
514
515             if ([formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willBeginInputSessionForElement:inFrame:userIsInteracting:)]) {
516                 NSObject<NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willBeginInputSessionForElement:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element)) inFrame:wrapper(*frame) userIsInteracting:userIsInteracting];
517                 encodeUserObject(userObject, userData);
518             } else if (userIsInteracting && [formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willBeginInputSessionForElement:inFrame:)]) {
519                 // FIXME: We check userIsInteracting so that we don't begin an input session for a
520                 // programmatic focus that doesn't cause the keyboard to appear. But this misses the case of
521                 // a programmatic focus happening while the keyboard is already shown. Once we have a way to
522                 // know the keyboard state in the Web Process, we should refine the condition.
523                 NSObject<NSSecureCoding> *userObject = [formDelegate _webProcessPlugInBrowserContextController:m_controller willBeginInputSessionForElement:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element)) inFrame:wrapper(*frame)];
524                 encodeUserObject(userObject, userData);
525             }
526         }
527
528         bool shouldNotifyOnFormChanges(WebKit::WebPage*) override
529         {
530             auto formDelegate = m_controller->_formDelegate.get();
531
532             if (![formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerShouldNotifyOnFormChanges:)])
533                 return false;
534
535             return [formDelegate _webProcessPlugInBrowserContextControllerShouldNotifyOnFormChanges:m_controller];
536         }
537
538         void didAssociateFormControls(WebKit::WebPage*, const Vector<RefPtr<WebCore::Element>>& elements) override
539         {
540             auto formDelegate = m_controller->_formDelegate.get();
541
542             if (![formDelegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:didAssociateFormControls:)])
543                 return;
544
545             auto controls = adoptNS([[NSMutableArray alloc] initWithCapacity:elements.size()]);
546             for (const auto& element : elements)
547                 [controls addObject:wrapper(*WebKit::InjectedBundleNodeHandle::getOrCreate(element.get()))];
548             return [formDelegate _webProcessPlugInBrowserContextController:m_controller didAssociateFormControls:controls.get()];
549         }
550
551     private:
552         WKWebProcessPlugInBrowserContextController *m_controller;
553     };
554
555     if (formDelegate)
556         _page->setInjectedBundleFormClient(std::make_unique<FormClient>(self));
557     else
558         _page->setInjectedBundleFormClient(nullptr);
559 }
560
561 - (id <WKWebProcessPlugInEditingDelegate>)_editingDelegate
562 {
563     return _editingDelegate.getAutoreleased();
564 }
565
566 static inline WKEditorInsertAction toWK(EditorInsertAction action)
567 {
568     switch (action) {
569     case EditorInsertAction::Typed:
570         return WKEditorInsertActionTyped;
571     case EditorInsertAction::Pasted:
572         return WKEditorInsertActionPasted;
573     case EditorInsertAction::Dropped:
574         return WKEditorInsertActionDropped;
575     }
576 }
577
578 - (void)_setEditingDelegate:(id <WKWebProcessPlugInEditingDelegate>)editingDelegate
579 {
580     _editingDelegate = editingDelegate;
581
582     class Client final : public API::InjectedBundle::EditorClient {
583     public:
584         explicit Client(WKWebProcessPlugInBrowserContextController *controller)
585             : m_controller { controller }
586             , m_delegateMethods { m_controller->_editingDelegate.get() }
587         {
588         }
589
590     private:
591         bool shouldInsertText(WebPage&, StringImpl* text, Range* rangeToReplace, EditorInsertAction action) final
592         {
593             if (!m_delegateMethods.shouldInsertText)
594                 return true;
595
596             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller shouldInsertText:String(text) replacingRange:wrapper(*InjectedBundleRangeHandle::getOrCreate(rangeToReplace)) givenAction:toWK(action)];
597         }
598
599         bool shouldChangeSelectedRange(WebPage&, Range* fromRange, Range* toRange, EAffinity affinity, bool stillSelecting) final
600         {
601             if (!m_delegateMethods.shouldChangeSelectedRange)
602                 return true;
603
604             auto apiFromRange = fromRange ? adoptNS([[WKDOMRange alloc] _initWithImpl:fromRange]) : nil;
605             auto apiToRange = toRange ? adoptNS([[WKDOMRange alloc] _initWithImpl:toRange]) : nil;
606 #if PLATFORM(IOS)
607             UITextStorageDirection apiAffinity = affinity == UPSTREAM ? UITextStorageDirectionBackward : UITextStorageDirectionForward;
608 #else
609             NSSelectionAffinity apiAffinity = affinity == UPSTREAM ? NSSelectionAffinityUpstream : NSSelectionAffinityDownstream;
610 #endif
611
612             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller shouldChangeSelectedRange:apiFromRange.get() toRange:apiToRange.get() affinity:apiAffinity stillSelecting:stillSelecting];
613         }
614
615         void didChange(WebKit::WebPage&, StringImpl*) final
616         {
617             if (!m_delegateMethods.didChange)
618                 return;
619
620             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextControllerDidChangeByEditing:m_controller];
621         }
622
623         void willWriteToPasteboard(WebKit::WebPage&, WebCore::Range* range) final
624         {
625             if (!m_delegateMethods.willWriteToPasteboard)
626                 return;
627
628             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller willWriteRangeToPasteboard:wrapper(*InjectedBundleRangeHandle::getOrCreate(range).get())];
629         }
630
631         void getPasteboardDataForRange(WebKit::WebPage&, WebCore::Range* range, Vector<String>& pasteboardTypes, Vector<RefPtr<WebCore::SharedBuffer>>& pasteboardData) final
632         {
633             if (!m_delegateMethods.getPasteboardDataForRange)
634                 return;
635
636             auto dataByType = [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller pasteboardDataForRange:wrapper(*InjectedBundleRangeHandle::getOrCreate(range).get())];
637             for (NSString *type in dataByType) {
638                 pasteboardTypes.append(type);
639                 pasteboardData.append(SharedBuffer::create(dataByType[type]));
640             };
641         }
642
643         void didWriteToPasteboard(WebKit::WebPage&) final
644         {
645             if (!m_delegateMethods.didWriteToPasteboard)
646                 return;
647
648             [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextControllerDidWriteToPasteboard:m_controller];
649         }
650
651         bool performTwoStepDrop(WebKit::WebPage&, WebCore::DocumentFragment& fragment, WebCore::Range& range, bool isMove) final
652         {
653             if (!m_delegateMethods.performTwoStepDrop)
654                 return false;
655
656             auto rangeHandle = InjectedBundleRangeHandle::getOrCreate(&range);
657             auto nodeHandle = InjectedBundleNodeHandle::getOrCreate(&fragment);
658             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller performTwoStepDrop:wrapper(*nodeHandle) atDestination:wrapper(*rangeHandle) isMove:isMove];
659         }
660
661         WTF::String replacementURLForResource(WebKit::WebPage&, Ref<WebCore::SharedBuffer>&& resourceData, const WTF::String& mimeType)
662         {
663             if (!m_delegateMethods.replacementURLForResource)
664                 return { };
665
666             NSString *type = (NSString *)mimeType;
667             auto data = resourceData->createNSData();
668             return [m_controller->_editingDelegate.get() _webProcessPlugInBrowserContextController:m_controller replacementURLForResource:data.get() mimeType:type];
669         }
670
671         WKWebProcessPlugInBrowserContextController *m_controller;
672         const struct DelegateMethods {
673             DelegateMethods(RetainPtr<id <WKWebProcessPlugInEditingDelegate>> delegate)
674                 : shouldInsertText([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:shouldInsertText:replacingRange:givenAction:)])
675                 , shouldChangeSelectedRange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:shouldChangeSelectedRange:toRange:affinity:stillSelecting:)])
676                 , didChange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerDidChangeByEditing:)])
677                 , willWriteToPasteboard([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:willWriteRangeToPasteboard:)])
678                 , getPasteboardDataForRange([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:pasteboardDataForRange:)])
679                 , didWriteToPasteboard([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextControllerDidWriteToPasteboard:)])
680                 , performTwoStepDrop([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:performTwoStepDrop:atDestination:isMove:)])
681                 , replacementURLForResource([delegate respondsToSelector:@selector(_webProcessPlugInBrowserContextController:replacementURLForResource:mimeType:)])
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             bool replacementURLForResource;
693         } m_delegateMethods;
694     };
695
696     if (editingDelegate)
697         _page->setInjectedBundleEditorClient(std::make_unique<Client>(self));
698     else
699         _page->setInjectedBundleEditorClient(nullptr);
700 }
701
702 - (BOOL)_defersLoading
703 {
704     return _page->defersLoading();
705 }
706
707 - (void)_setDefersLoading:(BOOL)defersLoading
708 {
709     _page->setDefersLoading(defersLoading);
710 }
711
712 - (BOOL)_usesNonPersistentWebsiteDataStore
713 {
714     return _page->usesEphemeralSession();
715 }
716
717 @end
718
719 #endif // WK_API_ENABLED