blob: da2bebd0446e9dd7bb97faea01d6d6a5b9dd6177 [file] [log] [blame] [edit]
/*
* 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 "InjectedBundlePage.h"
#include "ActivateFonts.h"
#include "InjectedBundle.h"
#include "StringFunctions.h"
#include "WPTFunctions.h"
#include "WebCoreTestSupport.h"
#include <cmath>
#include <JavaScriptCore/JSRetainPtr.h>
#include <JavaScriptCore/RegularExpression.h>
#include <WebKit/WKArray.h>
#include <WebKit/WKBundle.h>
#include <WebKit/WKBundleBackForwardList.h>
#include <WebKit/WKBundleBackForwardListItem.h>
#include <WebKit/WKBundleFrame.h>
#include <WebKit/WKBundleFramePrivate.h>
#include <WebKit/WKBundleHitTestResult.h>
#include <WebKit/WKBundleNodeHandlePrivate.h>
#include <WebKit/WKBundlePagePrivate.h>
#include <WebKit/WKBundlePrivate.h>
#include <WebKit/WKBundleRangeHandlePrivate.h>
#include <WebKit/WKSecurityOriginRef.h>
#include <WebKit/WKURLRequest.h>
#include <wtf/HashMap.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/RunLoop.h>
#include <wtf/URL.h>
#include <wtf/text/CString.h>
#if USE(CF)
#include "WebArchiveDumpSupport.h"
#include <wtf/cf/VectorCF.h>
#include <wtf/text/cf/StringConcatenateCF.h>
#endif
namespace WTF {
template<> class StringTypeAdapter<WKStringRef> {
public:
StringTypeAdapter(WKStringRef);
unsigned length() const { return m_string ? WKStringGetLength(m_string) : 0; }
bool is8Bit() const { return !m_string; }
template<typename CharacterType> void writeTo(std::span<CharacterType>) const;
private:
WKStringRef m_string;
};
inline StringTypeAdapter<WKStringRef>::StringTypeAdapter(WKStringRef string)
: m_string { string }
{
}
template<> inline void StringTypeAdapter<WKStringRef>::writeTo<Latin1Character>(std::span<Latin1Character>) const
{
}
template<> inline void StringTypeAdapter<WKStringRef>::writeTo<char16_t>(std::span<char16_t> destination) const
{
if (m_string)
WKStringGetCharacters(m_string, reinterpret_cast<WKChar*>(destination.data()), WKStringGetLength(m_string));
}
}
namespace WTR {
static double numericWindowProperty(WKBundleFrameRef frame, const char* name)
{
auto context = WKBundleFrameGetJavaScriptContext(frame);
return numericProperty(context, JSContextGetGlobalObject(context), name);
}
static WTF::String dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
{
auto name = toWTFString(stringProperty(context, nodeValue, "nodeName"));
if (auto parentNode = objectProperty(context, nodeValue, "parentNode"))
return makeString(name, " > "_s, dumpPath(context, parentNode));
return name;
}
static WTF::String dumpPath(WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
{
if (!node)
return "(null)"_s;
auto frame = adoptWK(WKBundleNodeHandleCopyOwningDocumentFrame(node));
JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame.get(), world);
JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame.get(), node, world);
ASSERT(JSValueIsObject(context, nodeValue));
JSObjectRef nodeObject = (JSObjectRef)nodeValue;
return dumpPath(context, nodeObject);
}
static WTF::String string(WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
{
if (!rangeRef)
return "(null)"_s;
auto frame = adoptWK(WKBundleRangeHandleCopyDocumentFrame(rangeRef));
auto context = WKBundleFrameGetJavaScriptContextForWorld(frame.get(), world);
auto rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame.get(), rangeRef, world);
ASSERT(JSValueIsObject(context, rangeValue));
auto rangeObject = (JSObjectRef)rangeValue;
return makeString("range from "_s,
numericProperty(context, rangeObject, "startOffset"),
" of "_s,
dumpPath(context, objectProperty(context, rangeObject, "startContainer")),
" to "_s,
numericProperty(context, rangeObject, "endOffset"),
" of "_s,
dumpPath(context, objectProperty(context, rangeObject, "endContainer"))
);
}
static WTF::String styleDecToStr(WKBundleCSSStyleDeclarationRef)
{
// DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
// No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
// the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
return "<DOMCSSStyleDeclaration ADDRESS>"_s;
}
static WTF::String string(WKBundleFrameRef frame)
{
auto name = adoptWK(WKBundleFrameCopyName(frame));
bool isMain = WKBundleFrameIsMainFrame(frame);
if (WKStringIsEmpty(name.get()))
return isMain ? "main frame"_s : "frame (anonymous)"_s;
return makeString(isMain ? "main frame \""_s : "frame \""_s, name.get(), '"');
}
static inline bool isLocalFileScheme(WKStringRef scheme)
{
return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "file");
}
static const char divider = '/';
WTF::String pathSuitableForTestResult(WKURLRef fileURL)
{
if (!fileURL)
return "(null)"_s;
auto schemeString = adoptWK(WKURLCopyScheme(fileURL));
if (!isLocalFileScheme(schemeString.get()))
return toWTFString(adoptWK(WKURLCopyString(fileURL)));
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
ALLOW_DEPRECATED_DECLARATIONS_END
auto mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
if (!mainFrameURL)
mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
String pathString = toWTFString(adoptWK(WKURLCopyPath(fileURL)));
String mainFrameURLPathString = mainFrameURL ? toWTFString(adoptWK(WKURLCopyPath(mainFrameURL.get()))) : ""_s;
auto basePath = StringView(mainFrameURLPathString).left(mainFrameURLPathString.reverseFind(divider) + 1);
if (!basePath.isEmpty() && pathString.startsWith(basePath))
return pathString.substring(basePath.length());
return toWTFString(adoptWK(WKURLCopyLastPathComponent(fileURL))); // We lose some information here, but it's better than exposing a full path, which is always machine specific.
}
static HashMap<uint64_t, String>& assignedUrlsCache()
{
static NeverDestroyed<HashMap<uint64_t, String>> cache;
return cache.get();
}
static inline void dumpResourceURL(uint64_t identifier, StringBuilder& stringBuilder)
{
if (assignedUrlsCache().contains(identifier))
stringBuilder.append(assignedUrlsCache().get(identifier));
else
stringBuilder.append("<unknown>"_s);
}
static HashMap<WKBundlePageRef, InjectedBundlePage*>& bundlePageMap()
{
static NeverDestroyed<HashMap<WKBundlePageRef, InjectedBundlePage*>> map;
return map.get();
}
InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
: m_page(page)
, m_world(adoptWK(WKBundleScriptWorldCreateWorld()))
{
ASSERT(!bundlePageMap().contains(page));
bundlePageMap().set(page, this);
WKBundlePageLoaderClientV9 loaderClient = {
{ 9, this },
didStartProvisionalLoadForFrame,
didReceiveServerRedirectForProvisionalLoadForFrame,
didFailProvisionalLoadWithErrorForFrame,
didCommitLoadForFrame,
didFinishDocumentLoadForFrame,
didFinishLoadForFrame,
didFailLoadWithErrorForFrame,
didSameDocumentNavigationForFrame,
didReceiveTitleForFrame,
0, // didFirstLayoutForFrame
0, // didFirstVisuallyNonEmptyLayoutForFrame
0, // didRemoveFrameFromHierarchy
0, // didDisplayInsecureContentForFrame
0, // didRunInsecureContentForFrame
didClearWindowForFrame,
didCancelClientRedirectForFrame,
willPerformClientRedirectForFrame,
didHandleOnloadEventsForFrame,
0, // didLayoutForFrame
0, // didNewFirstVisuallyNonEmptyLayout_unavailable
0, // didDetectXSSForFrame
0, // shouldGoToBackForwardListItem
0, // didCreateGlobalObjectForFrame
0, // willDisconnectDOMWindowExtensionFromGlobalObject
0, // didReconnectDOMWindowExtensionToGlobalObject
0, // willDestroyGlobalObjectForDOMWindowExtension
didFinishProgress, // didFinishProgress
0, // shouldForceUniversalAccessFromLocalURL
0, // didReceiveIntentForFrame
0, // registerIntentServiceForFrame
0, // didLayout
0, // featuresUsedInPage
0, // willLoadURLRequest
0, // willLoadDataRequest
0, // willDestroyFrame_unavailable
0, // userAgentForURL
willInjectUserScriptForFrame
};
WKBundlePageSetPageLoaderClient(m_page, &loaderClient.base);
WKBundlePageResourceLoadClientV1 resourceLoadClient = {
{ 1, this },
didInitiateLoadForResource,
willSendRequestForFrame,
didReceiveResponseForResource,
didReceiveContentLengthForResource,
didFinishLoadForResource,
didFailLoadForResource,
shouldCacheResponse,
0 // shouldUseCredentialStorage
};
WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient.base);
WKBundlePageEditorClientV1 editorClient = {
{ 1, this },
shouldBeginEditing,
shouldEndEditing,
shouldInsertNode,
shouldInsertText,
shouldDeleteRange,
shouldChangeSelectedRange,
shouldApplyStyle,
didBeginEditing,
didEndEditing,
didChange,
didChangeSelection,
0, /* willWriteToPasteboard */
0, /* getPasteboardDataForRange */
0, /* didWriteToPasteboard */
0, /* performTwoStepDrop */
};
WKBundlePageSetEditorClient(m_page, &editorClient.base);
}
InjectedBundlePage::~InjectedBundlePage()
{
ASSERT(bundlePageMap().contains(m_page));
bundlePageMap().remove(m_page);
}
void InjectedBundlePage::resetAfterTest()
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
ALLOW_DEPRECATED_DECLARATIONS_END
// WebKit currently doesn't reset focus even when navigating to a new page. This may or may not be a bug
// (see <https://bugs.webkit.org/show_bug.cgi?id=138334>), however for tests, we want to start each one with a clean state.
WKBundleFrameFocus(frame);
WebCoreTestSupport::resetInternalsObject(WKBundleFrameGetJavaScriptContext(frame));
assignedUrlsCache().clear();
// User scripts need to be removed after the test and before loading about:blank, as otherwise they would run in about:blank, and potentially leak results into a subsequest test.
WKBundlePageRemoveAllUserContent(m_page);
uninstallFakeHelvetica();
InjectedBundle::singleton().resetUserScriptInjectedCount();
m_didCommitMainFrameLoad = false;
}
// Loader Client Callbacks
static void dumpLoadEvent(WKBundleFrameRef frame, ASCIILiteral eventName)
{
InjectedBundle::singleton().outputText(makeString(string(frame), " - "_s, eventName, '\n'));
}
static String string(WKURLRequestRef request)
{
auto url = adoptWK(WKURLRequestCopyURL(request));
auto firstParty = adoptWK(WKURLRequestCopyFirstPartyForCookies(request));
auto httpMethod = adoptWK(WKURLRequestCopyHTTPMethod(request));
return makeString("<NSURLRequest URL "_s, pathSuitableForTestResult(url.get()),
", main document URL "_s, pathSuitableForTestResult(firstParty.get()),
", http method "_s, WKStringIsEmpty(httpMethod.get()) ? "(none)"_s : ""_s, httpMethod.get(), '>');
}
static String string(WKURLResponseRef response, bool shouldDumpResponseHeaders = false)
{
auto url = adoptWK(WKURLResponseCopyURL(response));
if (!url)
return "(null)"_s;
if (!shouldDumpResponseHeaders) {
return makeString("<NSURLResponse "_s, pathSuitableForTestResult(url.get()),
", http status code "_s, WKURLResponseHTTPStatusCode(response), '>');
}
return makeString("<NSURLResponse "_s, pathSuitableForTestResult(url.get()),
", http status code "_s, WKURLResponseHTTPStatusCode(response),
", "_s, InjectedBundlePage::responseHeaderCount(response), " headers>"_s);
}
#if !PLATFORM(COCOA)
// FIXME: Implement this for non-Cocoa ports. [GTK][WPE] https://bugs.webkit.org/show_bug.cgi?id=184295
uint64_t InjectedBundlePage::responseHeaderCount(WKURLResponseRef response)
{
return 0;
}
#endif
static inline void dumpErrorDescriptionSuitableForTestResult(WKErrorRef error, StringBuilder& stringBuilder)
{
auto errorDomain = toWTFString(adoptWK(WKErrorCopyDomain(error)));
auto errorCode = WKErrorGetErrorCode(error);
// We need to do some error mapping here to match the test expectations (Mac error names are expected).
if (errorDomain == "WebKitNetworkError"_s) {
errorDomain = "NSURLErrorDomain"_s;
errorCode = -999;
}
if (errorDomain == "WebKitPolicyError"_s)
errorDomain = "WebKitErrorDomain"_s;
stringBuilder.append("<NSError domain "_s, errorDomain, ", code "_s, errorCode);
if (auto url = adoptWK(WKErrorCopyFailingURL(error)))
stringBuilder.append(", failing URL \""_s, adoptWK(WKURLCopyString(url.get())).get(), '"');
stringBuilder.append('>');
}
void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
}
void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
}
void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
}
void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
}
void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
}
void InjectedBundlePage::didFinishProgress(WKBundlePageRef, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishProgress();
}
void InjectedBundlePage::willInjectUserScriptForFrame(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willInjectUserScriptForFrame();
}
void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
}
void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
}
void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
}
void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
}
void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
}
void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(page, frame, url, delay, date);
}
void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
}
void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
}
void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
}
WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
}
void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
}
void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
}
void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
}
void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadForResource(page, frame, identifier, error);
}
bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldCacheResponse(page, frame, identifier);
}
void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->testURL()) {
auto testURL = adoptWK(WKBundleFrameCopyProvisionalURL(frame));
testRunner->setTestURL(testURL.get());
}
platformDidStartProvisionalLoadForFrame(frame);
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didStartProvisionalLoadForFrame"_s);
if (!injectedBundle.topLoadingFrame())
injectedBundle.setTopLoadingFrame(frame);
if (testRunner->shouldStopProvisionalFrameLoads())
dumpLoadEvent(frame, "stopping load in didStartProvisionalLoadForFrame callback"_s);
}
void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpFrameLoadCallbacks())
return;
dumpLoadEvent(frame, "didReceiveServerRedirectForProvisionalLoadForFrame"_s);
}
void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
// In case of a COOP process-swap, the old process gets a didFailProvisionalLoadWithErrorForFrame delegate call. We want to ignore
// this call since it causes the test to dump its output too eagerly, before the test has had a chance to run in the new process.
if (WKErrorGetErrorCode(error) == kWKErrorCodeFrameLoadInterruptedByPolicyChange && WKBundleFrameIsMainFrame(frame) && !m_didCommitMainFrameLoad && injectedBundle.page() == this)
return;
if (testRunner->shouldDumpFrameLoadCallbacks()) {
dumpLoadEvent(frame, "didFailProvisionalLoadWithError"_s);
auto code = WKErrorGetErrorCode(error);
if (code == kWKErrorCodeCannotShowURL)
dumpLoadEvent(frame, "(ErrorCodeCannotShowURL)"_s);
else if (code == kWKErrorCodeFrameLoadBlockedByContentBlocker)
dumpLoadEvent(frame, "(kWKErrorCodeFrameLoadBlockedByContentBlocker)"_s);
}
frameDidChangeLocation(frame);
}
void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (WKBundleFrameIsMainFrame(frame))
m_didCommitMainFrameLoad = true;
if (!testRunner->shouldDumpFrameLoadCallbacks())
return;
dumpLoadEvent(frame, "didCommitLoadForFrame"_s);
}
void InjectedBundlePage::didFinishProgress()
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpProgressFinishedCallback())
return;
injectedBundle.outputText("postProgressFinishedNotification\n"_s);
}
void InjectedBundlePage::willInjectUserScriptForFrame()
{
InjectedBundle::singleton().increaseUserScriptInjectedCount();
}
enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
static void dumpFrameScrollPosition(WKBundleFrameRef frame, StringBuilder& stringBuilder, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
{
double x = numericWindowProperty(frame, "pageXOffset");
double y = numericWindowProperty(frame, "pageYOffset");
if (std::abs(x) <= 0.00000001 && std::abs(y) <= 0.00000001)
return;
if (shouldIncludeFrameName)
stringBuilder.append("frame '"_s, adoptWK(WKBundleFrameCopyName(frame)).get(), "' "_s);
stringBuilder.append("scrolled to "_s, x, ',', y, '\n');
}
static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame, StringBuilder& stringBuilder)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
auto childFrames = adoptWK(WKBundleFrameCopyChildFrames(frame));
ALLOW_DEPRECATED_DECLARATIONS_END
size_t size = WKArrayGetSize(childFrames.get());
for (size_t i = 0; i < size; ++i) {
WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
dumpFrameScrollPosition(subframe, stringBuilder, ShouldIncludeFrameName);
dumpDescendantFrameScrollPositions(subframe, stringBuilder);
}
}
void InjectedBundlePage::dumpAllFrameScrollPositions(StringBuilder& stringBuilder)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
ALLOW_DEPRECATED_DECLARATIONS_END
dumpFrameScrollPosition(frame, stringBuilder);
dumpDescendantFrameScrollPositions(frame, stringBuilder);
}
void InjectedBundlePage::dumpAllFramesText(StringBuilder& stringBuilder)
{
constexpr bool includeSubframes { true };
stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyFrameTextForTesting(m_page, includeSubframes))));
}
void InjectedBundlePage::dumpDOMAsWebArchive(WKBundleFrameRef frame, StringBuilder& stringBuilder)
{
#if USE(CF)
auto wkData = adoptWK(WKBundleFrameCopyWebArchive(frame));
RetainPtr cfData = toCFData(WKDataGetSpan(wkData.get()));
stringBuilder.append(WebCoreTestSupport::createXMLStringFromWebArchiveData(cfData.get()).get());
#endif
}
void InjectedBundlePage::dump()
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner) {
ASSERT_NOT_REACHED();
return;
}
if (injectedBundle.shouldForceRepaint()) {
// Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac
// does this at a slightly different time.) See <http://webkit.org/b/55469> for details.
WKBundlePageForceRepaint(m_page);
}
WKBundlePageFlushPendingEditorStateUpdate(m_page);
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
ALLOW_DEPRECATED_DECLARATIONS_END
auto urlRef = adoptWK(WKBundleFrameCopyURL(frame));
if (!urlRef)
return;
String url = toWTFString(adoptWK(WKURLCopyString(urlRef.get())));
auto mimeType = adoptWK(WKBundleFrameCopyMIMETypeForResourceWithURL(frame, urlRef.get()));
if (url.find("dumpAsText/"_s) != notFound || WKStringIsEqualToUTF8CString(mimeType.get(), "text/plain"))
testRunner->dumpAsText(false);
StringBuilder stringBuilder;
switch (testRunner->whatToDump()) {
case WhatToDump::RenderTree: {
if (injectedBundle.isPrinting())
stringBuilder.append(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentationForPrinting(m_page)).get());
else
stringBuilder.append(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentation(m_page, testRunner->renderTreeDumpOptions())).get());
break;
}
case WhatToDump::MainFrameText: {
constexpr bool includeSubframes { false };
stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyFrameTextForTesting(m_page, includeSubframes))));
break;
}
case WhatToDump::AllFramesText:
dumpAllFramesText(stringBuilder);
break;
case WhatToDump::Audio:
break;
case WhatToDump::DOMAsWebArchive:
dumpDOMAsWebArchive(frame, stringBuilder);
break;
}
if (testRunner->shouldDumpAllFrameScrollPositions())
dumpAllFrameScrollPositions(stringBuilder);
else if (testRunner->shouldDumpMainFrameScrollPosition()) {
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page), stringBuilder);
ALLOW_DEPRECATED_DECLARATIONS_END
}
if (testRunner->shouldDumpBackForwardListsForAllWindows())
injectedBundle.dumpBackForwardListsForAllPages(stringBuilder);
if (injectedBundle.shouldDumpPixels() && testRunner->shouldDumpPixels()) {
bool shouldCreateSnapshot = injectedBundle.isPrinting();
if (shouldCreateSnapshot) {
WKSnapshotOptions options = kWKSnapshotOptionsShareable;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKRect snapshotRect = WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page));
ALLOW_DEPRECATED_DECLARATIONS_END
if (injectedBundle.isPrinting())
options |= kWKSnapshotOptionsPrinting;
else {
options |= kWKSnapshotOptionsInViewCoordinates;
if (testRunner->shouldDumpSelectionRect())
options |= kWKSnapshotOptionsPaintSelectionRectangle;
}
injectedBundle.setPixelResult(adoptWK(WKBundlePageCreateSnapshotWithOptions(m_page, snapshotRect, options)).get());
} else
injectedBundle.setPixelResultIsPending(true);
if (WKBundlePageIsTrackingRepaints(m_page) && !injectedBundle.isPrinting())
injectedBundle.setRepaintRects(adoptWK(WKBundlePageCopyTrackedRepaintRects(m_page)).get());
}
injectedBundle.outputText(stringBuilder.toString(), InjectedBundle::IsFinalTestOutput::Yes);
injectedBundle.done();
}
void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didFinishLoadForFrame"_s);
frameDidChangeLocation(frame);
}
void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didFailLoadWithError"_s);
frameDidChangeLocation(frame);
}
void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
StringBuilder stringBuilder;
if (testRunner->shouldDumpFrameLoadCallbacks())
stringBuilder.append(string(frame), " - didReceiveTitle: "_s, title, '\n');
if (testRunner->shouldDumpTitleChanges())
stringBuilder.append("TITLE CHANGED: '"_s, title, "'\n"_s);
injectedBundle.outputText(stringBuilder.toString());
}
void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
auto context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
if (!context)
return;
if (WKBundleScriptWorldNormalWorld() != world) {
setGlobalObjectProperty(context, "__worldID", TestRunner::worldIDForWorld(world));
return;
}
testRunner->makeWindowObject(context);
injectedBundle.gcController()->makeWindowObject(context);
injectedBundle.eventSendingController()->makeWindowObject(context);
injectedBundle.textInputController()->makeWindowObject(context);
injectedBundle.accessibilityController()->makeWindowObject(context);
WebCoreTestSupport::injectInternalsObject(context);
}
void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didCancelClientRedirectForFrame"_s);
testRunner->setDidCancelClientRedirect(true);
}
void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKURLRef url, double delay, double date)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpFrameLoadCallbacks())
return;
injectedBundle.outputText(makeString(string(frame), " - willPerformClientRedirectToURL: "_s, pathSuitableForTestResult(url), '\n'));
}
void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpFrameLoadCallbacks())
return;
if (type != kWKSameDocumentNavigationAnchorNavigation)
return;
dumpLoadEvent(frame, "didChangeLocationWithinPageForFrame"_s);
}
void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didFinishDocumentLoadForFrame"_s);
if (unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame))
injectedBundle.outputText(makeString(string(frame), " - has "_s, pendingFrameUnloadEvents, " onunload handler(s)\n"_s));
}
void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
{
RefPtr testRunner = InjectedBundle::singleton().testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpFrameLoadCallbacks())
dumpLoadEvent(frame, "didHandleOnloadEventsForFrame"_s);
}
void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef request, bool)
{
if (!InjectedBundle::singleton().isTestRunning())
return;
auto url = adoptWK(WKURLRequestCopyURL(request));
assignedUrlsCache().add(identifier, pathSuitableForTestResult(url.get()));
}
// Resource Load Client Callbacks
static inline bool isLocalHost(WKStringRef host)
{
return WKStringIsEqualToUTF8CString(host, "127.0.0.1") || WKStringIsEqualToUTF8CString(host, "localhost");
}
static inline bool isHTTPOrHTTPSScheme(WKStringRef scheme)
{
return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "https");
}
static inline bool isAllowedHost(WKStringRef host)
{
return InjectedBundle::singleton().isAllowedHost(host);
}
WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef response)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (testRunner && testRunner->shouldDumpResourceLoadCallbacks()) {
StringBuilder stringBuilder;
dumpResourceURL(identifier, stringBuilder);
stringBuilder.append(" - willSendRequest "_s, string(request),
" redirectResponse "_s, string(response, testRunner->shouldDumpAllHTTPRedirectedResponseHeaders()), '\n');
injectedBundle.outputText(stringBuilder.toString());
}
if (testRunner && testRunner->willSendRequestReturnsNull())
return nullptr;
auto redirectURL = adoptWK(WKURLResponseCopyURL(response));
if (testRunner && testRunner->willSendRequestReturnsNullOnRedirect() && redirectURL) {
injectedBundle.outputText("Returning null for this redirect\n"_s);
return nullptr;
}
auto url = adoptWK(WKURLRequestCopyURL(request));
auto host = adoptWK(WKURLCopyHostName(url.get()));
auto scheme = adoptWK(WKURLCopyScheme(url.get()));
auto urlString = adoptWK(WKURLCopyString(url.get()));
if (host && !WKStringIsEmpty(host.get())
&& isHTTPOrHTTPSScheme(scheme.get())
&& !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error.
&& !isLocalHost(host.get())) {
bool mainFrameIsExternal = false;
if (injectedBundle.isTestRunning()) {
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(m_page);
ALLOW_DEPRECATED_DECLARATIONS_END
auto mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
if (!mainFrameURL || WKStringIsEqualToUTF8CString(adoptWK(WKURLCopyString(mainFrameURL.get())).get(), "about:blank"))
mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
if (mainFrameURL) {
auto mainFrameHost = adoptWK(WKURLCopyHostName(mainFrameURL.get()));
auto mainFrameScheme = adoptWK(WKURLCopyScheme(mainFrameURL.get()));
mainFrameIsExternal = isHTTPOrHTTPSScheme(mainFrameScheme.get()) && !isLocalHost(mainFrameHost.get());
}
}
if (!mainFrameIsExternal && !isAllowedHost(host.get())) {
auto blockedURL = makeString(urlString.get());
replace(blockedURL, JSC::Yarr::RegularExpression("\\?key=[-0123456789abcdefABCDEF]+"_s), "?key=GENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("&key=[-0123456789abcdefABCDEF]+"_s), "&key=GENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("%3Fkey%3D[-0123456789abcdefABCDEF]+"_s), "%3Fkey%3DGENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("%26key%3D[-0123456789abcdefABCDEF]+"_s), "%26key%3DGENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("%253Fkey%253D[-0123456789abcdefABCDEF]+"_s), "%253Fkey%253DGENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("%2526key%253D[-0123456789abcdefABCDEF]+"_s), "%2526key%253DGENERATED_KEY"_s);
replace(blockedURL, JSC::Yarr::RegularExpression("reportID=[-0123456789abcdefABCDEF]+"_s), "reportID=GENERATED_REPORT_ID"_s);
auto script = makeString("console.log('Blocked access to external URL "_s, blockedURL, "');"_s);
auto scriptRef = adopt(JSStringCreateWithUTF8CString(script.utf8().data()));
JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContext(frame);
JSEvaluateScript(jsContext, scriptRef.get(), 0, 0, 0, 0);
return nullptr;
}
}
if (testRunner) {
String body = testRunner->willSendRequestHTTPBody();
if (!body.isEmpty()) {
CString cBody = body.utf8();
auto body = adoptWK(WKDataCreate(reinterpret_cast<const unsigned char*>(cBody.data()), cBody.length()));
return WKURLRequestCopySettingHTTPBody(request, body.get());
}
}
WKRetain(request);
return request;
}
void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (testRunner->shouldDumpResourceLoadCallbacks()) {
StringBuilder stringBuilder;
dumpResourceURL(identifier, stringBuilder);
stringBuilder.append(" - didReceiveResponse "_s, string(response), '\n');
injectedBundle.outputText(stringBuilder.toString());
}
if (!testRunner->shouldDumpResourceResponseMIMETypes())
return;
auto url = adoptWK(WKURLResponseCopyURL(response));
auto urlString = adoptWK(WKURLCopyLastPathComponent(url.get()));
auto mimeTypeString = adoptWK(WKURLResponseCopyMIMEType(response));
StringBuilder stringBuilder;
stringBuilder.append(urlString.get(), " has MIME type "_s, mimeTypeString.get());
String platformMimeType = platformResponseMimeType(response);
if (!platformMimeType.isEmpty() && platformMimeType != toWTFString(mimeTypeString)) {
stringBuilder.append(" but platform response has "_s, platformMimeType);
}
stringBuilder.append('\n');
injectedBundle.outputText(stringBuilder.toString());
}
void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
{
}
void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpResourceLoadCallbacks())
return;
StringBuilder stringBuilder;
dumpResourceURL(identifier, stringBuilder);
stringBuilder.append(" - didFinishLoading\n"_s);
injectedBundle.outputText(stringBuilder.toString());
}
void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef error)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpResourceLoadCallbacks())
return;
StringBuilder stringBuilder;
dumpResourceURL(identifier, stringBuilder);
stringBuilder.append(" - didFailLoadingWithError: "_s);
dumpErrorDescriptionSuitableForTestResult(error, stringBuilder);
stringBuilder.append('\n');
injectedBundle.outputText(stringBuilder.toString());
}
bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
if (!testRunner->shouldDumpWillCacheResponse())
return true;
injectedBundle.outputText(makeString(identifier, " - willCacheResponse: called\n"_s));
// The default behavior is the cache the response.
return true;
}
// Editor Client Callbacks
bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
}
bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
}
bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
}
bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
}
bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
}
bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
}
bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
{
return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
}
void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
}
void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
}
void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
}
void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
{
static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
}
bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
if (testRunner->shouldDumpEditingCallbacks())
injectedBundle.outputText(makeString("EDITING DELEGATE: shouldBeginEditingInDOMRange:"_s, string(m_world.get(), range), '\n'));
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
if (testRunner->shouldDumpEditingCallbacks())
injectedBundle.outputText(makeString("EDITING DELEGATE: shouldEndEditingInDOMRange:"_s, string(m_world.get(), range), '\n'));
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
static constexpr ASCIILiteral insertactionstring[] = {
"WebViewInsertActionTyped"_s,
"WebViewInsertActionPasted"_s,
"WebViewInsertActionDropped"_s,
};
if (testRunner->shouldDumpEditingCallbacks()) {
injectedBundle.outputText(makeString("EDITING DELEGATE:"_s
" shouldInsertNode:"_s, dumpPath(m_world.get(), node),
" replacingDOMRange:"_s, string(m_world.get(), rangeToReplace),
" givenAction:"_s, insertactionstring[action], '\n'));
}
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
static constexpr ASCIILiteral insertactionstring[] = {
"WebViewInsertActionTyped"_s,
"WebViewInsertActionPasted"_s,
"WebViewInsertActionDropped"_s,
};
if (testRunner->shouldDumpEditingCallbacks()) {
injectedBundle.outputText(makeString("EDITING DELEGATE:"_s
" shouldInsertText:"_s, text,
" replacingDOMRange:"_s, string(m_world.get(), rangeToReplace),
" givenAction:"_s, insertactionstring[action], '\n'));
}
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
if (testRunner->shouldDumpEditingCallbacks())
injectedBundle.outputText(makeString("EDITING DELEGATE: shouldDeleteDOMRange:"_s, string(m_world.get(), range), '\n'));
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
static constexpr ASCIILiteral affinitystring[] = {
"NSSelectionAffinityUpstream"_s,
"NSSelectionAffinityDownstream"_s
};
if (testRunner->shouldDumpEditingCallbacks()) {
injectedBundle.outputText(makeString("EDITING DELEGATE:"_s
" shouldChangeSelectedDOMRange:"_s, string(m_world.get(), fromRange),
" toDOMRange:"_s, string(m_world.get(), toRange),
" affinity:"_s, affinitystring[affinity],
" stillSelecting:"_s, stillSelecting ? "TRUE"_s : "FALSE"_s, '\n'));
}
return testRunner->shouldAllowEditing();
}
bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return true;
if (testRunner->shouldDumpEditingCallbacks()) {
injectedBundle.outputText(makeString("EDITING DELEGATE:"
" shouldApplyStyle:"_s, styleDecToStr(style),
" toElementsInDOMRange:"_s, string(m_world.get(), range), '\n'));
}
return testRunner->shouldAllowEditing();
}
void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpEditingCallbacks())
return;
injectedBundle.outputText(makeString("EDITING DELEGATE: webViewDidBeginEditing:"_s, notificationName, '\n'));
}
void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpEditingCallbacks())
return;
injectedBundle.outputText(makeString("EDITING DELEGATE: webViewDidEndEditing:"_s, notificationName, '\n'));
}
void InjectedBundlePage::didChange(WKStringRef notificationName)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpEditingCallbacks())
return;
injectedBundle.outputText(makeString("EDITING DELEGATE: webViewDidChange:"_s, notificationName, '\n'));
}
void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner)
return;
if (!testRunner->shouldDumpEditingCallbacks())
return;
injectedBundle.outputText(makeString("EDITING DELEGATE: webViewDidChangeSelection:"_s, notificationName, '\n'));
}
String InjectedBundlePage::dumpHistory()
{
return makeString(
"\n============== Back Forward List ==============\n"_s,
adoptWK(WKBundlePageDumpHistoryForTesting(m_page, toWK("/LayoutTests/").get())).get(),
"===============================================\n"_s
);
}
#if !PLATFORM(COCOA)
void InjectedBundlePage::platformDidStartProvisionalLoadForFrame(WKBundleFrameRef)
{
}
String InjectedBundlePage::platformResponseMimeType(WKURLResponseRef)
{
return String();
}
#endif
static bool hasTestWaitAttribute(WKBundlePageRef page)
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
auto frame = WKBundlePageGetMainFrame(page);
ALLOW_DEPRECATED_DECLARATIONS_END
return frame && hasTestWaitAttribute(WKBundleFrameGetJavaScriptContext(frame));
}
static void dumpAfterWaitAttributeIsRemoved(WKBundlePageRef page)
{
if (hasTestWaitAttribute(page)) {
WKRetain(page);
// Use a 1ms interval between tries to allow lower priority run loop sources with zero delays to run.
RunLoop::currentSingleton().dispatchAfter(1_ms, [page] {
WKBundlePageCallAfterTasksAndTimers(page, [] (void* typelessPage) {
auto page = static_cast<WKBundlePageRef>(typelessPage);
dumpAfterWaitAttributeIsRemoved(page);
WKRelease(page);
}, const_cast<OpaqueWKBundlePage*>(page));
});
return;
}
auto& bundle = InjectedBundle::singleton();
RefPtr testRunner = bundle.testRunner();
if (!testRunner)
return;
if (auto currentPage = bundle.page(); currentPage && currentPage->page() == page)
currentPage->dump();
}
void InjectedBundlePage::frameDidChangeLocation(WKBundleFrameRef frame)
{
auto& injectedBundle = InjectedBundle::singleton();
RefPtr testRunner = injectedBundle.testRunner();
if (!testRunner) {
ASSERT_NOT_REACHED();
return;
}
if (frame != injectedBundle.topLoadingFrame())
return;
injectedBundle.setTopLoadingFrame(nullptr);
if (testRunner->shouldDisplayOnLoadFinish()) {
if (auto page = InjectedBundle::singleton().page())
WKBundlePageForceRepaint(page->page());
}
if (testRunner->shouldWaitUntilDone())
return;
if (injectedBundle.shouldProcessWorkQueue()) {
injectedBundle.processWorkQueue();
return;
}
auto page = InjectedBundle::singleton().page();
if (!page) {
injectedBundle.done();
return;
}
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (auto frame = WKBundlePageGetMainFrame(page->page()))
sendTestRenderedEvent(WKBundleFrameGetJavaScriptContext(frame));
ALLOW_DEPRECATED_DECLARATIONS_END
dumpAfterWaitAttributeIsRemoved(page->page());
}
void InjectedBundlePage::notifyDone()
{
if (InjectedBundle::singleton().topLoadingFrame())
return;
forceImmediateCompletion();
}
void InjectedBundlePage::forceImmediateCompletion()
{
dump();
}
} // namespace WTR