Add a context menu delegate method that takes a userInfo parameter
[WebKit-https.git] / Source / WebKit2 / UIProcess / Cocoa / UIDelegate.mm
1 /*
2  * Copyright (C) 2014 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 "UIDelegate.h"
28
29 #if WK_API_ENABLED
30
31 #import "CompletionHandlerCallChecker.h"
32 #import "NavigationActionData.h"
33 #import "WKFrameInfoInternal.h"
34 #import "WKNavigationActionInternal.h"
35 #import "WKSecurityOriginInternal.h"
36 #import "WKWebViewConfigurationInternal.h"
37 #import "WKWebViewInternal.h"
38 #import "WKWindowFeaturesInternal.h"
39 #import "WKUIDelegatePrivate.h"
40 #import "_WKContextMenuElementInfo.h"
41 #import "_WKFrameHandleInternal.h"
42 #import <WebCore/SecurityOriginData.h>
43 #import <WebCore/URL.h>
44
45 namespace WebKit {
46
47 UIDelegate::UIDelegate(WKWebView *webView)
48     : m_webView(webView)
49 {
50 }
51
52 UIDelegate::~UIDelegate()
53 {
54 }
55
56 #if ENABLE(CONTEXT_MENUS)
57 std::unique_ptr<API::ContextMenuClient> UIDelegate::createContextMenuClient()
58 {
59     return std::make_unique<ContextMenuClient>(*this);
60 }
61 #endif
62
63 std::unique_ptr<API::UIClient> UIDelegate::createUIClient()
64 {
65     return std::make_unique<UIClient>(*this);
66 }
67
68 RetainPtr<id <WKUIDelegate> > UIDelegate::delegate()
69 {
70     return m_delegate.get();
71 }
72
73 void UIDelegate::setDelegate(id <WKUIDelegate> delegate)
74 {
75     m_delegate = delegate;
76
77     m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures = [delegate respondsToSelector:@selector(webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:)];
78     m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)];
79     m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
80     m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:)];
81     m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler = [delegate respondsToSelector:@selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:)];
82     m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded = [delegate respondsToSelector:@selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:)];
83     m_delegateMethods.webViewPrintFrame = [delegate respondsToSelector:@selector(_webView:printFrame:)];
84     m_delegateMethods.webViewDidClose = [delegate respondsToSelector:@selector(webViewDidClose:)];
85     m_delegateMethods.webViewClose = [delegate respondsToSelector:@selector(_webViewClose:)];
86     m_delegateMethods.webViewFullscreenMayReturnToInline = [delegate respondsToSelector:@selector(_webViewFullscreenMayReturnToInline:)];
87     m_delegateMethods.webViewDidEnterFullscreen = [delegate respondsToSelector:@selector(_webViewDidEnterFullscreen:)];
88     m_delegateMethods.webViewDidExitFullscreen = [delegate respondsToSelector:@selector(_webViewDidExitFullscreen:)];
89 #if PLATFORM(IOS)
90 #if HAVE(APP_LINKS)
91     m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement = [delegate respondsToSelector:@selector(_webView:shouldIncludeAppLinkActionsForElement:)];
92 #endif
93     m_delegateMethods.webViewActionsForElementDefaultActions = [delegate respondsToSelector:@selector(_webView:actionsForElement:defaultActions:)];
94     m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint = [delegate respondsToSelector:@selector(_webView:didNotHandleTapAsClickAtPoint:)];
95 #endif
96     m_delegateMethods.webViewImageOrMediaDocumentSizeChanged = [delegate respondsToSelector:@selector(_webView:imageOrMediaDocumentSizeChanged:)];
97
98 #if ENABLE(CONTEXT_MENUS)
99     m_delegateMethods.webViewContextMenuForElement = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:)];
100     m_delegateMethods.webViewContextMenuForElementUserInfo = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:userInfo:)];
101 #endif
102 }
103
104 #if ENABLE(CONTEXT_MENUS)
105 UIDelegate::ContextMenuClient::ContextMenuClient(UIDelegate& uiDelegate)
106     : m_uiDelegate(uiDelegate)
107 {
108 }
109
110 UIDelegate::ContextMenuClient::~ContextMenuClient()
111 {
112 }
113
114 RetainPtr<NSMenu> UIDelegate::ContextMenuClient::menuFromProposedMenu(WebKit::WebPageProxy&, NSMenu *menu, const WebKit::WebHitTestResultData&, API::Object* userInfo)
115 {
116     if (!m_uiDelegate.m_delegateMethods.webViewContextMenuForElement && !m_uiDelegate.m_delegateMethods.webViewContextMenuForElementUserInfo)
117         return menu;
118
119     auto delegate = m_uiDelegate.m_delegate.get();
120     if (!delegate)
121         return menu;
122
123     auto contextMenuElementInfo = adoptNS([[_WKContextMenuElementInfo alloc] init]);
124
125     if (m_uiDelegate.m_delegateMethods.webViewContextMenuForElement)
126         return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView contextMenu:menu forElement:contextMenuElementInfo.get()];
127
128     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView contextMenu:menu forElement:contextMenuElementInfo.get() userInfo:static_cast<id <NSSecureCoding>>(userInfo->wrapper())];
129 }
130 #endif
131
132 UIDelegate::UIClient::UIClient(UIDelegate& uiDelegate)
133     : m_uiDelegate(uiDelegate)
134 {
135 }
136
137 UIDelegate::UIClient::~UIClient()
138 {
139 }
140
141 PassRefPtr<WebKit::WebPageProxy> UIDelegate::UIClient::createNewPage(WebKit::WebPageProxy*, WebKit::WebFrameProxy* initiatingFrame, const WebCore::SecurityOriginData& securityOriginData, const WebCore::ResourceRequest& request, const WebCore::WindowFeatures& windowFeatures, const WebKit::NavigationActionData& navigationActionData)
142 {
143     if (!m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures)
144         return nullptr;
145
146     auto delegate = m_uiDelegate.m_delegate.get();
147     if (!delegate)
148         return nullptr;
149
150     auto configuration = adoptNS([m_uiDelegate.m_webView->_configuration copy]);
151     [configuration _setRelatedWebView:m_uiDelegate.m_webView];
152
153     auto sourceFrameInfo = API::FrameInfo::create(*initiatingFrame, securityOriginData.securityOrigin());
154
155     bool shouldOpenAppLinks = !hostsAreEqual(WebCore::URL(WebCore::ParsedURLString, initiatingFrame->url()), request.url());
156     auto apiNavigationAction = API::NavigationAction::create(navigationActionData, sourceFrameInfo.ptr(), nullptr, request, WebCore::URL(), shouldOpenAppLinks);
157
158     auto apiWindowFeatures = API::WindowFeatures::create(windowFeatures);
159
160     RetainPtr<WKWebView> webView = [delegate webView:m_uiDelegate.m_webView createWebViewWithConfiguration:configuration.get() forNavigationAction:wrapper(apiNavigationAction) windowFeatures:wrapper(apiWindowFeatures)];
161
162     if (!webView)
163         return nullptr;
164
165     if ([webView->_configuration _relatedWebView] != m_uiDelegate.m_webView)
166         [NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
167
168     return webView->_page.get();
169 }
170
171 void UIDelegate::UIClient::runJavaScriptAlert(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, std::function<void ()> completionHandler)
172 {
173     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler) {
174         completionHandler();
175         return;
176     }
177
178     auto delegate = m_uiDelegate.m_delegate.get();
179     if (!delegate) {
180         completionHandler();
181         return;
182     }
183
184     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
185     [delegate webView:m_uiDelegate.m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:[completionHandler, checker] {
186         completionHandler();
187         checker->didCallCompletionHandler();
188     }];
189 }
190
191 void UIDelegate::UIClient::runJavaScriptConfirm(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, std::function<void (bool)> completionHandler)
192 {
193     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
194         completionHandler(false);
195         return;
196     }
197
198     auto delegate = m_uiDelegate.m_delegate.get();
199     if (!delegate) {
200         completionHandler(false);
201         return;
202     }
203
204     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
205     [delegate webView:m_uiDelegate.m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:[completionHandler, checker](BOOL result) {
206         completionHandler(result);
207         checker->didCallCompletionHandler();
208     }];
209 }
210
211 void UIDelegate::UIClient::runJavaScriptPrompt(WebKit::WebPageProxy*, const WTF::String& message, const WTF::String& defaultValue, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, std::function<void (const WTF::String&)> completionHandler)
212 {
213     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler) {
214         completionHandler(String());
215         return;
216     }
217
218     auto delegate = m_uiDelegate.m_delegate.get();
219     if (!delegate) {
220         completionHandler(String());
221         return;
222     }
223
224     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
225     [delegate webView:m_uiDelegate.m_webView runJavaScriptTextInputPanelWithPrompt:message defaultText:defaultValue initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:[completionHandler, checker](NSString *result) {
226         completionHandler(result);
227         checker->didCallCompletionHandler();
228     }];
229 }
230
231 void UIDelegate::UIClient::exceededDatabaseQuota(WebPageProxy*, WebFrameProxy*, API::SecurityOrigin* securityOrigin, const WTF::String& databaseName, const WTF::String& displayName, unsigned long long currentQuota, unsigned long long currentOriginUsage, unsigned long long currentUsage, unsigned long long expectedUsage, std::function<void (unsigned long long)> completionHandler)
232 {
233     if (!m_uiDelegate.m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
234         completionHandler(currentQuota);
235         return;
236     }
237
238     auto delegate = m_uiDelegate.m_delegate.get();
239     if (!delegate) {
240         completionHandler(currentQuota);
241         return;
242     }
243
244     ASSERT(securityOrigin);
245     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
246     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideDatabaseQuotaForSecurityOrigin:wrapper(*securityOrigin) currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:[completionHandler, checker](unsigned long long newQuota) {
247         checker->didCallCompletionHandler();
248         completionHandler(newQuota);
249     }];
250 }
251
252 void UIDelegate::UIClient::reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin& securityOrigin, uint64_t currentQuota, uint64_t totalBytesNeeded, std::function<void (unsigned long long)> completionHandler)
253 {
254     if (!m_uiDelegate.m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded) {
255         completionHandler(currentQuota);
256         return;
257     }
258
259     auto delegate = m_uiDelegate.m_delegate.get();
260     if (!delegate) {
261         completionHandler(currentQuota);
262         return;
263     }
264
265     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:));
266     RefPtr<API::SecurityOrigin> apiOrigin = API::SecurityOrigin::create(securityOrigin);
267     
268     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideWebApplicationCacheQuotaForSecurityOrigin:wrapper(*apiOrigin) currentQuota:currentQuota totalBytesNeeded:totalBytesNeeded decisionHandler:[completionHandler, checker](unsigned long long newQuota) {
269         checker->didCallCompletionHandler();
270         completionHandler(newQuota);
271     }];
272 }
273
274 void UIDelegate::UIClient::printFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy)
275 {
276     ASSERT_ARG(webFrameProxy, webFrameProxy);
277
278     if (!m_uiDelegate.m_delegateMethods.webViewPrintFrame)
279         return;
280
281     auto delegate = m_uiDelegate.m_delegate.get();
282     if (!delegate)
283         return;
284
285     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView printFrame:wrapper(API::FrameHandle::create(webFrameProxy->frameID()))];
286 }
287
288 void UIDelegate::UIClient::close(WebKit::WebPageProxy*)
289 {
290     if (m_uiDelegate.m_delegateMethods.webViewClose) {
291         auto delegate = m_uiDelegate.m_delegate.get();
292         if (!delegate)
293             return;
294
295         [(id <WKUIDelegatePrivate>)delegate _webViewClose:m_uiDelegate.m_webView];
296         return;
297     }
298
299     if (!m_uiDelegate.m_delegateMethods.webViewDidClose)
300         return;
301
302     auto delegate = m_uiDelegate.m_delegate.get();
303     if (!delegate)
304         return;
305
306     [delegate webViewDidClose:m_uiDelegate.m_webView];
307 }
308
309 void UIDelegate::UIClient::fullscreenMayReturnToInline(WebKit::WebPageProxy*)
310 {
311     if (!m_uiDelegate.m_delegateMethods.webViewFullscreenMayReturnToInline)
312         return;
313     
314     auto delegate = m_uiDelegate.m_delegate.get();
315     if (!delegate)
316         return;
317     
318     [(id <WKUIDelegatePrivate>)delegate _webViewFullscreenMayReturnToInline:m_uiDelegate.m_webView];
319 }
320
321 void UIDelegate::UIClient::didEnterFullscreen(WebKit::WebPageProxy*)
322 {
323     if (!m_uiDelegate.m_delegateMethods.webViewDidEnterFullscreen)
324         return;
325
326     auto delegate = m_uiDelegate.m_delegate.get();
327     if (!delegate)
328         return;
329
330     [(id <WKUIDelegatePrivate>)delegate _webViewDidEnterFullscreen:m_uiDelegate.m_webView];
331 }
332
333 void UIDelegate::UIClient::didExitFullscreen(WebKit::WebPageProxy*)
334 {
335     if (!m_uiDelegate.m_delegateMethods.webViewDidExitFullscreen)
336         return;
337
338     auto delegate = m_uiDelegate.m_delegate.get();
339     if (!delegate)
340         return;
341
342     [(id <WKUIDelegatePrivate>)delegate _webViewDidExitFullscreen:m_uiDelegate.m_webView];
343 }
344     
345 #if PLATFORM(IOS)
346 #if HAVE(APP_LINKS)
347 bool UIDelegate::UIClient::shouldIncludeAppLinkActionsForElement(_WKActivatedElementInfo *elementInfo)
348 {
349     if (!m_uiDelegate.m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement)
350         return true;
351
352     auto delegate = m_uiDelegate.m_delegate.get();
353     if (!delegate)
354         return true;
355
356     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView shouldIncludeAppLinkActionsForElement:elementInfo];
357 }
358 #endif
359
360 RetainPtr<NSArray> UIDelegate::UIClient::actionsForElement(_WKActivatedElementInfo *elementInfo, RetainPtr<NSArray> defaultActions)
361 {
362     if (!m_uiDelegate.m_delegateMethods.webViewActionsForElementDefaultActions)
363         return WTF::move(defaultActions);
364
365     auto delegate = m_uiDelegate.m_delegate.get();
366     if (!delegate)
367         return defaultActions;
368
369     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView actionsForElement:elementInfo defaultActions:defaultActions.get()];
370 }
371
372 void UIDelegate::UIClient::didNotHandleTapAsClick(const WebCore::IntPoint& point)
373 {
374     if (!m_uiDelegate.m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint)
375         return;
376
377     auto delegate = m_uiDelegate.m_delegate.get();
378     if (!delegate)
379         return;
380
381     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView didNotHandleTapAsClickAtPoint:point];
382 }
383 #endif
384
385 void UIDelegate::UIClient::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize)
386 {
387     if (!m_uiDelegate.m_delegateMethods.webViewImageOrMediaDocumentSizeChanged)
388         return;
389
390     auto delegate = m_uiDelegate.m_delegate.get();
391     if (!delegate)
392         return;
393
394     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView imageOrMediaDocumentSizeChanged:newSize];
395 }
396
397 } // namespace WebKit
398
399 #endif // WK_API_ENABLED