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);
 | |
| }
 | 
