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