/* * Copyright (C) 2010-2020 Apple Inc. All rights reserved. * * 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. AND ITS CONTRIBUTORS ``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 ITS 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 "config.h" #include "TestRunner.h" #include "DictionaryFunctions.h" #include "InjectedBundle.h" #include "InjectedBundlePage.h" #include "JSTestRunner.h" #include "PlatformWebView.h" #include "TestController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace WTR { ALLOW_DEPRECATED_DECLARATIONS_BEGIN Ref TestRunner::create() { return adoptRef(*new TestRunner); } TestRunner::TestRunner() : m_userStyleSheetLocation(toWK("")) { platformInitialize(); } TestRunner::~TestRunner() { } JSClassRef TestRunner::wrapperClass() { return JSTestRunner::testRunnerClass(); } static WKBundlePageRef page() { return InjectedBundle::singleton().page()->page(); } static WKBundleFrameRef mainFrame() { return WKBundlePageGetMainFrame(page()); } static JSContextRef mainFrameJSContext() { return WKBundleFrameGetJavaScriptContext(mainFrame()); } void TestRunner::display() { WKBundlePageForceRepaint(page()); } void TestRunner::displayAndTrackRepaints() { auto page = WTR::page(); WKBundlePageForceRepaint(page); WKBundlePageSetTracksRepaints(page, true); WKBundlePageResetTrackedRepaints(page); } static WKRetainPtr toWK(double value) { return adoptWK(WKDoubleCreate(value)); } static WKRetainPtr createWKDictionary(std::initializer_list>> pairs) { Vector keys; Vector values; Vector> strings; for (auto& pair : pairs) { auto key = toWK(pair.first); keys.append(key.get()); values.append(pair.second.get()); strings.append(WTFMove(key)); } return adoptWK(WKDictionaryCreate(keys.data(), values.data(), keys.size())); } template static WKRetainPtr postSynchronousMessageWithReturnValue(const char* name, const WKRetainPtr& value) { WKTypeRef rawReturnValue = nullptr; WKBundlePostSynchronousMessage(InjectedBundle::singleton().bundle(), toWK(name).get(), value.get(), &rawReturnValue); return adoptWK(rawReturnValue); } template static bool postSynchronousMessageReturningBoolean(const char* name, const WKRetainPtr& value) { return booleanValue(postSynchronousMessageWithReturnValue(name, value)); } static bool postSynchronousMessageReturningBoolean(const char* name) { return postSynchronousMessageReturningBoolean(name, WKRetainPtr { }); } template static WKRetainPtr postSynchronousPageMessageWithReturnValue(const char* name, const WKRetainPtr& value) { WKTypeRef rawReturnValue = nullptr; WKBundlePagePostSynchronousMessageForTesting(page(), toWK(name).get(), value.get(), &rawReturnValue); return adoptWK(rawReturnValue); } template static bool postSynchronousPageMessageReturningBoolean(const char* name, const WKRetainPtr& value) { return booleanValue(postSynchronousPageMessageWithReturnValue(name, value)); } static bool postSynchronousPageMessageReturningBoolean(const char* name) { return postSynchronousPageMessageReturningBoolean(name, WKRetainPtr { }); } static bool postSynchronousPageMessageReturningBoolean(const char* name, JSStringRef string) { return postSynchronousPageMessageReturningBoolean(name, toWK(string)); } template static uint64_t postSynchronousPageMessageReturningUInt64(const char* name, const WKRetainPtr& value) { return uint64Value(postSynchronousPageMessageWithReturnValue(name, value)); } static uint64_t postSynchronousMessageReturningUInt64(const char* name) { return uint64Value(postSynchronousMessageWithReturnValue(name, WKRetainPtr { })); } static bool postSynchronousPageMessageReturningUInt64(const char* name, JSStringRef string) { return postSynchronousPageMessageReturningUInt64(name, toWK(string)); } bool TestRunner::shouldDumpPixels() const { return postSynchronousMessageReturningBoolean("GetDumpPixels"); } void TestRunner::setDumpPixels(bool dumpPixels) { postSynchronousMessage("SetDumpPixels", dumpPixels); } void TestRunner::dumpAsText(bool dumpPixels) { if (whatToDump() < WhatToDump::MainFrameText) setWhatToDump(WhatToDump::MainFrameText); setDumpPixels(dumpPixels); } WhatToDump TestRunner::whatToDump() const { return static_cast(postSynchronousMessageReturningUInt64("GetWhatToDump")); } void TestRunner::setWhatToDump(WhatToDump whatToDump) { postSynchronousMessage("SetWhatToDump", static_cast(whatToDump)); } void TestRunner::setCustomPolicyDelegate(bool enabled, bool permissive) { m_policyDelegateEnabled = enabled; m_policyDelegatePermissive = permissive; InjectedBundle::singleton().setCustomPolicyDelegate(enabled, permissive); } void TestRunner::waitForPolicyDelegate() { setCustomPolicyDelegate(true); waitUntilDone(); } void TestRunner::waitUntilDownloadFinished() { m_shouldFinishAfterDownload = true; waitUntilDone(); } void TestRunner::waitUntilDone() { RELEASE_ASSERT(InjectedBundle::singleton().isTestRunning()); setWaitUntilDone(true); } void TestRunner::setWaitUntilDone(bool value) { postSynchronousMessage("SetWaitUntilDone", value); } bool TestRunner::shouldWaitUntilDone() const { return postSynchronousMessageReturningBoolean("GetWaitUntilDone"); } void TestRunner::notifyDone() { auto& injectedBundle = InjectedBundle::singleton(); if (!injectedBundle.isTestRunning()) return; if (shouldWaitUntilDone() && !injectedBundle.topLoadingFrame()) injectedBundle.page()->dump(); // We don't call invalidateWaitToDumpWatchdogTimer() here, even if we continue to wait for a load to finish. // The test is still subject to timeout checking - it is better to detect an async timeout inside WebKitTestRunner // than to let webkitpy do that, because WebKitTestRunner will dump partial results. setWaitUntilDone(false); } void TestRunner::forceImmediateCompletion() { auto& injectedBundle = InjectedBundle::singleton(); if (!injectedBundle.isTestRunning()) return; if (shouldWaitUntilDone() && injectedBundle.page()) injectedBundle.page()->dump(); setWaitUntilDone(false); } void TestRunner::setShouldDumpFrameLoadCallbacks(bool value) { postSynchronousMessage("SetDumpFrameLoadCallbacks", value); } bool TestRunner::shouldDumpFrameLoadCallbacks() { return postSynchronousMessageReturningBoolean("GetDumpFrameLoadCallbacks"); } unsigned TestRunner::imageCountInGeneralPasteboard() const { return InjectedBundle::singleton().imageCountInGeneralPasteboard(); } void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames) { WKBundlePageAddUserScript(page(), toWK(source).get(), (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd), (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly)); } void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames) { WKBundlePageAddUserStyleSheet(page(), toWK(source).get(), (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly)); } void TestRunner::keepWebHistory() { InjectedBundle::singleton().postSetAddsVisitedLinks(true); } void TestRunner::execCommand(JSStringRef name, JSStringRef showUI, JSStringRef value) { WKBundlePageExecuteEditingCommand(page(), toWK(name).get(), toWK(value).get()); } static std::optional findOptionsFromArray(JSValueRef optionsArrayAsValue) { auto context = mainFrameJSContext(); auto optionsArray = JSValueToObject(context, optionsArrayAsValue, nullptr); auto length = arrayLength(context, optionsArray); WKFindOptions options = 0; for (unsigned i = 0; i < length; ++i) { auto optionName = createJSString(context, JSObjectGetPropertyAtIndex(context, optionsArray, i, 0)); if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive")) options |= kWKFindOptionsCaseInsensitive; else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts")) options |= kWKFindOptionsAtWordStarts; else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart")) options |= kWKFindOptionsTreatMedialCapitalAsWordStart; else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards")) options |= kWKFindOptionsBackwards; else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround")) options |= kWKFindOptionsWrapAround; else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) { // FIXME: No kWKFindOptionsStartInSelection. } } return options; } bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue) { if (auto options = findOptionsFromArray(optionsArrayAsValue)) return WKBundlePageFindString(page(), toWK(target).get(), *options); return false; } void TestRunner::findStringMatchesInPage(JSStringRef target, JSValueRef optionsArrayAsValue) { if (auto options = findOptionsFromArray(optionsArrayAsValue)) WKBundlePageFindStringMatches(page(), toWK(target).get(), *options); } void TestRunner::replaceFindMatchesAtIndices(JSValueRef matchIndicesAsValue, JSStringRef replacementText, bool selectionOnly) { auto& bundle = InjectedBundle::singleton(); auto context = mainFrameJSContext(); auto matchIndicesObject = JSValueToObject(context, matchIndicesAsValue, 0); auto length = arrayLength(context, matchIndicesObject); auto indices = adoptWK(WKMutableArrayCreate()); for (unsigned i = 0; i < length; ++i) { auto value = JSObjectGetPropertyAtIndex(context, matchIndicesObject, i, nullptr); if (!JSValueIsNumber(context, value)) continue; auto index = adoptWK(WKUInt64Create(JSValueToNumber(context, value, nullptr))); WKArrayAppendItem(indices.get(), index.get()); } WKBundlePageReplaceStringMatches(bundle.page()->page(), indices.get(), toWK(replacementText).get(), selectionOnly); } void TestRunner::clearAllDatabases() { WKBundleClearAllDatabases(InjectedBundle::singleton().bundle()); postSynchronousMessage("DeleteAllIndexedDatabases", true); } void TestRunner::setDatabaseQuota(uint64_t quota) { return WKBundleSetDatabaseQuota(InjectedBundle::singleton().bundle(), quota); } void TestRunner::syncLocalStorage() { postSynchronousMessage("SyncLocalStorage", true); } void TestRunner::clearAllApplicationCaches() { WKBundlePageClearApplicationCache(page()); } void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin) { WKBundlePageClearApplicationCacheForOrigin(page(), toWK(origin).get()); } void TestRunner::setAppCacheMaximumSize(uint64_t size) { WKBundlePageSetAppCacheMaximumSize(page(), size); } long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin) { return WKBundlePageGetAppCacheUsageForOrigin(page(), toWK(origin).get()); } void TestRunner::disallowIncreaseForApplicationCacheQuota() { m_disallowIncreaseForApplicationCacheQuota = true; } static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings) { const size_t count = WKArrayGetSize(strings); auto array = JSObjectMakeArray(context, 0, 0, nullptr); for (size_t i = 0; i < count; ++i) { auto stringRef = static_cast(WKArrayGetItemAtIndex(strings, i)); JSObjectSetPropertyAtIndex(context, array, i, JSValueMakeString(context, toJS(stringRef).get()), nullptr); } return array; } JSValueRef TestRunner::originsWithApplicationCache() { return stringArrayToJS(mainFrameJSContext(), adoptWK(WKBundlePageCopyOriginsWithApplicationCache(page())).get()); } bool TestRunner::isCommandEnabled(JSStringRef name) { return WKBundlePageIsEditingCommandEnabled(page(), toWK(name).get()); } void TestRunner::setCanOpenWindows() { postSynchronousMessage("SetCanOpenWindows", true); } void TestRunner::setCustomUserAgent(JSStringRef userAgent) { postSynchronousMessage("SetCustomUserAgent", toWK(userAgent)); } void TestRunner::setAllowsAnySSLCertificate(bool enabled) { InjectedBundle::singleton().setAllowsAnySSLCertificate(enabled); postSynchronousPageMessage("SetAllowsAnySSLCertificate", enabled); } void TestRunner::setShouldSwapToEphemeralSessionOnNextNavigation(bool shouldSwap) { postSynchronousPageMessage("SetShouldSwapToEphemeralSessionOnNextNavigation", shouldSwap); } void TestRunner::setShouldSwapToDefaultSessionOnNextNavigation(bool shouldSwap) { postSynchronousPageMessage("SetShouldSwapToDefaultSessionOnNextNavigation", shouldSwap); } void TestRunner::addOriginAccessAllowListEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) { WKBundleAddOriginAccessAllowListEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains); } void TestRunner::removeOriginAccessAllowListEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains) { WKBundleRemoveOriginAccessAllowListEntry(InjectedBundle::singleton().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains); } bool TestRunner::isPageBoxVisible(int pageIndex) { auto& injectedBundle = InjectedBundle::singleton(); return WKBundleIsPageBoxVisible(injectedBundle.bundle(), mainFrame(), pageIndex); } void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value) { if (!element || !JSValueIsObject(context, element)) return; WKBundleNodeHandleSetHTMLInputElementValueForUser(adoptWK(WKBundleNodeHandleCreate(context, const_cast(element))).get(), toWK(value).get()); } void TestRunner::setAudioResult(JSContextRef context, JSValueRef data) { auto& injectedBundle = InjectedBundle::singleton(); // FIXME (123058): Use a JSC API to get buffer contents once such is exposed. injectedBundle.setAudioResult(adoptWK(WKBundleCreateWKDataFromUInt8Array(injectedBundle.bundle(), context, data)).get()); setWhatToDump(WhatToDump::Audio); setDumpPixels(false); } unsigned TestRunner::windowCount() { return InjectedBundle::singleton().pageCount(); } void TestRunner::clearBackForwardList() { WKBundleClearHistoryForTesting(page()); } void TestRunner::makeWindowObject(JSContextRef context) { setGlobalObjectProperty(context, "testRunner", this); } void TestRunner::showWebInspector() { WKBundleInspectorShow(WKBundlePageGetInspector(page())); } void TestRunner::closeWebInspector() { WKBundleInspectorClose(WKBundlePageGetInspector(page())); } void TestRunner::evaluateInWebInspector(JSStringRef script) { WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(page()), toWK(script).get()); } using WorldMap = WTF::HashMap>; static WorldMap& worldMap() { static WorldMap& map = *new WorldMap; return map; } unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world) { // FIXME: This is the anti-pattern of iterating an entire map. Typically we use a pair of maps or just a vector in a case like this. for (auto& mapEntry : worldMap()) { if (mapEntry.value == world) return mapEntry.key; } return 0; } void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script) { // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world // that is created once and cached forever. WKRetainPtr world; if (!worldID) world.adopt(WKBundleScriptWorldCreateWorld()); else { WKRetainPtr& worldSlot = worldMap().add(worldID, nullptr).iterator->value; if (!worldSlot) worldSlot.adopt(WKBundleScriptWorldCreateWorld()); world = worldSlot; } WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context); if (!frame) frame = mainFrame(); JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get()); JSEvaluateScript(jsContext, script, 0, 0, 0, 0); } void TestRunner::setPOSIXLocale(JSStringRef locale) { char localeBuf[32]; JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf)); setlocale(LC_ALL, localeBuf); } void TestRunner::setTextDirection(JSStringRef direction) { return WKBundleFrameSetTextDirection(mainFrame(), toWK(direction).get()); } void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage) { InjectedBundle::singleton().postNewBeforeUnloadReturnValue(!shouldStayOnPage); } bool TestRunner::didReceiveServerRedirectForProvisionalNavigation() const { return postSynchronousPageMessageReturningBoolean("DidReceiveServerRedirectForProvisionalNavigation"); } void TestRunner::clearDidReceiveServerRedirectForProvisionalNavigation() { postSynchronousPageMessage("ClearDidReceiveServerRedirectForProvisionalNavigation"); } void TestRunner::setPageVisibility(JSStringRef state) { InjectedBundle::singleton().setHidden(JSStringIsEqualToUTF8CString(state, "hidden")); } void TestRunner::resetPageVisibility() { InjectedBundle::singleton().setHidden(false); } using CallbackMap = WTF::HashMap; static CallbackMap& callbackMap() { static CallbackMap& map = *new CallbackMap; return map; } enum { AddChromeInputFieldCallbackID = 1, RemoveChromeInputFieldCallbackID, SetTextInChromeInputFieldCallbackID, SelectChromeInputFieldCallbackID, GetSelectedTextInChromeInputFieldCallbackID, FocusWebViewCallbackID, SetBackingScaleFactorCallbackID, DidBeginSwipeCallbackID, WillEndSwipeCallbackID, DidEndSwipeCallbackID, DidRemoveSwipeSnapshotCallbackID, SetStatisticsDebugModeCallbackID, SetStatisticsPrevalentResourceForDebugModeCallbackID, SetStatisticsLastSeenCallbackID, SetStatisticsMergeStatisticCallbackID, SetStatisticsExpiredStatisticCallbackID, SetStatisticsPrevalentResourceCallbackID, SetStatisticsVeryPrevalentResourceCallbackID, SetStatisticsHasHadUserInteractionCallbackID, StatisticsDidModifyDataRecordsCallbackID, StatisticsDidScanDataRecordsCallbackID, StatisticsDidClearThroughWebsiteDataRemovalCallbackID, StatisticsDidClearInMemoryAndPersistentStoreCallbackID, StatisticsDidResetToConsistentStateCallbackID, StatisticsDidSetBlockCookiesForHostCallbackID, StatisticsDidSetShouldDowngradeReferrerCallbackID, StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID, StatisticsDidSetFirstPartyWebsiteDataRemovalModeCallbackID, StatisticsDidSetToSameSiteStrictCookiesCallbackID, StatisticsDidSetFirstPartyHostCNAMEDomainCallbackID, StatisticsDidSetThirdPartyCNAMEDomainCallbackID, AllStorageAccessEntriesCallbackID, LoadedSubresourceDomainsCallbackID, DidRemoveAllSessionCredentialsCallbackID, GetApplicationManifestCallbackID, TextDidChangeInTextFieldCallbackID, TextFieldDidBeginEditingCallbackID, TextFieldDidEndEditingCallbackID, CustomMenuActionCallbackID, DidSetAppBoundDomainsCallbackID, EnterFullscreenForElementCallbackID, ExitFullscreenForElementCallbackID, AppBoundRequestContextDataForDomainCallbackID, FirstUIScriptCallbackID = 100 }; static void cacheTestRunnerCallback(unsigned index, JSValueRef callback) { if (!callback) return; auto context = mainFrameJSContext(); if (!JSValueIsObject(context, callback)) return; if (callbackMap().contains(index)) { InjectedBundle::singleton().outputText(makeString("FAIL: Tried to install a second TestRunner callback for the same event (id ", index, ")\n\n")); return; } JSValueProtect(context, callback); callbackMap().add(index, const_cast(callback)); } static void callTestRunnerCallback(unsigned index, size_t argumentCount = 0, const JSValueRef arguments[] = nullptr) { auto callback = callbackMap().take(index); if (!callback) return; auto context = mainFrameJSContext(); JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), argumentCount, arguments, 0); JSValueUnprotect(context, callback); } void TestRunner::clearTestRunnerCallbacks() { auto context = mainFrameJSContext(); for (auto& value : callbackMap().values()) JSValueUnprotect(context, JSValueToObject(context, value, nullptr)); callbackMap().clear(); } void TestRunner::accummulateLogsForChannel(JSStringRef) { // FIXME: Implement getting the call to all processes. } void TestRunner::addChromeInputField(JSValueRef callback) { cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback); InjectedBundle::singleton().postAddChromeInputField(); } void TestRunner::removeChromeInputField(JSValueRef callback) { cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback); InjectedBundle::singleton().postRemoveChromeInputField(); } void TestRunner::setTextInChromeInputField(JSStringRef text, JSValueRef callback) { cacheTestRunnerCallback(SetTextInChromeInputFieldCallbackID, callback); InjectedBundle::singleton().postSetTextInChromeInputField(toWTFString(text)); } void TestRunner::selectChromeInputField(JSValueRef callback) { cacheTestRunnerCallback(SelectChromeInputFieldCallbackID, callback); InjectedBundle::singleton().postSelectChromeInputField(); } void TestRunner::getSelectedTextInChromeInputField(JSValueRef callback) { cacheTestRunnerCallback(GetSelectedTextInChromeInputFieldCallbackID, callback); InjectedBundle::singleton().postGetSelectedTextInChromeInputField(); } void TestRunner::focusWebView(JSValueRef callback) { cacheTestRunnerCallback(FocusWebViewCallbackID, callback); InjectedBundle::singleton().postFocusWebView(); } void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback) { cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback); InjectedBundle::singleton().postSetBackingScaleFactor(backingScaleFactor); } void TestRunner::setWindowIsKey(bool isKey) { InjectedBundle::singleton().postSetWindowIsKey(isKey); } void TestRunner::setViewSize(double width, double height) { InjectedBundle::singleton().postSetViewSize(width, height); } void TestRunner::callAddChromeInputFieldCallback() { callTestRunnerCallback(AddChromeInputFieldCallbackID); } void TestRunner::callRemoveChromeInputFieldCallback() { callTestRunnerCallback(RemoveChromeInputFieldCallbackID); } void TestRunner::callSetTextInChromeInputFieldCallback() { callTestRunnerCallback(SetTextInChromeInputFieldCallbackID); } void TestRunner::callSelectChromeInputFieldCallback() { callTestRunnerCallback(SelectChromeInputFieldCallbackID); } void TestRunner::callGetSelectedTextInChromeInputFieldCallback(JSStringRef text) { auto textValue = JSValueMakeString(mainFrameJSContext(), text); callTestRunnerCallback(GetSelectedTextInChromeInputFieldCallbackID, 1, &textValue); } void TestRunner::callFocusWebViewCallback() { callTestRunnerCallback(FocusWebViewCallbackID); } void TestRunner::callSetBackingScaleFactorCallback() { callTestRunnerCallback(SetBackingScaleFactorCallbackID); } void TestRunner::setAlwaysAcceptCookies(bool accept) { postSynchronousMessage("SetAlwaysAcceptCookies", accept); } void TestRunner::setOnlyAcceptFirstPartyCookies(bool accept) { postSynchronousMessage("SetOnlyAcceptFirstPartyCookies", accept); } void TestRunner::setEnterFullscreenForElementCallback(JSValueRef callback) { cacheTestRunnerCallback(EnterFullscreenForElementCallbackID, callback); } void TestRunner::callEnterFullscreenForElementCallback() { callTestRunnerCallback(EnterFullscreenForElementCallbackID); } void TestRunner::setExitFullscreenForElementCallback(JSValueRef callback) { cacheTestRunnerCallback(ExitFullscreenForElementCallbackID, callback); } void TestRunner::callExitFullscreenForElementCallback() { callTestRunnerCallback(ExitFullscreenForElementCallbackID); } double TestRunner::preciseTime() { return WallTime::now().secondsSinceEpoch().seconds(); } void TestRunner::setRenderTreeDumpOptions(unsigned short options) { m_renderTreeDumpOptions = options; } void TestRunner::setUserStyleSheetEnabled(bool enabled) { m_userStyleSheetEnabled = enabled; auto emptyString = toWK(""); auto location = enabled ? m_userStyleSheetLocation.get() : emptyString.get(); auto& injectedBundle = InjectedBundle::singleton(); WKBundleSetUserStyleSheetLocationForTesting(injectedBundle.bundle(), location); } void TestRunner::setUserStyleSheetLocation(JSStringRef location) { m_userStyleSheetLocation = toWK(location); if (m_userStyleSheetEnabled) setUserStyleSheetEnabled(true); } void TestRunner::setTabKeyCyclesThroughElements(bool enabled) { WKBundleSetTabKeyCyclesThroughElements(InjectedBundle::singleton().bundle(), page(), enabled); } void TestRunner::setSerializeHTTPLoads() { // WK2 doesn't reorder loads. } void TestRunner::dispatchPendingLoadRequests() { // WK2 doesn't keep pending requests. } void TestRunner::setCacheModel(int model) { InjectedBundle::singleton().setCacheModel(model); } void TestRunner::setAsynchronousSpellCheckingEnabled(bool enabled) { auto& injectedBundle = InjectedBundle::singleton(); WKBundleSetAsynchronousSpellCheckingEnabledForTesting(injectedBundle.bundle(), enabled); } void TestRunner::grantWebNotificationPermission(JSStringRef origin) { WKBundleSetWebNotificationPermission(InjectedBundle::singleton().bundle(), page(), toWK(origin).get(), true); } void TestRunner::denyWebNotificationPermission(JSStringRef origin) { WKBundleSetWebNotificationPermission(InjectedBundle::singleton().bundle(), page(), toWK(origin).get(), false); } void TestRunner::removeAllWebNotificationPermissions() { WKBundleRemoveAllWebNotificationPermissions(InjectedBundle::singleton().bundle(), page()); } void TestRunner::simulateWebNotificationClick(JSValueRef notification) { auto& injectedBundle = InjectedBundle::singleton(); injectedBundle.postSimulateWebNotificationClick(WKBundleGetWebNotificationID(injectedBundle.bundle(), mainFrameJSContext(), notification)); } void TestRunner::setGeolocationPermission(bool enabled) { // FIXME: This should be done by frame. InjectedBundle::singleton().setGeolocationPermission(enabled); } bool TestRunner::isGeolocationProviderActive() { return InjectedBundle::singleton().isGeolocationProviderActive(); } void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, std::optional altitude, std::optional altitudeAccuracy, std::optional heading, std::optional speed, std::optional floorLevel) { InjectedBundle::singleton().setMockGeolocationPosition(latitude, longitude, accuracy, altitude, altitudeAccuracy, heading, speed, floorLevel); } void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message) { InjectedBundle::singleton().setMockGeolocationPositionUnavailableError(toWK(message).get()); } void TestRunner::setUserMediaPermission(bool enabled) { // FIXME: This should be done by frame. InjectedBundle::singleton().setUserMediaPermission(enabled); } void TestRunner::resetUserMediaPermission() { // FIXME: This should be done by frame. InjectedBundle::singleton().resetUserMediaPermission(); } bool TestRunner::isDoingMediaCapture() const { return postSynchronousPageMessageReturningBoolean("IsDoingMediaCapture"); } void TestRunner::setUserMediaPersistentPermissionForOrigin(bool permission, JSStringRef origin, JSStringRef parentOrigin) { InjectedBundle::singleton().setUserMediaPersistentPermissionForOrigin(permission, toWK(origin).get(), toWK(parentOrigin).get()); } unsigned TestRunner::userMediaPermissionRequestCountForOrigin(JSStringRef origin, JSStringRef parentOrigin) const { return InjectedBundle::singleton().userMediaPermissionRequestCountForOrigin(toWK(origin).get(), toWK(parentOrigin).get()); } void TestRunner::resetUserMediaPermissionRequestCountForOrigin(JSStringRef origin, JSStringRef parentOrigin) { InjectedBundle::singleton().resetUserMediaPermissionRequestCountForOrigin(toWK(origin).get(), toWK(parentOrigin).get()); } bool TestRunner::callShouldCloseOnWebView() { return WKBundleFrameCallShouldCloseOnWebView(mainFrame()); } void TestRunner::queueBackNavigation(unsigned howFarBackward) { InjectedBundle::singleton().queueBackNavigation(howFarBackward); } void TestRunner::queueForwardNavigation(unsigned howFarForward) { InjectedBundle::singleton().queueForwardNavigation(howFarForward); } void TestRunner::queueLoad(JSStringRef url, JSStringRef target, bool shouldOpenExternalURLs) { auto& injectedBundle = InjectedBundle::singleton(); auto baseURLWK = adoptWK(WKBundleFrameCopyURL(mainFrame())); auto urlWK = adoptWK(WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(url).utf8().data())); auto urlStringWK = adoptWK(WKURLCopyString(urlWK.get())); injectedBundle.queueLoad(urlStringWK.get(), toWK(target).get(), shouldOpenExternalURLs); } void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL) { auto baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr(); auto unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr(); InjectedBundle::singleton().queueLoadHTMLString(toWK(content).get(), baseURLWK.get(), unreachableURLWK.get()); } void TestRunner::queueReload() { InjectedBundle::singleton().queueReload(); } void TestRunner::queueLoadingScript(JSStringRef script) { InjectedBundle::singleton().queueLoadingScript(toWK(script).get()); } void TestRunner::queueNonLoadingScript(JSStringRef script) { InjectedBundle::singleton().queueNonLoadingScript(toWK(script).get()); } void TestRunner::setRejectsProtectionSpaceAndContinueForAuthenticationChallenges(bool value) { postPageMessage("SetRejectsProtectionSpaceAndContinueForAuthenticationChallenges", value); } void TestRunner::setHandlesAuthenticationChallenges(bool handlesAuthenticationChallenges) { postPageMessage("SetHandlesAuthenticationChallenges", handlesAuthenticationChallenges); } void TestRunner::setShouldLogCanAuthenticateAgainstProtectionSpace(bool value) { postPageMessage("SetShouldLogCanAuthenticateAgainstProtectionSpace", value); } void TestRunner::setShouldLogDownloadCallbacks(bool value) { postPageMessage("SetShouldLogDownloadCallbacks", value); } void TestRunner::setAuthenticationUsername(JSStringRef username) { postPageMessage("SetAuthenticationUsername", toWK(username)); } void TestRunner::setAuthenticationPassword(JSStringRef password) { postPageMessage("SetAuthenticationPassword", toWK(password)); } bool TestRunner::secureEventInputIsEnabled() const { return postSynchronousPageMessageReturningBoolean("SecureEventInputIsEnabled"); } void TestRunner::setBlockAllPlugins(bool shouldBlock) { postPageMessage("SetBlockAllPlugins", shouldBlock); } void TestRunner::setPluginSupportedMode(JSStringRef mode) { postPageMessage("SetPluginSupportedMode", toWK(mode)); } JSValueRef TestRunner::failNextNewCodeBlock() { return JSC::failNextNewCodeBlock(mainFrameJSContext()); } JSValueRef TestRunner::numberOfDFGCompiles(JSValueRef function) { return JSC::numberOfDFGCompiles(mainFrameJSContext(), function); } JSValueRef TestRunner::neverInlineFunction(JSValueRef function) { return JSC::setNeverInline(mainFrameJSContext(), function); } void TestRunner::setShouldDecideNavigationPolicyAfterDelay(bool value) { m_shouldDecideNavigationPolicyAfterDelay = value; postPageMessage("SetShouldDecideNavigationPolicyAfterDelay", value); } void TestRunner::setShouldDecideResponsePolicyAfterDelay(bool value) { m_shouldDecideResponsePolicyAfterDelay = value; postPageMessage("SetShouldDecideResponsePolicyAfterDelay", value); } void TestRunner::setNavigationGesturesEnabled(bool value) { postPageMessage("SetNavigationGesturesEnabled", value); } void TestRunner::setIgnoresViewportScaleLimits(bool value) { postPageMessage("SetIgnoresViewportScaleLimits", value); } void TestRunner::setShouldDownloadUndisplayableMIMETypes(bool value) { postPageMessage("SetShouldDownloadUndisplayableMIMETypes", value); } void TestRunner::setShouldAllowDeviceOrientationAndMotionAccess(bool value) { postPageMessage("SetShouldAllowDeviceOrientationAndMotionAccess", value); } void TestRunner::terminateNetworkProcess() { postSynchronousPageMessage("TerminateNetworkProcess"); } void TestRunner::terminateServiceWorkers() { postSynchronousPageMessage("TerminateServiceWorkers"); } void TestRunner::setUseSeparateServiceWorkerProcess(bool value) { postSynchronousPageMessage("SetUseSeparateServiceWorkerProcess", value); } static unsigned nextUIScriptCallbackID() { static unsigned callbackID = FirstUIScriptCallbackID; return callbackID++; } void TestRunner::runUIScript(JSStringRef script, JSValueRef callback) { unsigned callbackID = nextUIScriptCallbackID(); cacheTestRunnerCallback(callbackID, callback); postPageMessage("RunUIProcessScript", createWKDictionary({ { "Script", toWK(script) }, { "CallbackID", adoptWK(WKUInt64Create(callbackID)).get() }, })); } void TestRunner::runUIScriptImmediately(JSStringRef script, JSValueRef callback) { unsigned callbackID = nextUIScriptCallbackID(); cacheTestRunnerCallback(callbackID, callback); postPageMessage("RunUIProcessScriptImmediately", createWKDictionary({ { "Script", toWK(script) }, { "CallbackID", adoptWK(WKUInt64Create(callbackID)).get() }, })); } void TestRunner::runUIScriptCallback(unsigned callbackID, JSStringRef result) { JSValueRef resultValue = JSValueMakeString(mainFrameJSContext(), result); callTestRunnerCallback(callbackID, 1, &resultValue); } void TestRunner::setAllowedMenuActions(JSValueRef actions) { auto messageBody = adoptWK(WKMutableArrayCreate()); auto context = mainFrameJSContext(); auto actionsArray = JSValueToObject(context, actions, nullptr); auto length = arrayLength(context, actionsArray); for (unsigned i = 0; i < length; ++i) { auto value = JSObjectGetPropertyAtIndex(context, actionsArray, i, nullptr); WKArrayAppendItem(messageBody.get(), toWKString(context, value).get()); } postPageMessage("SetAllowedMenuActions", messageBody); } void TestRunner::installCustomMenuAction(JSStringRef name, bool dismissesAutomatically, JSValueRef callback) { cacheTestRunnerCallback(CustomMenuActionCallbackID, callback); postPageMessage("InstallCustomMenuAction", createWKDictionary({ { "name", toWK(name) }, { "dismissesAutomatically", adoptWK(WKBooleanCreate(dismissesAutomatically)).get() }, })); } void TestRunner::installDidBeginSwipeCallback(JSValueRef callback) { cacheTestRunnerCallback(DidBeginSwipeCallbackID, callback); } void TestRunner::installWillEndSwipeCallback(JSValueRef callback) { cacheTestRunnerCallback(WillEndSwipeCallbackID, callback); } void TestRunner::installDidEndSwipeCallback(JSValueRef callback) { cacheTestRunnerCallback(DidEndSwipeCallbackID, callback); } void TestRunner::installDidRemoveSwipeSnapshotCallback(JSValueRef callback) { cacheTestRunnerCallback(DidRemoveSwipeSnapshotCallbackID, callback); } void TestRunner::callDidBeginSwipeCallback() { callTestRunnerCallback(DidBeginSwipeCallbackID); } void TestRunner::callWillEndSwipeCallback() { callTestRunnerCallback(WillEndSwipeCallbackID); } void TestRunner::callDidEndSwipeCallback() { callTestRunnerCallback(DidEndSwipeCallbackID); } void TestRunner::callDidRemoveSwipeSnapshotCallback() { callTestRunnerCallback(DidRemoveSwipeSnapshotCallbackID); } void TestRunner::clearStatisticsDataForDomain(JSStringRef domain) { postSynchronousMessage("ClearStatisticsDataForDomain", toWK(domain)); } bool TestRunner::doesStatisticsDomainIDExistInDatabase(unsigned domainID) { return postSynchronousPageMessageReturningBoolean("DoesStatisticsDomainIDExistInDatabase", createWKDictionary({ { "DomainID", adoptWK(WKUInt64Create(domainID)) } })); } void TestRunner::setStatisticsEnabled(bool value) { postSynchronousMessage("SetStatisticsEnabled", value); } bool TestRunner::isStatisticsEphemeral() { return postSynchronousPageMessageReturningBoolean("IsStatisticsEphemeral"); } void TestRunner::setStatisticsDebugMode(bool value, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsDebugModeCallbackID, completionHandler); postMessage("SetStatisticsDebugMode", value); } void TestRunner::statisticsCallDidSetDebugModeCallback() { callTestRunnerCallback(SetStatisticsDebugModeCallbackID); } void TestRunner::setStatisticsPrevalentResourceForDebugMode(JSStringRef hostName, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsPrevalentResourceForDebugModeCallbackID, completionHandler); postMessage("SetStatisticsPrevalentResourceForDebugMode", hostName); } void TestRunner::statisticsCallDidSetPrevalentResourceForDebugModeCallback() { callTestRunnerCallback(SetStatisticsPrevalentResourceForDebugModeCallbackID); } void TestRunner::setStatisticsLastSeen(JSStringRef hostName, double seconds, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsLastSeenCallbackID, completionHandler); postMessage("SetStatisticsLastSeen", createWKDictionary({ { "HostName", toWK(hostName) }, { "Value", toWK(seconds) }, })); } void TestRunner::statisticsCallDidSetLastSeenCallback() { callTestRunnerCallback(SetStatisticsLastSeenCallbackID); } void TestRunner::setStatisticsMergeStatistic(JSStringRef hostName, JSStringRef topFrameDomain1, JSStringRef topFrameDomain2, double lastSeen, bool hadUserInteraction, double mostRecentUserInteraction, bool isGrandfathered, bool isPrevalent, bool isVeryPrevalent, unsigned dataRecordsRemoved, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsMergeStatisticCallbackID, completionHandler); postMessage("SetStatisticsMergeStatistic", createWKDictionary({ { "HostName", toWK(hostName) }, { "TopFrameDomain1", toWK(topFrameDomain1) }, { "TopFrameDomain2", toWK(topFrameDomain2) }, { "LastSeen", adoptWK(WKDoubleCreate(lastSeen)) }, { "HadUserInteraction", adoptWK(WKBooleanCreate(hadUserInteraction)) }, { "MostRecentUserInteraction", adoptWK(WKDoubleCreate(mostRecentUserInteraction)) }, { "IsGrandfathered", adoptWK(WKBooleanCreate(isGrandfathered)) }, { "IsPrevalent", adoptWK(WKBooleanCreate(isPrevalent)) }, { "IsVeryPrevalent", adoptWK(WKBooleanCreate(isVeryPrevalent)) }, { "DataRecordsRemoved", adoptWK(WKUInt64Create(dataRecordsRemoved)) }, })); } void TestRunner::statisticsCallDidSetMergeStatisticCallback() { callTestRunnerCallback(SetStatisticsMergeStatisticCallbackID); } void TestRunner::setStatisticsExpiredStatistic(JSStringRef hostName, unsigned numberOfOperatingDaysPassed, bool hadUserInteraction, bool isScheduledForAllButCookieDataRemoval, bool isPrevalent, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsExpiredStatisticCallbackID, completionHandler); postMessage("SetStatisticsExpiredStatistic", createWKDictionary({ { "HostName", toWK(hostName) }, { "NumberOfOperatingDaysPassed", adoptWK(WKUInt64Create(numberOfOperatingDaysPassed)) }, { "HadUserInteraction", adoptWK(WKBooleanCreate(hadUserInteraction)) }, { "IsScheduledForAllButCookieDataRemoval", adoptWK(WKBooleanCreate(isScheduledForAllButCookieDataRemoval)) }, { "IsPrevalent", adoptWK(WKBooleanCreate(isPrevalent)) } })); } void TestRunner::statisticsCallDidSetExpiredStatisticCallback() { callTestRunnerCallback(SetStatisticsExpiredStatisticCallbackID); } void TestRunner::setStatisticsPrevalentResource(JSStringRef hostName, bool value, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsPrevalentResourceCallbackID, completionHandler); postMessage("SetStatisticsPrevalentResource", createWKDictionary({ { "HostName", toWK(hostName) }, { "Value", adoptWK(WKBooleanCreate(value)) }, })); } void TestRunner::statisticsCallDidSetPrevalentResourceCallback() { callTestRunnerCallback(SetStatisticsPrevalentResourceCallbackID); } void TestRunner::setStatisticsVeryPrevalentResource(JSStringRef hostName, bool value, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsVeryPrevalentResourceCallbackID, completionHandler); postMessage("SetStatisticsVeryPrevalentResource", createWKDictionary({ { "HostName", toWK(hostName) }, { "Value", adoptWK(WKBooleanCreate(value)) }, })); } void TestRunner::statisticsCallDidSetVeryPrevalentResourceCallback() { callTestRunnerCallback(SetStatisticsVeryPrevalentResourceCallbackID); } void TestRunner::dumpResourceLoadStatistics() { postSynchronousPageMessage("dumpResourceLoadStatistics"); } bool TestRunner::isStatisticsPrevalentResource(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("IsStatisticsPrevalentResource", hostName); } bool TestRunner::isStatisticsVeryPrevalentResource(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("IsStatisticsVeryPrevalentResource", hostName); } bool TestRunner::isStatisticsRegisteredAsSubresourceUnder(JSStringRef subresourceHost, JSStringRef topFrameHost) { return postSynchronousPageMessageReturningBoolean("IsStatisticsRegisteredAsSubresourceUnder", createWKDictionary({ { "SubresourceHost", toWK(subresourceHost) }, { "TopFrameHost", toWK(topFrameHost) }, })); } bool TestRunner::isStatisticsRegisteredAsSubFrameUnder(JSStringRef subFrameHost, JSStringRef topFrameHost) { return postSynchronousPageMessageReturningBoolean("IsStatisticsRegisteredAsSubFrameUnder", createWKDictionary({ { "SubFrameHost", toWK(subFrameHost) }, { "TopFrameHost", toWK(topFrameHost) }, })); } bool TestRunner::isStatisticsRegisteredAsRedirectingTo(JSStringRef hostRedirectedFrom, JSStringRef hostRedirectedTo) { return postSynchronousPageMessageReturningBoolean("IsStatisticsRegisteredAsRedirectingTo", createWKDictionary({ { "HostRedirectedFrom", toWK(hostRedirectedFrom) }, { "HostRedirectedTo", toWK(hostRedirectedTo) }, })); } void TestRunner::setStatisticsHasHadUserInteraction(JSStringRef hostName, bool value, JSValueRef completionHandler) { cacheTestRunnerCallback(SetStatisticsHasHadUserInteractionCallbackID, completionHandler); postMessage("SetStatisticsHasHadUserInteraction", createWKDictionary({ { "HostName", toWK(hostName) }, { "Value", adoptWK(WKBooleanCreate(value)) }, })); } void TestRunner::statisticsCallDidSetHasHadUserInteractionCallback() { callTestRunnerCallback(SetStatisticsHasHadUserInteractionCallbackID); } bool TestRunner::isStatisticsHasHadUserInteraction(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("IsStatisticsHasHadUserInteraction", hostName); } bool TestRunner::isStatisticsOnlyInDatabaseOnce(JSStringRef subHost, JSStringRef topHost) { return postSynchronousPageMessageReturningBoolean("IsStatisticsOnlyInDatabaseOnce", createWKDictionary({ { "SubHost", toWK(subHost) }, { "TopHost", toWK(topHost) }, })); } void TestRunner::setStatisticsGrandfathered(JSStringRef hostName, bool value) { postSynchronousMessage("SetStatisticsGrandfathered", createWKDictionary({ { "HostName", toWK(hostName) }, { "Value", adoptWK(WKBooleanCreate(value)) }, })); } bool TestRunner::isStatisticsGrandfathered(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("IsStatisticsGrandfathered", hostName); } void TestRunner::setStatisticsSubframeUnderTopFrameOrigin(JSStringRef hostName, JSStringRef topFrameHostName) { postSynchronousMessage("SetStatisticsSubframeUnderTopFrameOrigin", createWKDictionary({ { "HostName", toWK(hostName) }, { "TopFrameHostName", toWK(topFrameHostName) }, })); } void TestRunner::setStatisticsSubresourceUnderTopFrameOrigin(JSStringRef hostName, JSStringRef topFrameHostName) { postSynchronousMessage("SetStatisticsSubresourceUnderTopFrameOrigin", createWKDictionary({ { "HostName", toWK(hostName) }, { "TopFrameHostName", toWK(topFrameHostName) }, })); } void TestRunner::setStatisticsSubresourceUniqueRedirectTo(JSStringRef hostName, JSStringRef hostNameRedirectedTo) { postSynchronousMessage("SetStatisticsSubresourceUniqueRedirectTo", createWKDictionary({ { "HostName", toWK(hostName) }, { "HostNameRedirectedTo", toWK(hostNameRedirectedTo) }, })); } void TestRunner::setStatisticsSubresourceUniqueRedirectFrom(JSStringRef hostName, JSStringRef hostNameRedirectedFrom) { postSynchronousMessage("SetStatisticsSubresourceUniqueRedirectFrom", createWKDictionary({ { "HostName", toWK(hostName) }, { "HostNameRedirectedFrom", toWK(hostNameRedirectedFrom) }, })); } void TestRunner::setStatisticsTopFrameUniqueRedirectTo(JSStringRef hostName, JSStringRef hostNameRedirectedTo) { postSynchronousMessage("SetStatisticsTopFrameUniqueRedirectTo", createWKDictionary({ { "HostName", toWK(hostName) }, { "HostNameRedirectedTo", toWK(hostNameRedirectedTo) }, })); } void TestRunner::setStatisticsTopFrameUniqueRedirectFrom(JSStringRef hostName, JSStringRef hostNameRedirectedFrom) { postSynchronousMessage("SetStatisticsTopFrameUniqueRedirectFrom", createWKDictionary({ { "HostName", toWK(hostName) }, { "HostNameRedirectedFrom", toWK(hostNameRedirectedFrom) }, })); } void TestRunner::setStatisticsCrossSiteLoadWithLinkDecoration(JSStringRef fromHost, JSStringRef toHost) { postSynchronousMessage("SetStatisticsCrossSiteLoadWithLinkDecoration", createWKDictionary({ { "FromHost", toWK(fromHost) }, { "ToHost", toWK(toHost) }, })); } void TestRunner::setStatisticsTimeToLiveUserInteraction(double seconds) { postSynchronousMessage("SetStatisticsTimeToLiveUserInteraction", seconds); } void TestRunner::installStatisticsDidModifyDataRecordsCallback(JSValueRef callback) { if (!!callback) { cacheTestRunnerCallback(StatisticsDidModifyDataRecordsCallbackID, callback); // Setting a callback implies we expect to receive callbacks. So register for them. setStatisticsNotifyPagesWhenDataRecordsWereScanned(true); } } void TestRunner::statisticsDidModifyDataRecordsCallback() { callTestRunnerCallback(StatisticsDidModifyDataRecordsCallbackID); } void TestRunner::installStatisticsDidScanDataRecordsCallback(JSValueRef callback) { if (!!callback) { cacheTestRunnerCallback(StatisticsDidScanDataRecordsCallbackID, callback); // Setting a callback implies we expect to receive callbacks. So register for them. setStatisticsNotifyPagesWhenDataRecordsWereScanned(true); } } void TestRunner::statisticsDidScanDataRecordsCallback() { callTestRunnerCallback(StatisticsDidScanDataRecordsCallbackID); } bool TestRunner::statisticsNotifyObserver() { return InjectedBundle::singleton().statisticsNotifyObserver(); } void TestRunner::statisticsProcessStatisticsAndDataRecords() { postSynchronousMessage("StatisticsProcessStatisticsAndDataRecords"); } void TestRunner::statisticsUpdateCookieBlocking(JSValueRef completionHandler) { cacheTestRunnerCallback(StatisticsDidSetBlockCookiesForHostCallbackID, completionHandler); postMessage("StatisticsUpdateCookieBlocking"); } void TestRunner::statisticsCallDidSetBlockCookiesForHostCallback() { callTestRunnerCallback(StatisticsDidSetBlockCookiesForHostCallbackID); } void TestRunner::setStatisticsNotifyPagesWhenDataRecordsWereScanned(bool value) { postSynchronousMessage("StatisticsNotifyPagesWhenDataRecordsWereScanned", value); } void TestRunner::setStatisticsIsRunningTest(bool value) { postSynchronousMessage("StatisticsSetIsRunningTest", value); } void TestRunner::setStatisticsShouldClassifyResourcesBeforeDataRecordsRemoval(bool value) { postSynchronousMessage("StatisticsShouldClassifyResourcesBeforeDataRecordsRemoval", value); } void TestRunner::setStatisticsMinimumTimeBetweenDataRecordsRemoval(double seconds) { postSynchronousMessage("SetStatisticsMinimumTimeBetweenDataRecordsRemoval", seconds); } void TestRunner::setStatisticsGrandfatheringTime(double seconds) { postSynchronousMessage("SetStatisticsGrandfatheringTime", seconds); } void TestRunner::setStatisticsMaxStatisticsEntries(unsigned entries) { postSynchronousMessage("SetMaxStatisticsEntries", entries); } void TestRunner::setStatisticsPruneEntriesDownTo(unsigned entries) { postSynchronousMessage("SetPruneEntriesDownTo", entries); } void TestRunner::statisticsClearInMemoryAndPersistentStore(JSValueRef callback) { cacheTestRunnerCallback(StatisticsDidClearInMemoryAndPersistentStoreCallbackID, callback); postMessage("StatisticsClearInMemoryAndPersistentStore"); } void TestRunner::statisticsClearInMemoryAndPersistentStoreModifiedSinceHours(unsigned hours, JSValueRef callback) { cacheTestRunnerCallback(StatisticsDidClearInMemoryAndPersistentStoreCallbackID, callback); postMessage("StatisticsClearInMemoryAndPersistentStoreModifiedSinceHours", hours); } void TestRunner::statisticsClearThroughWebsiteDataRemoval(JSValueRef callback) { cacheTestRunnerCallback(StatisticsDidClearThroughWebsiteDataRemovalCallbackID, callback); postMessage("StatisticsClearThroughWebsiteDataRemoval"); } void TestRunner::statisticsDeleteCookiesForHost(JSStringRef hostName, bool includeHttpOnlyCookies) { postSynchronousMessage("StatisticsDeleteCookiesForHost", createWKDictionary({ { "HostName", toWK(hostName) }, { "IncludeHttpOnlyCookies", adoptWK(WKBooleanCreate(includeHttpOnlyCookies)) }, })); } bool TestRunner::isStatisticsHasLocalStorage(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("IsStatisticsHasLocalStorage", hostName); } void TestRunner::setStatisticsCacheMaxAgeCap(double seconds) { postSynchronousMessage("SetStatisticsCacheMaxAgeCap", seconds); } bool TestRunner::hasStatisticsIsolatedSession(JSStringRef hostName) { return postSynchronousPageMessageReturningBoolean("HasStatisticsIsolatedSession", hostName); } void TestRunner::setStatisticsShouldDowngradeReferrer(bool value, JSValueRef completionHandler) { if (m_hasSetDowngradeReferrerCallback) return; cacheTestRunnerCallback(StatisticsDidSetShouldDowngradeReferrerCallbackID, completionHandler); postMessage("SetStatisticsShouldDowngradeReferrer", value); m_hasSetDowngradeReferrerCallback = true; } void TestRunner::statisticsCallDidSetShouldDowngradeReferrerCallback() { callTestRunnerCallback(StatisticsDidSetShouldDowngradeReferrerCallbackID); m_hasSetDowngradeReferrerCallback = false; } void TestRunner::setStatisticsShouldBlockThirdPartyCookies(bool value, JSValueRef completionHandler, bool onlyOnSitesWithoutUserInteraction) { if (m_hasSetBlockThirdPartyCookiesCallback) return; cacheTestRunnerCallback(StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID, completionHandler); auto messageName = "SetStatisticsShouldBlockThirdPartyCookies"; if (onlyOnSitesWithoutUserInteraction) messageName = "SetStatisticsShouldBlockThirdPartyCookiesOnSitesWithoutUserInteraction"; postMessage(messageName, value); m_hasSetBlockThirdPartyCookiesCallback = true; } void TestRunner::statisticsCallDidSetShouldBlockThirdPartyCookiesCallback() { callTestRunnerCallback(StatisticsDidSetShouldBlockThirdPartyCookiesCallbackID); m_hasSetBlockThirdPartyCookiesCallback = false; } void TestRunner::setStatisticsFirstPartyWebsiteDataRemovalMode(bool value, JSValueRef completionHandler) { if (m_hasSetFirstPartyWebsiteDataRemovalModeCallback) return; cacheTestRunnerCallback(StatisticsDidSetFirstPartyWebsiteDataRemovalModeCallbackID, completionHandler); postMessage("SetStatisticsFirstPartyWebsiteDataRemovalMode", value); m_hasSetFirstPartyWebsiteDataRemovalModeCallback = true; } void TestRunner::statisticsCallDidSetFirstPartyWebsiteDataRemovalModeCallback() { callTestRunnerCallback(StatisticsDidSetFirstPartyWebsiteDataRemovalModeCallbackID); m_hasSetFirstPartyWebsiteDataRemovalModeCallback = false; } void TestRunner::statisticsCallClearInMemoryAndPersistentStoreCallback() { callTestRunnerCallback(StatisticsDidClearInMemoryAndPersistentStoreCallbackID); } void TestRunner::statisticsCallClearThroughWebsiteDataRemovalCallback() { callTestRunnerCallback(StatisticsDidClearThroughWebsiteDataRemovalCallbackID); } void TestRunner::statisticsSetToSameSiteStrictCookies(JSStringRef hostName, JSValueRef completionHandler) { cacheTestRunnerCallback(StatisticsDidSetToSameSiteStrictCookiesCallbackID, completionHandler); postMessage("StatisticsSetToSameSiteStrictCookies", hostName); } void TestRunner::statisticsCallDidSetToSameSiteStrictCookiesCallback() { callTestRunnerCallback(StatisticsDidSetToSameSiteStrictCookiesCallbackID); } void TestRunner::statisticsSetFirstPartyHostCNAMEDomain(JSStringRef firstPartyURLString, JSStringRef cnameURLString, JSValueRef completionHandler) { cacheTestRunnerCallback(StatisticsDidSetFirstPartyHostCNAMEDomainCallbackID, completionHandler); postMessage("StatisticsSetFirstPartyHostCNAMEDomain", createWKDictionary({ { "FirstPartyURL", toWK(firstPartyURLString) }, { "CNAME", toWK(cnameURLString) }, })); } void TestRunner::statisticsCallDidSetFirstPartyHostCNAMEDomainCallback() { callTestRunnerCallback(StatisticsDidSetFirstPartyHostCNAMEDomainCallbackID); } void TestRunner::statisticsSetThirdPartyCNAMEDomain(JSStringRef cnameURLString, JSValueRef completionHandler) { cacheTestRunnerCallback(StatisticsDidSetThirdPartyCNAMEDomainCallbackID, completionHandler); postMessage("StatisticsSetThirdPartyCNAMEDomain", toWK(cnameURLString)); } void TestRunner::statisticsCallDidSetThirdPartyCNAMEDomainCallback() { callTestRunnerCallback(StatisticsDidSetThirdPartyCNAMEDomainCallbackID); } void TestRunner::statisticsResetToConsistentState(JSValueRef completionHandler) { cacheTestRunnerCallback(StatisticsDidResetToConsistentStateCallbackID, completionHandler); postMessage("StatisticsResetToConsistentState"); } void TestRunner::statisticsCallDidResetToConsistentStateCallback() { callTestRunnerCallback(StatisticsDidResetToConsistentStateCallbackID); } void TestRunner::installTextDidChangeInTextFieldCallback(JSValueRef callback) { cacheTestRunnerCallback(TextDidChangeInTextFieldCallbackID, callback); } void TestRunner::textDidChangeInTextFieldCallback() { callTestRunnerCallback(TextDidChangeInTextFieldCallbackID); } void TestRunner::installTextFieldDidBeginEditingCallback(JSValueRef callback) { cacheTestRunnerCallback(TextFieldDidBeginEditingCallbackID, callback); } void TestRunner::textFieldDidBeginEditingCallback() { callTestRunnerCallback(TextFieldDidBeginEditingCallbackID); } void TestRunner::installTextFieldDidEndEditingCallback(JSValueRef callback) { cacheTestRunnerCallback(TextFieldDidEndEditingCallbackID, callback); } void TestRunner::textFieldDidEndEditingCallback() { callTestRunnerCallback(TextFieldDidEndEditingCallbackID); } void TestRunner::getAllStorageAccessEntries(JSValueRef callback) { cacheTestRunnerCallback(AllStorageAccessEntriesCallbackID, callback); postMessage("GetAllStorageAccessEntries"); } static JSValueRef makeDomainsValue(const Vector& domains) { StringBuilder builder; builder.append('['); bool firstDomain = true; for (auto& domain : domains) { builder.append(firstDomain ? "\"" : ", \"", domain, '"'); firstDomain = false; } builder.append(']'); return JSValueMakeFromJSONString(mainFrameJSContext(), createJSString(builder.toString().utf8().data()).get()); } void TestRunner::callDidReceiveAllStorageAccessEntriesCallback(Vector& domains) { auto result = makeDomainsValue(domains); callTestRunnerCallback(AllStorageAccessEntriesCallbackID, 1, &result); } void TestRunner::loadedSubresourceDomains(JSValueRef callback) { cacheTestRunnerCallback(LoadedSubresourceDomainsCallbackID, callback); postMessage("LoadedSubresourceDomains"); } void TestRunner::callDidReceiveLoadedSubresourceDomainsCallback(Vector&& domains) { auto result = makeDomainsValue(domains); callTestRunnerCallback(LoadedSubresourceDomainsCallbackID, 1, &result); } void TestRunner::addMockMediaDevice(JSStringRef persistentId, JSStringRef label, const char* type) { postSynchronousMessage("AddMockMediaDevice", createWKDictionary({ { "PersistentID", toWK(persistentId) }, { "Label", toWK(label) }, { "Type", toWK(type) }, })); } void TestRunner::addMockCameraDevice(JSStringRef persistentId, JSStringRef label) { addMockMediaDevice(persistentId, label, "camera"); } void TestRunner::addMockMicrophoneDevice(JSStringRef persistentId, JSStringRef label) { addMockMediaDevice(persistentId, label, "microphone"); } void TestRunner::addMockScreenDevice(JSStringRef persistentId, JSStringRef label) { addMockMediaDevice(persistentId, label, "screen"); } void TestRunner::clearMockMediaDevices() { postSynchronousMessage("ClearMockMediaDevices"); } void TestRunner::removeMockMediaDevice(JSStringRef persistentId) { postSynchronousMessage("RemoveMockMediaDevice", toWK(persistentId)); } void TestRunner::resetMockMediaDevices() { postSynchronousMessage("ResetMockMediaDevices"); } void TestRunner::setMockCameraOrientation(unsigned orientation) { postSynchronousMessage("SetMockCameraOrientation", orientation); } bool TestRunner::isMockRealtimeMediaSourceCenterEnabled() { return postSynchronousMessageReturningBoolean("IsMockRealtimeMediaSourceCenterEnabled"); } #if ENABLE(GAMEPAD) void TestRunner::connectMockGamepad(unsigned index) { postSynchronousMessage("ConnectMockGamepad", index); } void TestRunner::disconnectMockGamepad(unsigned index) { postSynchronousMessage("DisconnectMockGamepad", index); } void TestRunner::setMockGamepadDetails(unsigned index, JSStringRef gamepadID, JSStringRef mapping, unsigned axisCount, unsigned buttonCount) { postSynchronousMessage("SetMockGamepadDetails", createWKDictionary({ { "GamepadID", toWK(gamepadID) }, { "Mapping", toWK(mapping) }, { "GamepadIndex", adoptWK(WKUInt64Create(index)) }, { "AxisCount", adoptWK(WKUInt64Create(axisCount)) }, { "ButtonCount", adoptWK(WKUInt64Create(buttonCount)) }, })); } void TestRunner::setMockGamepadAxisValue(unsigned index, unsigned axisIndex, double value) { postSynchronousMessage("SetMockGamepadAxisValue", createWKDictionary({ { "GamepadIndex", adoptWK(WKUInt64Create(index)) }, { "AxisIndex", adoptWK(WKUInt64Create(axisIndex)) }, { "Value", adoptWK(WKDoubleCreate(value)) }, })); } void TestRunner::setMockGamepadButtonValue(unsigned index, unsigned buttonIndex, double value) { postSynchronousMessage("SetMockGamepadButtonValue", createWKDictionary({ { "GamepadIndex", adoptWK(WKUInt64Create(index)) }, { "ButtonIndex", adoptWK(WKUInt64Create(buttonIndex)) }, { "Value", adoptWK(WKDoubleCreate(value)) }, })); } #else void TestRunner::connectMockGamepad(unsigned) { } void TestRunner::disconnectMockGamepad(unsigned) { } void TestRunner::setMockGamepadDetails(unsigned, JSStringRef, JSStringRef, unsigned, unsigned) { } void TestRunner::setMockGamepadAxisValue(unsigned, unsigned, double) { } void TestRunner::setMockGamepadButtonValue(unsigned, unsigned, double) { } #endif // ENABLE(GAMEPAD) void TestRunner::setOpenPanelFiles(JSValueRef filesValue) { JSContextRef context = mainFrameJSContext(); if (!JSValueIsArray(context, filesValue)) return; auto files = (JSObjectRef)filesValue; auto fileURLs = adoptWK(WKMutableArrayCreate()); auto filesLength = arrayLength(context, files); for (size_t i = 0; i < filesLength; ++i) { JSValueRef fileValue = JSObjectGetPropertyAtIndex(context, files, i, nullptr); if (!JSValueIsString(context, fileValue)) continue; auto file = createJSString(context, fileValue); size_t fileBufferSize = JSStringGetMaximumUTF8CStringSize(file.get()) + 1; auto fileBuffer = makeUniqueArray(fileBufferSize); JSStringGetUTF8CString(file.get(), fileBuffer.get(), fileBufferSize); auto baseURL = m_testURL.get(); if (fileBuffer[0] == '/') baseURL = WKURLCreateWithUTF8CString("file://"); WKArrayAppendItem(fileURLs.get(), adoptWK(WKURLCreateWithBaseURL(baseURL, fileBuffer.get())).get()); } postPageMessage("SetOpenPanelFileURLs", fileURLs); } void TestRunner::setOpenPanelFilesMediaIcon(JSValueRef data) { #if PLATFORM(IOS_FAMILY) // FIXME (123058): Use a JSC API to get buffer contents once such is exposed. auto iconData = adoptWK(WKBundleCreateWKDataFromUInt8Array(InjectedBundle::singleton().bundle(), mainFrameJSContext(), data)); postPageMessage("SetOpenPanelFileURLsMediaIcon", iconData); #else UNUSED_PARAM(data); #endif } void TestRunner::removeAllSessionCredentials(JSValueRef callback) { cacheTestRunnerCallback(DidRemoveAllSessionCredentialsCallbackID, callback); postMessage("RemoveAllSessionCredentials", true); } void TestRunner::callDidRemoveAllSessionCredentialsCallback() { callTestRunnerCallback(DidRemoveAllSessionCredentialsCallbackID); } void TestRunner::clearDOMCache(JSStringRef origin) { postSynchronousMessage("ClearDOMCache", toWK(origin)); } void TestRunner::clearDOMCaches() { postSynchronousMessage("ClearDOMCaches"); } bool TestRunner::hasDOMCache(JSStringRef origin) { return postSynchronousPageMessageReturningBoolean("HasDOMCache", origin); } uint64_t TestRunner::domCacheSize(JSStringRef origin) { return postSynchronousPageMessageReturningUInt64("DOMCacheSize", origin); } void TestRunner::setAllowStorageQuotaIncrease(bool willIncrease) { postSynchronousPageMessage("SetAllowStorageQuotaIncrease", willIncrease); } void TestRunner::getApplicationManifestThen(JSValueRef callback) { cacheTestRunnerCallback(GetApplicationManifestCallbackID, callback); postMessage("GetApplicationManifest"); } void TestRunner::didGetApplicationManifest() { callTestRunnerCallback(GetApplicationManifestCallbackID); } void TestRunner::performCustomMenuAction() { callTestRunnerCallback(CustomMenuActionCallbackID); } size_t TestRunner::userScriptInjectedCount() const { return InjectedBundle::singleton().userScriptInjectedCount(); } void TestRunner::injectUserScript(JSStringRef script) { postSynchronousMessage("InjectUserScript", toWK(script)); } void TestRunner::sendDisplayConfigurationChangedMessageForTesting() { postSynchronousMessage("SendDisplayConfigurationChangedMessageForTesting"); } void TestRunner::setServiceWorkerFetchTimeout(double seconds) { postSynchronousMessage("SetServiceWorkerFetchTimeout", seconds); } // WebAuthn void TestRunner::addTestKeyToKeychain(JSStringRef privateKeyBase64, JSStringRef attrLabel, JSStringRef applicationTagBase64) { postSynchronousMessage("AddTestKeyToKeychain", createWKDictionary({ { "PrivateKey", toWK(privateKeyBase64) }, { "AttrLabel", toWK(attrLabel) }, { "ApplicationTag", toWK(applicationTagBase64) }, })); } void TestRunner::cleanUpKeychain(JSStringRef attrLabel, JSStringRef applicationLabelBase64) { if (!applicationLabelBase64) { postSynchronousMessage("CleanUpKeychain", createWKDictionary({ { "AttrLabel", toWK(attrLabel) }, })); return; } postSynchronousMessage("CleanUpKeychain", createWKDictionary({ { "AttrLabel", toWK(attrLabel) }, { "ApplicationLabel", toWK(applicationLabelBase64) }, })); } bool TestRunner::keyExistsInKeychain(JSStringRef attrLabel, JSStringRef applicationLabelBase64) { return postSynchronousMessageReturningBoolean("KeyExistsInKeychain", createWKDictionary({ { "AttrLabel", toWK(attrLabel) }, { "ApplicationLabel", toWK(applicationLabelBase64) }, })); } unsigned long TestRunner::serverTrustEvaluationCallbackCallsCount() { return postSynchronousMessageReturningUInt64("ServerTrustEvaluationCallbackCallsCount"); } void TestRunner::setShouldDismissJavaScriptAlertsAsynchronously(bool shouldDismissAsynchronously) { postSynchronousMessage("ShouldDismissJavaScriptAlertsAsynchronously", shouldDismissAsynchronously); } void TestRunner::abortModal() { postSynchronousMessage("AbortModal"); } void TestRunner::dumpPrivateClickMeasurement() { postSynchronousPageMessage("DumpPrivateClickMeasurement"); } void TestRunner::clearPrivateClickMeasurement() { postSynchronousPageMessage("ClearPrivateClickMeasurement"); } void TestRunner::clearPrivateClickMeasurementsThroughWebsiteDataRemoval() { postSynchronousMessage("ClearPrivateClickMeasurementsThroughWebsiteDataRemoval"); } void TestRunner::setPrivateClickMeasurementOverrideTimerForTesting(bool value) { postSynchronousPageMessage("SetPrivateClickMeasurementOverrideTimerForTesting", value); } void TestRunner::markAttributedPrivateClickMeasurementsAsExpiredForTesting() { postSynchronousPageMessage("MarkAttributedPrivateClickMeasurementsAsExpiredForTesting"); } void TestRunner::simulateResourceLoadStatisticsSessionRestart() { postSynchronousPageMessage("SimulateResourceLoadStatisticsSessionRestart"); } void TestRunner::setPrivateClickMeasurementTokenPublicKeyURLForTesting(JSStringRef urlString) { postSynchronousPageMessage("SetPrivateClickMeasurementTokenPublicKeyURLForTesting", adoptWK(WKURLCreateWithUTF8CString(toWTFString(urlString).utf8().data()))); } void TestRunner::setPrivateClickMeasurementTokenSignatureURLForTesting(JSStringRef urlString) { postSynchronousPageMessage("SetPrivateClickMeasurementTokenSignatureURLForTesting", adoptWK(WKURLCreateWithUTF8CString(toWTFString(urlString).utf8().data()))); } void TestRunner::setPrivateClickMeasurementAttributionReportURLsForTesting(JSStringRef sourceURLString, JSStringRef destinationURLString) { postSynchronousPageMessage("SetPrivateClickMeasurementAttributionReportURLsForTesting", createWKDictionary({ { "SourceURLString", toWK(sourceURLString) }, { "AttributeOnURLString", toWK(destinationURLString) }, })); } void TestRunner::markPrivateClickMeasurementsAsExpiredForTesting() { postSynchronousPageMessage("MarkPrivateClickMeasurementsAsExpiredForTesting"); } void TestRunner::setPrivateClickMeasurementFraudPreventionValuesForTesting(JSStringRef unlinkableToken, JSStringRef secretToken, JSStringRef signature, JSStringRef keyID) { postSynchronousMessage("SetPCMFraudPreventionValuesForTesting", createWKDictionary({ { "UnlinkableToken", toWK(unlinkableToken) }, { "SecretToken", toWK(secretToken) }, { "Signature", toWK(signature) }, { "KeyID", toWK(keyID) }, })); } bool TestRunner::hasAppBoundSession() { return postSynchronousPageMessageReturningBoolean("HasAppBoundSession"); } void TestRunner::clearAppBoundSession() { postSynchronousMessage("ClearAppBoundSession"); } void TestRunner::setAppBoundDomains(JSValueRef originArray, JSValueRef completionHandler) { cacheTestRunnerCallback(DidSetAppBoundDomainsCallbackID, completionHandler); auto context = mainFrameJSContext(); if (!JSValueIsArray(context, originArray)) return; auto origins = JSValueToObject(context, originArray, nullptr); auto originURLs = adoptWK(WKMutableArrayCreate()); auto originsLength = arrayLength(context, origins); for (unsigned i = 0; i < originsLength; ++i) { JSValueRef originValue = JSObjectGetPropertyAtIndex(context, origins, i, nullptr); if (!JSValueIsString(context, originValue)) continue; auto origin = createJSString(context, originValue); size_t originBufferSize = JSStringGetMaximumUTF8CStringSize(origin.get()) + 1; auto originBuffer = makeUniqueArray(originBufferSize); JSStringGetUTF8CString(origin.get(), originBuffer.get(), originBufferSize); WKArrayAppendItem(originURLs.get(), adoptWK(WKURLCreateWithUTF8CString(originBuffer.get())).get()); } auto messageName = toWK("SetAppBoundDomains"); WKBundlePostMessage(InjectedBundle::singleton().bundle(), messageName.get(), originURLs.get()); } void TestRunner::didSetAppBoundDomainsCallback() { callTestRunnerCallback(DidSetAppBoundDomainsCallbackID); } bool TestRunner::didLoadAppInitiatedRequest() { return postSynchronousPageMessageReturningBoolean("DidLoadAppInitiatedRequest"); } bool TestRunner::didLoadNonAppInitiatedRequest() { return postSynchronousPageMessageReturningBoolean("DidLoadNonAppInitiatedRequest"); } void TestRunner::setIsSpeechRecognitionPermissionGranted(bool granted) { postSynchronousPageMessage("SetIsSpeechRecognitionPermissionGranted", granted); } void TestRunner::setIsMediaKeySystemPermissionGranted(bool granted) { postSynchronousPageMessage("SetIsMediaKeySystemPermissionGranted", granted); } ALLOW_DEPRECATED_DECLARATIONS_END } // namespace WTR