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 "WKWebViewConfigurationInternal.h"
36#import "WKWebViewInternal.h"
37#import "WKWindowFeaturesInternal.h"
38#import "WKUIDelegatePrivate.h"
39#import "_WKFrameHandleInternal.h"
40#import "_WKSecurityOriginInternal.h"
41
42namespace WebKit {
43
44UIDelegate::UIDelegate(WKWebView *webView)
45    : m_webView(webView)
46{
47}
48
49UIDelegate::~UIDelegate()
50{
51}
52
53std::unique_ptr<API::UIClient> UIDelegate::createUIClient()
54{
55    return std::make_unique<UIClient>(*this);
56}
57
58RetainPtr<id <WKUIDelegate> > UIDelegate::delegate()
59{
60    return m_delegate.get();
61}
62
63void UIDelegate::setDelegate(id <WKUIDelegate> delegate)
64{
65    m_delegate = delegate;
66
67    m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures = [delegate respondsToSelector:@selector(webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:)];
68    m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)];
69    m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
70    m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:)];
71    m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler = [delegate respondsToSelector:@selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:)];
72    m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded = [delegate respondsToSelector:@selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:)];
73    m_delegateMethods.webViewPrintFrame = [delegate respondsToSelector:@selector(_webView:printFrame:)];
74#if PLATFORM(IOS)
75    m_delegateMethods.webViewActionsForElementDefaultActions = [delegate respondsToSelector:@selector(_webView:actionsForElement:defaultActions:)];
76    m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint = [delegate respondsToSelector:@selector(_webView:didNotHandleTapAsClickAtPoint:)];
77#endif
78}
79
80UIDelegate::UIClient::UIClient(UIDelegate& uiDelegate)
81    : m_uiDelegate(uiDelegate)
82{
83}
84
85UIDelegate::UIClient::~UIClient()
86{
87}
88
89PassRefPtr<WebKit::WebPageProxy> UIDelegate::UIClient::createNewPage(WebKit::WebPageProxy*, WebKit::WebFrameProxy* initiatingFrame, const WebCore::ResourceRequest& request, const WebCore::WindowFeatures& windowFeatures, const WebKit::NavigationActionData& navigationActionData)
90{
91    if (!m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures)
92        return nullptr;
93
94    auto delegate = m_uiDelegate.m_delegate.get();
95    if (!delegate)
96        return nullptr;
97
98    auto configuration = adoptNS([m_uiDelegate.m_webView->_configuration copy]);
99    [configuration _setRelatedWebView:m_uiDelegate.m_webView];
100
101    auto navigationAction = adoptNS([[WKNavigationAction alloc] _initWithNavigationActionData:navigationActionData]);
102
103    [navigationAction setSourceFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*initiatingFrame]).get()];
104    [navigationAction setRequest:request.nsURLRequest(WebCore::DoNotUpdateHTTPBody)];
105
106    RetainPtr<WKWebView> webView = [delegate.get() webView:m_uiDelegate.m_webView createWebViewWithConfiguration:configuration.get() forNavigationAction:navigationAction.get() windowFeatures:adoptNS([[WKWindowFeatures alloc] _initWithWindowFeatures:windowFeatures]).get()];
107
108    if (!webView)
109        return nullptr;
110
111    if ([webView->_configuration _relatedWebView] != m_uiDelegate.m_webView)
112        [NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
113
114    return webView->_page.get();
115}
116
117void UIDelegate::UIClient::runJavaScriptAlert(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, std::function<void ()> completionHandler)
118{
119    if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler) {
120        completionHandler();
121        return;
122    }
123
124    auto delegate = m_uiDelegate.m_delegate.get();
125    if (!delegate) {
126        completionHandler();
127        return;
128    }
129
130    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
131    [delegate webView:m_uiDelegate.m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*webFrameProxy]).get() completionHandler:[completionHandler, checker] {
132        completionHandler();
133        checker->didCallCompletionHandler();
134    }];
135}
136
137void UIDelegate::UIClient::runJavaScriptConfirm(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, std::function<void (bool)> completionHandler)
138{
139    if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
140        completionHandler(false);
141        return;
142    }
143
144    auto delegate = m_uiDelegate.m_delegate.get();
145    if (!delegate) {
146        completionHandler(false);
147        return;
148    }
149
150    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
151    [delegate webView:m_uiDelegate.m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*webFrameProxy]).get() completionHandler:[completionHandler, checker](BOOL result) {
152        completionHandler(result);
153        checker->didCallCompletionHandler();
154    }];
155}
156
157void UIDelegate::UIClient::runJavaScriptPrompt(WebKit::WebPageProxy*, const WTF::String& message, const WTF::String& defaultValue, WebKit::WebFrameProxy* webFrameProxy, std::function<void (const WTF::String&)> completionHandler)
158{
159    if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler) {
160        completionHandler(String());
161        return;
162    }
163
164    auto delegate = m_uiDelegate.m_delegate.get();
165    if (!delegate) {
166        completionHandler(String());
167        return;
168    }
169
170    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
171    [delegate webView:m_uiDelegate.m_webView runJavaScriptTextInputPanelWithPrompt:message defaultText:defaultValue initiatedByFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*webFrameProxy]).get() completionHandler:[completionHandler, checker](NSString *result) {
172        completionHandler(result);
173        checker->didCallCompletionHandler();
174    }];
175}
176
177void UIDelegate::UIClient::exceededDatabaseQuota(WebPageProxy*, WebFrameProxy*, WebSecurityOrigin* 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)
178{
179    if (!m_uiDelegate.m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
180        completionHandler(currentQuota);
181        return;
182    }
183
184    auto delegate = m_uiDelegate.m_delegate.get();
185    if (!delegate) {
186        completionHandler(currentQuota);
187        return;
188    }
189
190    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
191    [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideDatabaseQuotaForSecurityOrigin:adoptNS([[_WKSecurityOrigin alloc] _initWithSecurityOrigin:securityOrigin->securityOrigin()]).get() currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:[completionHandler, checker](unsigned long long newQuota) {
192        checker->didCallCompletionHandler();
193        completionHandler(newQuota);
194    }];
195}
196
197void UIDelegate::UIClient::reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin& securityOrigin, uint64_t currentQuota, uint64_t totalBytesNeeded, std::function<void (unsigned long long)> completionHandler)
198{
199    if (!m_uiDelegate.m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded) {
200        completionHandler(currentQuota);
201        return;
202    }
203
204    auto delegate = m_uiDelegate.m_delegate.get();
205    if (!delegate) {
206        completionHandler(currentQuota);
207        return;
208    }
209
210    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:));
211    [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideWebApplicationCacheQuotaForSecurityOrigin:adoptNS([[_WKSecurityOrigin alloc] _initWithSecurityOrigin:securityOrigin]).get() currentQuota:currentQuota totalBytesNeeded:totalBytesNeeded decisionHandler:[completionHandler, checker](unsigned long long newQuota) {
212        checker->didCallCompletionHandler();
213        completionHandler(newQuota);
214    }];
215}
216
217void UIDelegate::UIClient::printFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy)
218{
219    ASSERT_ARG(webFrameProxy, webFrameProxy);
220
221    if (!m_uiDelegate.m_delegateMethods.webViewPrintFrame)
222        return;
223
224    auto delegate = m_uiDelegate.m_delegate.get();
225    if (!delegate)
226        return;
227
228    [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView printFrame:wrapper(*API::FrameHandle::create(webFrameProxy->frameID()))];
229}
230
231#if PLATFORM(IOS)
232RetainPtr<NSArray> UIDelegate::UIClient::actionsForElement(_WKActivatedElementInfo *elementInfo, RetainPtr<NSArray> defaultActions)
233{
234    if (!m_uiDelegate.m_delegateMethods.webViewActionsForElementDefaultActions)
235        return WTF::move(defaultActions);
236
237    auto delegate = m_uiDelegate.m_delegate.get();
238    if (!delegate)
239        return defaultActions;
240
241    return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView actionsForElement:elementInfo defaultActions:defaultActions.get()];
242}
243
244void UIDelegate::UIClient::didNotHandleTapAsClick(const WebCore::IntPoint& point)
245{
246    if (!m_uiDelegate.m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint)
247        return;
248
249    auto delegate = m_uiDelegate.m_delegate.get();
250    if (!delegate)
251        return;
252
253    [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView didNotHandleTapAsClickAtPoint:point];
254}
255#endif
256
257} // namespace WebKit
258
259#endif // WK_API_ENABLED
260