mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
	
	
		
			394 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			394 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Copyright (C) 2018 Sony Interactive Entertainment Inc. | ||
|  |  * | ||
|  |  * Redistribution and use in source and binary forms, with or without | ||
|  |  * modification, are permitted provided that the following conditions | ||
|  |  * are met: | ||
|  |  * 1. Redistributions of source code must retain the above copyright | ||
|  |  *    notice, this list of conditions and the following disclaimer. | ||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||
|  |  *    notice, this list of conditions and the following disclaimer in the | ||
|  |  *    documentation and/or other materials provided with the distribution. | ||
|  |  * | ||
|  |  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | ||
|  |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
|  |  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | ||
|  |  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | ||
|  |  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
|  |  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
|  |  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | ||
|  |  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
|  |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|  |  */ | ||
|  | #include "stdafx.h"
 | ||
|  | #include "Common.h"
 | ||
|  | #include "MainWindow.h"
 | ||
|  | #include "PlaywrightLibResource.h"
 | ||
|  | #include "WebKitBrowserWindow.h"
 | ||
|  | #include <WebCore/GDIUtilities.h>
 | ||
|  | #include <WebKit/WKAuthenticationChallenge.h>
 | ||
|  | #include <WebKit/WKAuthenticationDecisionListener.h>
 | ||
|  | #include <WebKit/WKCertificateInfoCurl.h>
 | ||
|  | #include <WebKit/WKCredential.h>
 | ||
|  | #include <WebKit/WKFramePolicyListener.h>
 | ||
|  | #include <WebKit/WKInspector.h>
 | ||
|  | #include <WebKit/WKProtectionSpace.h>
 | ||
|  | #include <WebKit/WKProtectionSpaceCurl.h>
 | ||
|  | #include <WebKit/WKWebsiteDataStoreRef.h>
 | ||
|  | #include <WebKit/WKWebsiteDataStoreRefCurl.h>
 | ||
|  | #include <vector>
 | ||
|  | 
 | ||
|  | std::wstring createPEMString(WKCertificateInfoRef certificateInfo) | ||
|  | { | ||
|  |     auto chainSize = WKCertificateInfoGetCertificateChainSize(certificateInfo); | ||
|  | 
 | ||
|  |     std::wstring pems; | ||
|  | 
 | ||
|  |     for (auto i = 0; i < chainSize; i++) { | ||
|  |         auto certificate = adoptWK(WKCertificateInfoCopyCertificateAtIndex(certificateInfo, i)); | ||
|  |         auto size = WKDataGetSize(certificate.get()); | ||
|  |         auto data = WKDataGetBytes(certificate.get()); | ||
|  | 
 | ||
|  |         for (size_t i = 0; i < size; i++) | ||
|  |             pems.push_back(data[i]); | ||
|  |     } | ||
|  | 
 | ||
|  |     return replaceString(pems, L"\n", L"\r\n"); | ||
|  | } | ||
|  | 
 | ||
|  | WebKitBrowserWindow::WebKitBrowserWindow(BrowserWindowClient& client, HWND mainWnd, WKPageConfigurationRef conf) | ||
|  |     : m_client(client) | ||
|  |     , m_hMainWnd(mainWnd) | ||
|  | { | ||
|  |     RECT rect = { }; | ||
|  |     m_view = adoptWK(WKViewCreate(rect, conf, mainWnd)); | ||
|  |     WKViewSetIsInWindow(m_view.get(), true); | ||
|  | 
 | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  | 
 | ||
|  |     WKPageNavigationClientV0 navigationClient = { }; | ||
|  |     navigationClient.base.version = 0; | ||
|  |     navigationClient.base.clientInfo = this; | ||
|  |     navigationClient.didReceiveAuthenticationChallenge = didReceiveAuthenticationChallenge; | ||
|  |     WKPageSetPageNavigationClient(page, &navigationClient.base); | ||
|  | 
 | ||
|  |     WKPageUIClientV14 uiClient = { }; | ||
|  |     uiClient.base.version = 14; | ||
|  |     uiClient.base.clientInfo = this; | ||
|  |     uiClient.createNewPage = createNewPage; | ||
|  |     uiClient.didNotHandleKeyEvent = didNotHandleKeyEvent; | ||
|  |     uiClient.close = closeWindow; | ||
|  |     uiClient.runJavaScriptAlert = runJavaScriptAlert; | ||
|  |     uiClient.runJavaScriptConfirm = runJavaScriptConfirm; | ||
|  |     uiClient.runJavaScriptPrompt = runJavaScriptPrompt; | ||
|  |     uiClient.runBeforeUnloadConfirmPanel = runBeforeUnloadConfirmPanel; | ||
|  |     uiClient.handleJavaScriptDialog = handleJavaScriptDialog; | ||
|  |     uiClient.getWindowFrame = getWindowFrame; | ||
|  |     WKPageSetPageUIClient(page, &uiClient.base); | ||
|  | 
 | ||
|  |     WKPageStateClientV0 stateClient = { }; | ||
|  |     stateClient.base.version = 0; | ||
|  |     stateClient.base.clientInfo = this; | ||
|  |     stateClient.didChangeTitle = didChangeTitle; | ||
|  |     stateClient.didChangeIsLoading = didChangeIsLoading; | ||
|  |     stateClient.didChangeActiveURL = didChangeActiveURL; | ||
|  |     WKPageSetPageStateClient(page, &stateClient.base); | ||
|  | 
 | ||
|  |     WKPagePolicyClientV1 policyClient = { }; | ||
|  |     policyClient.base.version = 1; | ||
|  |     policyClient.base.clientInfo = this; | ||
|  |     policyClient.decidePolicyForResponse_deprecatedForUseWithV0 = decidePolicyForResponse; | ||
|  |     WKPageSetPagePolicyClient(page, &policyClient.base); | ||
|  |     resetZoom(); | ||
|  | } | ||
|  | 
 | ||
|  | WebKitBrowserWindow::~WebKitBrowserWindow() | ||
|  | { | ||
|  |     if (m_alertDialog) { | ||
|  |         WKRelease(m_alertDialog); | ||
|  |         m_alertDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (m_confirmDialog) { | ||
|  |         WKRelease(m_confirmDialog); | ||
|  |         m_confirmDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (m_promptDialog) { | ||
|  |         WKRelease(m_promptDialog); | ||
|  |         m_promptDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (m_beforeUnloadDialog) { | ||
|  |         WKRelease(m_beforeUnloadDialog); | ||
|  |         m_beforeUnloadDialog = NULL; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | HWND WebKitBrowserWindow::hwnd() | ||
|  | { | ||
|  |     return WKViewGetWindow(m_view.get()); | ||
|  | } | ||
|  | 
 | ||
|  | HRESULT WebKitBrowserWindow::loadURL(const BSTR& url) | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     WKPageLoadURL(page, createWKURL(_bstr_t(url)).get()); | ||
|  |     return true; | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::reload() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     WKPageReload(page); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::navigateForwardOrBackward(bool forward) | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     if (forward) | ||
|  |         WKPageGoForward(page); | ||
|  |     else | ||
|  |         WKPageGoBack(page); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::launchInspector() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     auto inspector = WKPageGetInspector(page); | ||
|  |     WKInspectorShow(inspector); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::setUserAgent(_bstr_t& customUAString) | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     auto ua = createWKString(customUAString); | ||
|  |     WKPageSetCustomUserAgent(page, ua.get()); | ||
|  | } | ||
|  | 
 | ||
|  | _bstr_t WebKitBrowserWindow::userAgent() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     auto ua = adoptWK(WKPageCopyUserAgent(page)); | ||
|  |     return createString(ua.get()).c_str(); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::resetZoom() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     WKPageSetPageZoomFactor(page, WebCore::deviceScaleFactorForWindow(hwnd())); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::zoomIn() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     double s = WKPageGetPageZoomFactor(page); | ||
|  |     WKPageSetPageZoomFactor(page, s * 1.25); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::zoomOut() | ||
|  | { | ||
|  |     auto page = WKViewGetPage(m_view.get()); | ||
|  |     double s = WKPageGetPageZoomFactor(page); | ||
|  |     WKPageSetPageZoomFactor(page, s * 0.8); | ||
|  | } | ||
|  | 
 | ||
|  | static WebKitBrowserWindow& toWebKitBrowserWindow(const void *clientInfo) | ||
|  | { | ||
|  |     return *const_cast<WebKitBrowserWindow*>(static_cast<const WebKitBrowserWindow*>(clientInfo)); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::didChangeTitle(const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     auto page = WKViewGetPage(thisWindow.m_view.get()); | ||
|  |     WKRetainPtr<WKStringRef> title = adoptWK(WKPageCopyTitle(page)); | ||
|  |     std::wstring titleString = createString(title.get()) + L" [WebKit]"; | ||
|  |     SetWindowText(thisWindow.m_hMainWnd, titleString.c_str()); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::didChangeIsLoading(const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::didChangeActiveURL(const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     auto page = WKViewGetPage(thisWindow.m_view.get()); | ||
|  |     WKRetainPtr<WKURLRef> url = adoptWK(WKPageCopyActiveURL(page)); | ||
|  |     thisWindow.m_client.activeURLChanged(createString(url.get())); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::didReceiveAuthenticationChallenge(WKPageRef page, WKAuthenticationChallengeRef challenge, const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     auto protectionSpace = WKAuthenticationChallengeGetProtectionSpace(challenge); | ||
|  |     auto decisionListener = WKAuthenticationChallengeGetDecisionListener(challenge); | ||
|  |     auto authenticationScheme = WKProtectionSpaceGetAuthenticationScheme(protectionSpace); | ||
|  | 
 | ||
|  |     if (authenticationScheme == kWKProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested) { | ||
|  |         if (thisWindow.canTrustServerCertificate(protectionSpace)) { | ||
|  |             WKRetainPtr<WKStringRef> username = createWKString("accept server trust"); | ||
|  |             WKRetainPtr<WKStringRef> password = createWKString(""); | ||
|  |             WKRetainPtr<WKCredentialRef> wkCredential = adoptWK(WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession)); | ||
|  |             WKAuthenticationDecisionListenerUseCredential(decisionListener, wkCredential.get()); | ||
|  |             return; | ||
|  |         } | ||
|  |     } else { | ||
|  |         WKRetainPtr<WKStringRef> realm(WKProtectionSpaceCopyRealm(protectionSpace)); | ||
|  | 
 | ||
|  |         if (auto credential = askCredential(thisWindow.hwnd(), createString(realm.get()))) { | ||
|  |             WKRetainPtr<WKStringRef> username = createWKString(credential->username); | ||
|  |             WKRetainPtr<WKStringRef> password = createWKString(credential->password); | ||
|  |             WKRetainPtr<WKCredentialRef> wkCredential = adoptWK(WKCredentialCreate(username.get(), password.get(), kWKCredentialPersistenceForSession)); | ||
|  |             WKAuthenticationDecisionListenerUseCredential(decisionListener, wkCredential.get()); | ||
|  |             return; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     WKAuthenticationDecisionListenerUseCredential(decisionListener, nullptr); | ||
|  | } | ||
|  | 
 | ||
|  | bool WebKitBrowserWindow::canTrustServerCertificate(WKProtectionSpaceRef protectionSpace) | ||
|  | { | ||
|  |     auto host = createString(adoptWK(WKProtectionSpaceCopyHost(protectionSpace)).get()); | ||
|  |     auto certificateInfo = adoptWK(WKProtectionSpaceCopyCertificateInfo(protectionSpace)); | ||
|  |     auto verificationError = WKCertificateInfoGetVerificationError(certificateInfo.get()); | ||
|  |     auto description = createString(adoptWK(WKCertificateInfoCopyVerificationErrorDescription(certificateInfo.get())).get()); | ||
|  |     auto pem = createPEMString(certificateInfo.get()); | ||
|  | 
 | ||
|  |     auto it = m_acceptedServerTrustCerts.find(host); | ||
|  |     if (it != m_acceptedServerTrustCerts.end() && it->second == pem) | ||
|  |         return true; | ||
|  | 
 | ||
|  |     std::wstring textString = L"[HOST] " + host + L"\r\n"; | ||
|  |     textString.append(L"[ERROR] " + std::to_wstring(verificationError) + L"\r\n"); | ||
|  |     textString.append(L"[DESCRIPTION] " + description + L"\r\n"); | ||
|  |     textString.append(pem); | ||
|  | 
 | ||
|  |     if (askServerTrustEvaluation(hwnd(), textString)) { | ||
|  |         m_acceptedServerTrustCerts.emplace(host, pem); | ||
|  |         return true; | ||
|  |     } | ||
|  | 
 | ||
|  |     return false; | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::closeWindow(WKPageRef page, const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     PostMessage(thisWindow.m_hMainWnd, WM_CLOSE, 0, 0); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::runJavaScriptAlert(WKPageRef page, WKStringRef alertText, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptAlertResultListenerRef listener, const void *clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     WKRetain(listener); | ||
|  |     thisWindow.m_alertDialog = listener; | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::runJavaScriptConfirm(WKPageRef page, WKStringRef message, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptConfirmResultListenerRef listener, const void *clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     WKRetain(listener); | ||
|  |     thisWindow.m_confirmDialog = listener; | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::runJavaScriptPrompt(WKPageRef page, WKStringRef message, WKStringRef defaultValue, WKFrameRef frame, WKSecurityOriginRef securityOrigin, WKPageRunJavaScriptPromptResultListenerRef listener, const void *clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     WKRetain(listener); | ||
|  |     thisWindow.m_promptDialog = listener; | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::runBeforeUnloadConfirmPanel(WKPageRef page, WKStringRef message, WKFrameRef frame, WKPageRunBeforeUnloadConfirmPanelResultListenerRef listener, const void *clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     WKRetain(listener); | ||
|  |     thisWindow.m_beforeUnloadDialog = listener; | ||
|  | } | ||
|  |      | ||
|  | void WebKitBrowserWindow::handleJavaScriptDialog(WKPageRef page, bool accept, WKStringRef value, const void *clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     if (thisWindow.m_alertDialog) { | ||
|  |         WKPageRunJavaScriptAlertResultListenerCall(thisWindow.m_alertDialog); | ||
|  |         WKRelease(thisWindow.m_alertDialog); | ||
|  |         thisWindow.m_alertDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (thisWindow.m_confirmDialog) { | ||
|  |         WKPageRunJavaScriptConfirmResultListenerCall(thisWindow.m_confirmDialog, accept); | ||
|  |         WKRelease(thisWindow.m_confirmDialog); | ||
|  |         thisWindow.m_confirmDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (thisWindow.m_promptDialog) { | ||
|  |         WKPageRunJavaScriptPromptResultListenerCall(thisWindow.m_promptDialog, accept ? value : NULL); | ||
|  |         WKRelease(thisWindow.m_promptDialog); | ||
|  |         thisWindow.m_promptDialog = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (thisWindow.m_beforeUnloadDialog) { | ||
|  |         WKPageRunBeforeUnloadConfirmPanelResultListenerCall(thisWindow.m_beforeUnloadDialog, accept); | ||
|  |         WKRelease(thisWindow.m_beforeUnloadDialog); | ||
|  |         thisWindow.m_beforeUnloadDialog = NULL; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | WKRect WebKitBrowserWindow::getWindowFrame(WKPageRef page, const void *clientInfo) { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     WKRect wkFrame { }; | ||
|  |     RECT r; | ||
|  |     if (::GetWindowRect(thisWindow.m_hMainWnd, &r)) { | ||
|  |         wkFrame.origin.x = r.left; | ||
|  |         wkFrame.origin.y = r.top; | ||
|  |         wkFrame.size.width = r.right - r.left; | ||
|  |         wkFrame.size.height = r.bottom - r.top; | ||
|  |     } | ||
|  |     return wkFrame; | ||
|  | } | ||
|  | 
 | ||
|  | WKPageRef WebKitBrowserWindow::createPageCallback(WKPageConfigurationRef configuration) | ||
|  | { | ||
|  |     // This comes from the Playwright agent, configuration is a pool+data pair.
 | ||
|  |     return WebKitBrowserWindow::createViewCallback(configuration, true); | ||
|  | } | ||
|  | 
 | ||
|  | WKPageRef WebKitBrowserWindow::createViewCallback(WKPageConfigurationRef configuration, bool navigate) | ||
|  | { | ||
|  |     auto* newWindow = new MainWindow(); | ||
|  |     bool ok = newWindow->init(hInst, configuration); | ||
|  |     if (navigate) | ||
|  |         newWindow->browserWindow()->loadURL(_bstr_t("about:blank").GetBSTR()); | ||
|  | 
 | ||
|  |     auto* newBrowserWindow = newWindow->browserWindow(); | ||
|  |     return WKViewGetPage(newBrowserWindow->m_view.get()); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | WKPageRef WebKitBrowserWindow::createNewPage(WKPageRef, WKPageConfigurationRef configuration, WKNavigationActionRef, WKWindowFeaturesRef, const void*) | ||
|  | { | ||
|  |     // This comes from the client for popups, configuration is inherited from main page.
 | ||
|  |     // Retain popups as per API contract.
 | ||
|  |     WKRetainPtr<WKPageRef> newPage = createViewCallback(configuration, false); | ||
|  |     return newPage.leakRef(); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::didNotHandleKeyEvent(WKPageRef, WKNativeEventPtr event, const void* clientInfo) | ||
|  | { | ||
|  |     auto& thisWindow = toWebKitBrowserWindow(clientInfo); | ||
|  |     PostMessage(thisWindow.m_hMainWnd, event->message, event->wParam, event->lParam); | ||
|  | } | ||
|  | 
 | ||
|  | void WebKitBrowserWindow::decidePolicyForResponse(WKPageRef page, WKFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKFramePolicyListenerRef listener, WKTypeRef userData, const void* clientInfo) | ||
|  | { | ||
|  |     if (WKURLResponseIsAttachment(response)) | ||
|  |         WKFramePolicyListenerDownload(listener); | ||
|  |     else | ||
|  |         WKFramePolicyListenerUse(listener); | ||
|  | } |