1/*
2 * Copyright (C) 2010-2016 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "InjectedBundlePage.h"
28
29#include "ActivateFonts.h"
30#include "InjectedBundle.h"
31#include "StringFunctions.h"
32#include "WebCoreTestSupport.h"
33#include <cmath>
34#include <JavaScriptCore/JSRetainPtr.h>
35#include <WebKit/WKArray.h>
36#include <WebKit/WKBundle.h>
37#include <WebKit/WKBundleBackForwardList.h>
38#include <WebKit/WKBundleBackForwardListItem.h>
39#include <WebKit/WKBundleFrame.h>
40#include <WebKit/WKBundleFramePrivate.h>
41#include <WebKit/WKBundleHitTestResult.h>
42#include <WebKit/WKBundleNavigationAction.h>
43#include <WebKit/WKBundleNavigationActionPrivate.h>
44#include <WebKit/WKBundleNodeHandlePrivate.h>
45#include <WebKit/WKBundlePagePrivate.h>
46#include <WebKit/WKBundlePrivate.h>
47#include <WebKit/WKSecurityOriginRef.h>
48#include <WebKit/WKURLRequest.h>
49#include <wtf/HashMap.h>
50#include <wtf/text/CString.h>
51#include <wtf/text/StringBuilder.h>
52
53#if USE(CF) && !PLATFORM(WIN_CAIRO)
54#include "WebArchiveDumpSupport.h"
55#endif
56
57using namespace std;
58
59namespace WTR {
60
61static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
62{
63 if (!object)
64 return 0;
65 auto propertyNameString = adopt(JSStringCreateWithUTF8CString(propertyName));
66 return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
67}
68
69static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
70{
71 JSValueRef value = propertyValue(context, object, propertyName);
72 if (!value)
73 return 0;
74 return JSValueToNumber(context, value, 0);
75}
76
77static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
78{
79 return static_cast<int>(propertyValueDouble(context, object, propertyName));
80}
81
82static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
83{
84 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
85 return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
86}
87
88static WTF::String dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
89{
90 JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
91 auto jsStringNodeName = adopt(JSValueToStringCopy(context, nodeNameValue, 0));
92 WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
93
94 JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
95
96 StringBuilder stringBuilder;
97 stringBuilder.append(toWTFString(nodeName));
98
99 if (parentNode && JSValueIsObject(context, parentNode)) {
100 stringBuilder.appendLiteral(" > ");
101 stringBuilder.append(dumpPath(context, (JSObjectRef)parentNode));
102 }
103
104 return stringBuilder.toString();
105}
106
107static WTF::String dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
108{
109 if (!node)
110 return "(null)";
111
112 WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
113
114 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
115 JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
116 ASSERT(JSValueIsObject(context, nodeValue));
117 JSObjectRef nodeObject = (JSObjectRef)nodeValue;
118
119 return dumpPath(context, nodeObject);
120}
121
122static WTF::String rangeToStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
123{
124 if (!rangeRef)
125 return "(null)";
126
127 WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
128
129 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
130 JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
131 ASSERT(JSValueIsObject(context, rangeValue));
132 JSObjectRef rangeObject = (JSObjectRef)rangeValue;
133
134 JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
135 ASSERT(JSValueIsObject(context, startNodeValue));
136 JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
137
138 JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
139 ASSERT(JSValueIsObject(context, endNodeValue));
140 JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
141
142 int startOffset = propertyValueInt(context, rangeObject, "startOffset");
143 int endOffset = propertyValueInt(context, rangeObject, "endOffset");
144
145 StringBuilder stringBuilder;
146 stringBuilder.appendLiteral("range from ");
147 stringBuilder.appendNumber(startOffset);
148 stringBuilder.appendLiteral(" of ");
149 stringBuilder.append(dumpPath(context, startNodeObject));
150 stringBuilder.appendLiteral(" to ");
151 stringBuilder.appendNumber(endOffset);
152 stringBuilder.appendLiteral(" of ");
153 stringBuilder.append(dumpPath(context, endNodeObject));
154 return stringBuilder.toString();
155}
156
157static WKRetainPtr<WKStringRef> NavigationTypeToString(WKFrameNavigationType type)
158{
159 switch (type) {
160 case kWKFrameNavigationTypeLinkClicked:
161 return adoptWK(WKStringCreateWithUTF8CString("link clicked"));
162 case kWKFrameNavigationTypeFormSubmitted:
163 return adoptWK(WKStringCreateWithUTF8CString("form submitted"));
164 case kWKFrameNavigationTypeBackForward:
165 return adoptWK(WKStringCreateWithUTF8CString("back/forward"));
166 case kWKFrameNavigationTypeReload:
167 return adoptWK(WKStringCreateWithUTF8CString("reload"));
168 case kWKFrameNavigationTypeFormResubmitted:
169 return adoptWK(WKStringCreateWithUTF8CString("form resubmitted"));
170 case kWKFrameNavigationTypeOther:
171 return adoptWK(WKStringCreateWithUTF8CString("other"));
172 }
173 return adoptWK(WKStringCreateWithUTF8CString("illegal value"));
174}
175
176static WTF::String styleDecToStr(WKBundleCSSStyleDeclarationRef style)
177{
178 // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
179 // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
180 // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
181 StringBuilder stringBuilder;
182 stringBuilder.appendLiteral("<DOMCSSStyleDeclaration ADDRESS>");
183 return stringBuilder.toString();
184}
185
186static WTF::String securityOriginToStr(WKSecurityOriginRef origin)
187{
188 StringBuilder stringBuilder;
189 stringBuilder.append('{');
190 stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyProtocol(origin))));
191 stringBuilder.appendLiteral(", ");
192 stringBuilder.append(toWTFString(adoptWK(WKSecurityOriginCopyHost(origin))));
193 stringBuilder.appendLiteral(", ");
194 stringBuilder.appendNumber(WKSecurityOriginGetPort(origin));
195 stringBuilder.append('}');
196
197 return stringBuilder.toString();
198}
199
200static WTF::String frameToStr(WKBundleFrameRef frame)
201{
202 WKRetainPtr<WKStringRef> name = adoptWK(WKBundleFrameCopyName(frame));
203 StringBuilder stringBuilder;
204 if (WKBundleFrameIsMainFrame(frame)) {
205 if (!WKStringIsEmpty(name.get())) {
206 stringBuilder.appendLiteral("main frame \"");
207 stringBuilder.append(toWTFString(name));
208 stringBuilder.append('"');
209 } else
210 stringBuilder.appendLiteral("main frame");
211 } else {
212 if (!WKStringIsEmpty(name.get())) {
213 stringBuilder.appendLiteral("frame \"");
214 stringBuilder.append(toWTFString(name));
215 stringBuilder.append('"');
216 }
217 else
218 stringBuilder.appendLiteral("frame (anonymous)");
219 }
220
221 return stringBuilder.toString();
222}
223
224static inline bool isLocalFileScheme(WKStringRef scheme)
225{
226 return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "file");
227}
228
229static const char divider = '/';
230
231static inline WTF::String pathSuitableForTestResult(WKURLRef fileUrl)
232{
233 if (!fileUrl)
234 return "(null)";
235
236 WKRetainPtr<WKStringRef> schemeString = adoptWK(WKURLCopyScheme(fileUrl));
237 if (!isLocalFileScheme(schemeString.get()))
238 return toWTFString(adoptWK(WKURLCopyString(fileUrl)));
239
240 WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::singleton().page()->page());
241 WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
242 if (!mainFrameURL)
243 mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
244
245 String pathString = toWTFString(adoptWK(WKURLCopyPath(fileUrl)));
246 String mainFrameURLPathString = toWTFString(adoptWK(WKURLCopyPath(mainFrameURL.get())));
247 String basePath = mainFrameURLPathString.substring(0, mainFrameURLPathString.reverseFind(divider) + 1);
248
249 if (!basePath.isEmpty() && pathString.startsWith(basePath))
250 return pathString.substring(basePath.length());
251 return toWTFString(adoptWK(WKURLCopyLastPathComponent(fileUrl))); // We lose some information here, but it's better than exposing a full path, which is always machine specific.
252}
253
254static HashMap<uint64_t, String>& assignedUrlsCache()
255{
256 static NeverDestroyed<HashMap<uint64_t, String>> cache;
257 return cache.get();
258}
259
260static inline void dumpResourceURL(uint64_t identifier, StringBuilder& stringBuilder)
261{
262 if (assignedUrlsCache().contains(identifier))
263 stringBuilder.append(assignedUrlsCache().get(identifier));
264 else
265 stringBuilder.appendLiteral("<unknown>");
266}
267
268InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
269 : m_page(page)
270 , m_world(adoptWK(WKBundleScriptWorldCreateWorld()))
271{
272 WKBundlePageLoaderClientV9 loaderClient = {
273 { 9, this },
274 didStartProvisionalLoadForFrame,
275 didReceiveServerRedirectForProvisionalLoadForFrame,
276 didFailProvisionalLoadWithErrorForFrame,
277 didCommitLoadForFrame,
278 didFinishDocumentLoadForFrame,
279 didFinishLoadForFrame,
280 didFailLoadWithErrorForFrame,
281 didSameDocumentNavigationForFrame,
282 didReceiveTitleForFrame,
283 0, // didFirstLayoutForFrame
284 0, // didFirstVisuallyNonEmptyLayoutForFrame
285 0, // didRemoveFrameFromHierarchy
286 didDisplayInsecureContentForFrame,
287 didRunInsecureContentForFrame,
288 didClearWindowForFrame,
289 didCancelClientRedirectForFrame,
290 willPerformClientRedirectForFrame,
291 didHandleOnloadEventsForFrame,
292 0, // didLayoutForFrame
293 0, // didNewFirstVisuallyNonEmptyLayout_unavailable
294 didDetectXSSForFrame,
295 0, // shouldGoToBackForwardListItem
296 0, // didCreateGlobalObjectForFrame
297 0, // willDisconnectDOMWindowExtensionFromGlobalObject
298 0, // didReconnectDOMWindowExtensionToGlobalObject
299 0, // willDestroyGlobalObjectForDOMWindowExtension
300 didFinishProgress, // didFinishProgress
301 0, // shouldForceUniversalAccessFromLocalURL
302 0, // didReceiveIntentForFrame
303 0, // registerIntentServiceForFrame
304 0, // didLayout
305 0, // featuresUsedInPage
306 0, // willLoadURLRequest
307 0, // willLoadDataRequest
308 0, // willDestroyFrame_unavailable
309 0, // userAgentForURL
310 willInjectUserScriptForFrame
311 };
312 WKBundlePageSetPageLoaderClient(m_page, &loaderClient.base);
313
314 WKBundlePageResourceLoadClientV1 resourceLoadClient = {
315 { 1, this },
316 didInitiateLoadForResource,
317 willSendRequestForFrame,
318 didReceiveResponseForResource,
319 didReceiveContentLengthForResource,
320 didFinishLoadForResource,
321 didFailLoadForResource,
322 shouldCacheResponse,
323 0 // shouldUseCredentialStorage
324 };
325 WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient.base);
326
327 WKBundlePagePolicyClientV0 policyClient = {
328 { 0, this },
329 decidePolicyForNavigationAction,
330 decidePolicyForNewWindowAction,
331 decidePolicyForResponse,
332 unableToImplementPolicy
333 };
334 WKBundlePageSetPolicyClient(m_page, &policyClient.base);
335
336 WKBundlePageUIClientV2 uiClient = {
337 { 2, this },
338 willAddMessageToConsole,
339 willSetStatusbarText,
340 willRunJavaScriptAlert,
341 willRunJavaScriptConfirm,
342 willRunJavaScriptPrompt,
343 0, /*mouseDidMoveOverElement*/
344 0, /*pageDidScroll*/
345 0, /*paintCustomOverhangArea*/
346 0, /*shouldGenerateFileForUpload*/
347 0, /*generateFileForUpload*/
348 0, /*shouldRubberBandInDirection*/
349 0, /*statusBarIsVisible*/
350 0, /*menuBarIsVisible*/
351 0, /*toolbarsAreVisible*/
352 didReachApplicationCacheOriginQuota,
353 didExceedDatabaseQuota,
354 0, /*plugInStartLabelTitle*/
355 0, /*plugInStartLabelSubtitle*/
356 0, /*plugInExtraStyleSheet*/
357 0, /*plugInExtraScript*/
358 };
359 WKBundlePageSetUIClient(m_page, &uiClient.base);
360
361 WKBundlePageEditorClientV1 editorClient = {
362 { 1, this },
363 shouldBeginEditing,
364 shouldEndEditing,
365 shouldInsertNode,
366 shouldInsertText,
367 shouldDeleteRange,
368 shouldChangeSelectedRange,
369 shouldApplyStyle,
370 didBeginEditing,
371 didEndEditing,
372 didChange,
373 didChangeSelection,
374 0, /* willWriteToPasteboard */
375 0, /* getPasteboardDataForRange */
376 0, /* didWriteToPasteboard */
377 0, /* performTwoStepDrop */
378 };
379 WKBundlePageSetEditorClient(m_page, &editorClient.base);
380
381#if ENABLE(FULLSCREEN_API)
382 WKBundlePageFullScreenClientV1 fullScreenClient = {
383 { 1, this },
384 supportsFullScreen,
385 enterFullScreenForElement,
386 exitFullScreenForElement,
387 beganEnterFullScreen,
388 beganExitFullScreen,
389 closeFullScreen,
390 };
391 WKBundlePageSetFullScreenClient(m_page, &fullScreenClient.base);
392#endif
393}
394
395InjectedBundlePage::~InjectedBundlePage()
396{
397}
398
399void InjectedBundlePage::stopLoading()
400{
401 WKBundlePageStopLoading(m_page);
402}
403
404void InjectedBundlePage::prepare()
405{
406 WKBundlePageClearMainFrameName(m_page);
407
408 WKBundlePageSetPageZoomFactor(m_page, 1);
409 WKBundlePageSetTextZoomFactor(m_page, 1);
410
411 WKPoint origin = { 0, 0 };
412 WKBundlePageSetScaleAtOrigin(m_page, 1, origin);
413
414 WKBundleClearHistoryForTesting(m_page);
415
416 WKBundleFrameClearOpener(WKBundlePageGetMainFrame(m_page));
417
418 WKBundlePageSetTracksRepaints(m_page, false);
419
420 // Force consistent "responsive" behavior for WebPage::eventThrottlingDelay() for testing. Tests can override via internals.
421 WKEventThrottlingBehavior behavior = kWKEventThrottlingBehaviorResponsive;
422 WKBundlePageSetEventThrottlingBehaviorOverride(m_page, &behavior);
423}
424
425void InjectedBundlePage::resetAfterTest()
426{
427 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
428
429 // WebKit currently doesn't reset focus even when navigating to a new page. This may or may not be a bug
430 // (see <https://bugs.webkit.org/show_bug.cgi?id=138334>), however for tests, we want to start each one with a clean state.
431 WKBundleFrameFocus(frame);
432
433 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
434 WebCoreTestSupport::resetInternalsObject(context);
435 assignedUrlsCache().clear();
436
437 // 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.
438 WKBundlePageRemoveAllUserContent(m_page);
439
440 uninstallFakeHelvetica();
441
442 InjectedBundle::singleton().resetUserScriptInjectedCount();
443}
444
445// Loader Client Callbacks
446
447// String output must be identical to -[WebFrame _drt_descriptionSuitableForTestResult].
448static void dumpFrameDescriptionSuitableForTestResult(WKBundleFrameRef frame, StringBuilder& stringBuilder)
449{
450 WKRetainPtr<WKStringRef> name = adoptWK(WKBundleFrameCopyName(frame));
451 if (WKBundleFrameIsMainFrame(frame)) {
452 if (WKStringIsEmpty(name.get())) {
453 stringBuilder.appendLiteral("main frame");
454 return;
455 }
456
457 stringBuilder.appendLiteral("main frame \"");
458 stringBuilder.append(toWTFString(name));
459 stringBuilder.append('"');
460 return;
461 }
462
463 if (WKStringIsEmpty(name.get())) {
464 stringBuilder.appendLiteral("frame (anonymous)");
465 return;
466 }
467
468 stringBuilder.appendLiteral("frame \"");
469 stringBuilder.append(toWTFString(name));
470 stringBuilder.append('"');
471}
472
473static void dumpLoadEvent(WKBundleFrameRef frame, const char* eventName)
474{
475 StringBuilder stringBuilder;
476 dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
477 stringBuilder.appendLiteral(" - ");
478 stringBuilder.append(eventName);
479 stringBuilder.append('\n');
480 InjectedBundle::singleton().outputText(stringBuilder.toString());
481}
482
483static inline void dumpRequestDescriptionSuitableForTestResult(WKURLRequestRef request, StringBuilder& stringBuilder)
484{
485 WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
486 WKRetainPtr<WKURLRef> firstParty = adoptWK(WKURLRequestCopyFirstPartyForCookies(request));
487 WKRetainPtr<WKStringRef> httpMethod = adoptWK(WKURLRequestCopyHTTPMethod(request));
488
489 stringBuilder.appendLiteral("<NSURLRequest URL ");
490 stringBuilder.append(pathSuitableForTestResult(url.get()));
491 stringBuilder.appendLiteral(", main document URL ");
492 stringBuilder.append(pathSuitableForTestResult(firstParty.get()));
493 stringBuilder.appendLiteral(", http method ");
494
495 if (WKStringIsEmpty(httpMethod.get()))
496 stringBuilder.appendLiteral("(none)");
497 else
498 stringBuilder.append(toWTFString(httpMethod));
499
500 stringBuilder.append('>');
501}
502
503static inline void dumpResponseDescriptionSuitableForTestResult(WKURLResponseRef response, StringBuilder& stringBuilder, bool shouldDumpResponseHeaders = false)
504{
505 WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
506 if (!url) {
507 stringBuilder.appendLiteral("(null)");
508 return;
509 }
510 stringBuilder.appendLiteral("<NSURLResponse ");
511 stringBuilder.append(pathSuitableForTestResult(url.get()));
512 stringBuilder.appendLiteral(", http status code ");
513 stringBuilder.appendNumber(WKURLResponseHTTPStatusCode(response));
514
515 if (shouldDumpResponseHeaders) {
516 stringBuilder.appendLiteral(", ");
517 stringBuilder.appendNumber(InjectedBundlePage::responseHeaderCount(response));
518 stringBuilder.appendLiteral(" headers");
519 }
520 stringBuilder.append('>');
521}
522
523#if !PLATFORM(COCOA)
524// FIXME: Implement this for non cocoa ports.
525// [GTK][WPE] https://bugs.webkit.org/show_bug.cgi?id=184295
526uint64_t InjectedBundlePage::responseHeaderCount(WKURLResponseRef response)
527{
528 return 0;
529}
530#endif
531
532static inline void dumpErrorDescriptionSuitableForTestResult(WKErrorRef error, StringBuilder& stringBuilder)
533{
534 WKRetainPtr<WKStringRef> errorDomain = adoptWK(WKErrorCopyDomain(error));
535 int errorCode = WKErrorGetErrorCode(error);
536
537 // We need to do some error mapping here to match the test expectations (Mac error names are expected).
538 if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitNetworkError")) {
539 errorDomain = adoptWK(WKStringCreateWithUTF8CString("NSURLErrorDomain"));
540 errorCode = -999;
541 }
542
543 if (WKStringIsEqualToUTF8CString(errorDomain.get(), "WebKitPolicyError"))
544 errorDomain = adoptWK(WKStringCreateWithUTF8CString("WebKitErrorDomain"));
545
546 stringBuilder.appendLiteral("<NSError domain ");
547 stringBuilder.append(toWTFString(errorDomain));
548 stringBuilder.appendLiteral(", code ");
549 stringBuilder.appendNumber(errorCode);
550
551 WKRetainPtr<WKURLRef> url = adoptWK(WKErrorCopyFailingURL(error));
552 if (url.get()) {
553 WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
554 stringBuilder.appendLiteral(", failing URL \"");
555 stringBuilder.append(toWTFString(urlString));
556 stringBuilder.append('"');
557 }
558
559 stringBuilder.append('>');
560}
561
562void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
563{
564 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
565}
566
567void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
568{
569 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
570}
571
572void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
573{
574 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
575}
576
577void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
578{
579 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
580}
581
582void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
583{
584 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
585}
586
587void InjectedBundlePage::didFinishProgress(WKBundlePageRef, const void *clientInfo)
588{
589 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishProgress();
590}
591
592void InjectedBundlePage::willInjectUserScriptForFrame(WKBundlePageRef, WKBundleFrameRef, WKBundleScriptWorldRef, const void* clientInfo)
593{
594 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willInjectUserScriptForFrame();
595}
596
597void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
598{
599 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
600}
601
602void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
603{
604 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
605}
606
607void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
608{
609 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
610}
611
612void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
613{
614 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
615}
616
617void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
618{
619 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
620}
621
622void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
623{
624 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(page, frame, url, delay, date);
625}
626
627void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
628{
629 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
630}
631
632void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
633{
634 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
635}
636
637void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
638{
639 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
640}
641
642void InjectedBundlePage::didDetectXSSForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
643{
644 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDetectXSSForFrame(frame);
645}
646
647void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
648{
649 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
650}
651
652void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
653{
654 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
655}
656
657WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
658{
659 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
660}
661
662void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
663{
664 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
665}
666
667void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
668{
669 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
670}
671
672void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
673{
674 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
675}
676
677void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
678{
679 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadForResource(page, frame, identifier, error);
680}
681
682bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
683{
684 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldCacheResponse(page, frame, identifier);
685}
686
687void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
688{
689 auto& injectedBundle = InjectedBundle::singleton();
690 if (!injectedBundle.isTestRunning())
691 return;
692
693 if (!injectedBundle.testRunner()->testURL()) {
694 WKRetainPtr<WKURLRef> testURL = adoptWK(WKBundleFrameCopyProvisionalURL(frame));
695 injectedBundle.testRunner()->setTestURL(testURL.get());
696 }
697
698 platformDidStartProvisionalLoadForFrame(frame);
699
700 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
701 dumpLoadEvent(frame, "didStartProvisionalLoadForFrame");
702
703 if (!injectedBundle.topLoadingFrame())
704 injectedBundle.setTopLoadingFrame(frame);
705
706 if (injectedBundle.testRunner()->shouldStopProvisionalFrameLoads())
707 dumpLoadEvent(frame, "stopping load in didStartProvisionalLoadForFrame callback");
708}
709
710void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
711{
712 auto& injectedBundle = InjectedBundle::singleton();
713 if (!injectedBundle.isTestRunning())
714 return;
715
716 if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
717 return;
718
719 dumpLoadEvent(frame, "didReceiveServerRedirectForProvisionalLoadForFrame");
720}
721
722void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
723{
724 auto& injectedBundle = InjectedBundle::singleton();
725 if (!injectedBundle.isTestRunning())
726 return;
727
728 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
729 dumpLoadEvent(frame, "didFailProvisionalLoadWithError");
730 auto code = WKErrorGetErrorCode(error);
731 if (code == kWKErrorCodeCannotShowURL)
732 dumpLoadEvent(frame, "(ErrorCodeCannotShowURL)");
733 else if (code == kWKErrorCodeFrameLoadBlockedByContentBlocker)
734 dumpLoadEvent(frame, "(kWKErrorCodeFrameLoadBlockedByContentBlocker)");
735 }
736
737 frameDidChangeLocation(frame);
738}
739
740void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
741{
742 auto& injectedBundle = InjectedBundle::singleton();
743 if (!injectedBundle.isTestRunning())
744 return;
745
746 if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
747 return;
748
749 dumpLoadEvent(frame, "didCommitLoadForFrame");
750}
751
752void InjectedBundlePage::didFinishProgress()
753{
754 auto& injectedBundle = InjectedBundle::singleton();
755 if (!injectedBundle.isTestRunning())
756 return;
757
758 if (!injectedBundle.testRunner()->shouldDumpProgressFinishedCallback())
759 return;
760
761 injectedBundle.outputText("postProgressFinishedNotification\n");
762}
763
764void InjectedBundlePage::willInjectUserScriptForFrame()
765{
766 InjectedBundle::singleton().increaseUserScriptInjectedCount();
767}
768
769enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
770
771static void dumpFrameScrollPosition(WKBundleFrameRef frame, StringBuilder& stringBuilder, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
772{
773 double x = numericWindowPropertyValue(frame, "pageXOffset");
774 double y = numericWindowPropertyValue(frame, "pageYOffset");
775 if (fabs(x) <= 0.00000001 && fabs(y) <= 0.00000001)
776 return;
777
778 if (shouldIncludeFrameName) {
779 WKRetainPtr<WKStringRef> name = adoptWK(WKBundleFrameCopyName(frame));
780 stringBuilder.appendLiteral("frame '");
781 stringBuilder.append(toWTFString(name));
782 stringBuilder.appendLiteral("' ");
783 }
784 stringBuilder.appendLiteral("scrolled to ");
785 stringBuilder.appendECMAScriptNumber(x);
786 stringBuilder.append(',');
787 stringBuilder.appendECMAScriptNumber(y);
788 stringBuilder.append('\n');
789}
790
791static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame, StringBuilder& stringBuilder)
792{
793 WKRetainPtr<WKArrayRef> childFrames = adoptWK(WKBundleFrameCopyChildFrames(frame));
794 size_t size = WKArrayGetSize(childFrames.get());
795 for (size_t i = 0; i < size; ++i) {
796 WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
797 dumpFrameScrollPosition(subframe, stringBuilder, ShouldIncludeFrameName);
798 dumpDescendantFrameScrollPositions(subframe, stringBuilder);
799 }
800}
801
802void InjectedBundlePage::dumpAllFrameScrollPositions(StringBuilder& stringBuilder)
803{
804 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
805 dumpFrameScrollPosition(frame, stringBuilder);
806 dumpDescendantFrameScrollPositions(frame, stringBuilder);
807}
808
809static JSRetainPtr<JSStringRef> toJS(const char* string)
810{
811 return adopt(JSStringCreateWithUTF8CString(string));
812}
813
814static bool hasDocumentElement(WKBundleFrameRef frame)
815{
816 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
817 JSObjectRef globalObject = JSContextGetGlobalObject(context);
818
819 JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
820 if (!documentValue)
821 return false;
822
823 ASSERT(JSValueIsObject(context, documentValue));
824 JSObjectRef document = JSValueToObject(context, documentValue, 0);
825
826 JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
827 if (!documentElementValue)
828 return false;
829
830 return JSValueToBoolean(context, documentElementValue);
831}
832
833static void dumpFrameText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
834{
835 // If the frame doesn't have a document element, its inner text will be an empty string, so
836 // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
837 // anything in this case, so we shouldn't either.
838 if (!hasDocumentElement(frame))
839 return;
840
841 WKRetainPtr<WKStringRef> text = adoptWK(WKBundleFrameCopyInnerText(frame));
842 stringBuilder.append(toWTFString(text));
843 stringBuilder.append('\n');
844}
845
846static void dumpDescendantFramesText(WKBundleFrameRef frame, StringBuilder& stringBuilder)
847{
848 WKRetainPtr<WKArrayRef> childFrames = adoptWK(WKBundleFrameCopyChildFrames(frame));
849 size_t size = WKArrayGetSize(childFrames.get());
850 for (size_t i = 0; i < size; ++i) {
851 WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
852 WKRetainPtr<WKStringRef> subframeName = adoptWK(WKBundleFrameCopyName(subframe));
853
854 // DumpRenderTree ignores empty frames, so do the same thing here.
855 if (!hasDocumentElement(subframe))
856 continue;
857
858 stringBuilder.appendLiteral("\n--------\nFrame: '");
859 stringBuilder.append(toWTFString(subframeName));
860 stringBuilder.appendLiteral("'\n--------\n");
861
862 dumpFrameText(subframe, stringBuilder);
863 dumpDescendantFramesText(subframe, stringBuilder);
864 }
865}
866
867void InjectedBundlePage::dumpAllFramesText(StringBuilder& stringBuilder)
868{
869 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
870 dumpFrameText(frame, stringBuilder);
871 dumpDescendantFramesText(frame, stringBuilder);
872}
873
874
875void InjectedBundlePage::dumpDOMAsWebArchive(WKBundleFrameRef frame, StringBuilder& stringBuilder)
876{
877#if USE(CF) && !PLATFORM(WIN_CAIRO)
878 WKRetainPtr<WKDataRef> wkData = adoptWK(WKBundleFrameCopyWebArchive(frame));
879 RetainPtr<CFDataRef> cfData = adoptCF(CFDataCreate(0, WKDataGetBytes(wkData.get()), WKDataGetSize(wkData.get())));
880 RetainPtr<CFStringRef> cfString = adoptCF(WebCoreTestSupport::createXMLStringFromWebArchiveData(cfData.get()));
881 stringBuilder.append(cfString.get());
882#endif
883}
884
885void InjectedBundlePage::dump()
886{
887 auto& injectedBundle = InjectedBundle::singleton();
888 ASSERT(injectedBundle.isTestRunning());
889
890 // Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac
891 // does this at a slightly different time.) See <http://webkit.org/b/55469> for details.
892 WKBundlePageForceRepaint(m_page);
893
894 WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
895 WKRetainPtr<WKURLRef> urlRef = adoptWK(WKBundleFrameCopyURL(frame));
896 String url = toWTFString(adoptWK(WKURLCopyString(urlRef.get())));
897 WKRetainPtr<WKStringRef> mimeType = adoptWK(WKBundleFrameCopyMIMETypeForResourceWithURL(frame, urlRef.get()));
898 if (url.find("dumpAsText/") != notFound || WKStringIsEqualToUTF8CString(mimeType.get(), "text/plain"))
899 injectedBundle.testRunner()->dumpAsText(false);
900
901 StringBuilder stringBuilder;
902
903 switch (injectedBundle.testRunner()->whatToDump()) {
904 case WhatToDump::RenderTree: {
905 if (injectedBundle.testRunner()->isPrinting())
906 stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentationForPrinting(m_page)).get()));
907 else
908 stringBuilder.append(toWTFString(adoptWK(WKBundlePageCopyRenderTreeExternalRepresentation(m_page, injectedBundle.testRunner()->renderTreeDumpOptions())).get()));
909 break;
910 }
911 case WhatToDump::MainFrameText:
912 dumpFrameText(WKBundlePageGetMainFrame(m_page), stringBuilder);
913 break;
914 case WhatToDump::AllFramesText:
915 dumpAllFramesText(stringBuilder);
916 break;
917 case WhatToDump::Audio:
918 break;
919 case WhatToDump::DOMAsWebArchive:
920 dumpDOMAsWebArchive(frame, stringBuilder);
921 break;
922 }
923
924 if (injectedBundle.testRunner()->shouldDumpAllFrameScrollPositions())
925 dumpAllFrameScrollPositions(stringBuilder);
926 else if (injectedBundle.testRunner()->shouldDumpMainFrameScrollPosition())
927 dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page), stringBuilder);
928
929 if (injectedBundle.testRunner()->shouldDumpBackForwardListsForAllWindows())
930 injectedBundle.dumpBackForwardListsForAllPages(stringBuilder);
931
932 if (injectedBundle.shouldDumpPixels() && injectedBundle.testRunner()->shouldDumpPixels()) {
933 bool shouldCreateSnapshot = injectedBundle.testRunner()->isPrinting();
934 if (shouldCreateSnapshot) {
935 WKSnapshotOptions options = kWKSnapshotOptionsShareable;
936 WKRect snapshotRect = WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page));
937
938 if (injectedBundle.testRunner()->isPrinting())
939 options |= kWKSnapshotOptionsPrinting;
940 else {
941 options |= kWKSnapshotOptionsInViewCoordinates;
942 if (injectedBundle.testRunner()->shouldDumpSelectionRect())
943 options |= kWKSnapshotOptionsPaintSelectionRectangle;
944 }
945
946 injectedBundle.setPixelResult(adoptWK(WKBundlePageCreateSnapshotWithOptions(m_page, snapshotRect, options)).get());
947 } else
948 injectedBundle.setPixelResultIsPending(true);
949
950 if (WKBundlePageIsTrackingRepaints(m_page) && !injectedBundle.testRunner()->isPrinting())
951 injectedBundle.setRepaintRects(adoptWK(WKBundlePageCopyTrackedRepaintRects(m_page)).get());
952 }
953
954 injectedBundle.outputText(stringBuilder.toString());
955 injectedBundle.done();
956}
957
958void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
959{
960 auto& injectedBundle = InjectedBundle::singleton();
961 if (!injectedBundle.isTestRunning())
962 return;
963
964 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
965 dumpLoadEvent(frame, "didFinishLoadForFrame");
966
967 frameDidChangeLocation(frame);
968}
969
970void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
971{
972 auto& injectedBundle = InjectedBundle::singleton();
973 if (!injectedBundle.isTestRunning())
974 return;
975
976 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
977 dumpLoadEvent(frame, "didFailLoadWithError");
978
979 frameDidChangeLocation(frame);
980}
981
982void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
983{
984 auto& injectedBundle = InjectedBundle::singleton();
985 if (!injectedBundle.isTestRunning())
986 return;
987
988 StringBuilder stringBuilder;
989 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks()) {
990 dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
991 stringBuilder.appendLiteral(" - didReceiveTitle: ");
992 stringBuilder.append(toWTFString(title));
993 stringBuilder.append('\n');
994 }
995
996 if (injectedBundle.testRunner()->shouldDumpTitleChanges()) {
997 stringBuilder.appendLiteral("TITLE CHANGED: '");
998 stringBuilder.append(toWTFString(title));
999 stringBuilder.appendLiteral("'\n");
1000 }
1001
1002 injectedBundle.outputText(stringBuilder.toString());
1003}
1004
1005void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
1006{
1007 auto& injectedBundle = InjectedBundle::singleton();
1008 if (!injectedBundle.isTestRunning())
1009 return;
1010
1011 JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
1012 JSObjectRef window = JSContextGetGlobalObject(context);
1013
1014 if (WKBundleScriptWorldNormalWorld() != world) {
1015 JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, TestRunner::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
1016 return;
1017 }
1018
1019 JSValueRef exception = nullptr;
1020 injectedBundle.testRunner()->makeWindowObject(context, window, &exception);
1021 injectedBundle.gcController()->makeWindowObject(context, window, &exception);
1022 injectedBundle.eventSendingController()->makeWindowObject(context, window, &exception);
1023 injectedBundle.textInputController()->makeWindowObject(context, window, &exception);
1024#if HAVE(ACCESSIBILITY)
1025 injectedBundle.accessibilityController()->makeWindowObject(context, window, &exception);
1026#endif
1027
1028 WebCoreTestSupport::injectInternalsObject(context);
1029}
1030
1031void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
1032{
1033 auto& injectedBundle = InjectedBundle::singleton();
1034 if (!injectedBundle.isTestRunning())
1035 return;
1036
1037 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1038 dumpLoadEvent(frame, "didCancelClientRedirectForFrame");
1039
1040 injectedBundle.testRunner()->setDidCancelClientRedirect(true);
1041}
1042
1043void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef, WKBundleFrameRef frame, WKURLRef url, double delay, double date)
1044{
1045 auto& injectedBundle = InjectedBundle::singleton();
1046 if (!injectedBundle.isTestRunning())
1047 return;
1048
1049 if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1050 return;
1051
1052 StringBuilder stringBuilder;
1053 dumpFrameDescriptionSuitableForTestResult(frame, stringBuilder);
1054 stringBuilder.appendLiteral(" - willPerformClientRedirectToURL: ");
1055 stringBuilder.append(pathSuitableForTestResult(url));
1056 stringBuilder.appendLiteral(" \n");
1057 injectedBundle.outputText(stringBuilder.toString());
1058}
1059
1060void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
1061{
1062 auto& injectedBundle = InjectedBundle::singleton();
1063 if (!injectedBundle.isTestRunning())
1064 return;
1065
1066 if (!injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1067 return;
1068
1069 if (type != kWKSameDocumentNavigationAnchorNavigation)
1070 return;
1071
1072 dumpLoadEvent(frame, "didChangeLocationWithinPageForFrame");
1073}
1074
1075void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
1076{
1077 auto& injectedBundle = InjectedBundle::singleton();
1078 if (!injectedBundle.isTestRunning())
1079 return;
1080
1081 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1082 dumpLoadEvent(frame, "didFinishDocumentLoadForFrame");
1083
1084 unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
1085 if (pendingFrameUnloadEvents) {
1086 StringBuilder stringBuilder;
1087 stringBuilder.append(frameToStr(frame));
1088 stringBuilder.appendLiteral(" - has ");
1089 stringBuilder.appendNumber(pendingFrameUnloadEvents);
1090 stringBuilder.appendLiteral(" onunload handler(s)\n");
1091 injectedBundle.outputText(stringBuilder.toString());
1092 }
1093}
1094
1095void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
1096{
1097 auto& injectedBundle = InjectedBundle::singleton();
1098 if (!injectedBundle.isTestRunning())
1099 return;
1100
1101 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1102 dumpLoadEvent(frame, "didHandleOnloadEventsForFrame");
1103}
1104
1105void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef)
1106{
1107 auto& injectedBundle = InjectedBundle::singleton();
1108 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1109 injectedBundle.outputText("didDisplayInsecureContent\n");
1110}
1111
1112void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef)
1113{
1114 auto& injectedBundle = InjectedBundle::singleton();
1115 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1116 injectedBundle.outputText("didRunInsecureContent\n");
1117}
1118
1119void InjectedBundlePage::didDetectXSSForFrame(WKBundleFrameRef)
1120{
1121 auto& injectedBundle = InjectedBundle::singleton();
1122 if (injectedBundle.testRunner()->shouldDumpFrameLoadCallbacks())
1123 injectedBundle.outputText("didDetectXSS\n");
1124}
1125
1126void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef request, bool)
1127{
1128 if (!InjectedBundle::singleton().isTestRunning())
1129 return;
1130
1131 WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1132 assignedUrlsCache().add(identifier, pathSuitableForTestResult(url.get()));
1133}
1134
1135// Resource Load Client Callbacks
1136
1137static inline bool isLocalHost(WKStringRef host)
1138{
1139 return WKStringIsEqualToUTF8CString(host, "127.0.0.1") || WKStringIsEqualToUTF8CString(host, "localhost");
1140}
1141
1142static inline bool isHTTPOrHTTPSScheme(WKStringRef scheme)
1143{
1144 return WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme, "https");
1145}
1146
1147static inline bool isAllowedHost(WKStringRef host)
1148{
1149 return InjectedBundle::singleton().isAllowedHost(host);
1150}
1151
1152WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef response)
1153{
1154 auto& injectedBundle = InjectedBundle::singleton();
1155 if (injectedBundle.isTestRunning()
1156 && injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
1157 StringBuilder stringBuilder;
1158 dumpResourceURL(identifier, stringBuilder);
1159 stringBuilder.appendLiteral(" - willSendRequest ");
1160 dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
1161 stringBuilder.appendLiteral(" redirectResponse ");
1162 dumpResponseDescriptionSuitableForTestResult(response, stringBuilder, injectedBundle.testRunner()->shouldDumpAllHTTPRedirectedResponseHeaders());
1163 stringBuilder.append('\n');
1164 injectedBundle.outputText(stringBuilder.toString());
1165 }
1166
1167 if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNull())
1168 return nullptr;
1169
1170 WKRetainPtr<WKURLRef> redirectURL = adoptWK(WKURLResponseCopyURL(response));
1171 if (injectedBundle.isTestRunning() && injectedBundle.testRunner()->willSendRequestReturnsNullOnRedirect() && redirectURL) {
1172 injectedBundle.outputText("Returning null for this redirect\n");
1173 return nullptr;
1174 }
1175
1176 WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1177 WKRetainPtr<WKStringRef> host = adoptWK(WKURLCopyHostName(url.get()));
1178 WKRetainPtr<WKStringRef> scheme = adoptWK(WKURLCopyScheme(url.get()));
1179 WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
1180 if (host && !WKStringIsEmpty(host.get())
1181 && isHTTPOrHTTPSScheme(scheme.get())
1182 && !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error.
1183 && !isLocalHost(host.get())) {
1184 bool mainFrameIsExternal = false;
1185 if (injectedBundle.isTestRunning()) {
1186 WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(m_page);
1187 WKRetainPtr<WKURLRef> mainFrameURL = adoptWK(WKBundleFrameCopyURL(mainFrame));
1188 if (!mainFrameURL || WKStringIsEqualToUTF8CString(adoptWK(WKURLCopyString(mainFrameURL.get())).get(), "about:blank"))
1189 mainFrameURL = adoptWK(WKBundleFrameCopyProvisionalURL(mainFrame));
1190
1191 WKRetainPtr<WKStringRef> mainFrameHost = adoptWK(WKURLCopyHostName(mainFrameURL.get()));
1192 WKRetainPtr<WKStringRef> mainFrameScheme = adoptWK(WKURLCopyScheme(mainFrameURL.get()));
1193 mainFrameIsExternal = isHTTPOrHTTPSScheme(mainFrameScheme.get()) && !isLocalHost(mainFrameHost.get());
1194 }
1195 if (!mainFrameIsExternal && !isAllowedHost(host.get())) {
1196 StringBuilder stringBuilder;
1197 stringBuilder.appendLiteral("Blocked access to external URL ");
1198 stringBuilder.append(toWTFString(urlString));
1199 stringBuilder.append('\n');
1200 injectedBundle.outputText(stringBuilder.toString());
1201 return nullptr;
1202 }
1203 }
1204
1205 if (injectedBundle.isTestRunning()) {
1206 String body = injectedBundle.testRunner()->willSendRequestHTTPBody();
1207 if (!body.isEmpty()) {
1208 CString cBody = body.utf8();
1209 WKRetainPtr<WKDataRef> body = adoptWK(WKDataCreate(reinterpret_cast<const unsigned char*>(cBody.data()), cBody.length()));
1210 return WKURLRequestCopySettingHTTPBody(request, body.get());
1211 }
1212 }
1213
1214 WKRetain(request);
1215 return request;
1216}
1217
1218void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef, uint64_t identifier, WKURLResponseRef response)
1219{
1220 auto& injectedBundle = InjectedBundle::singleton();
1221 if (!injectedBundle.isTestRunning())
1222 return;
1223
1224 if (injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks()) {
1225 StringBuilder stringBuilder;
1226 dumpResourceURL(identifier, stringBuilder);
1227 stringBuilder.appendLiteral(" - didReceiveResponse ");
1228 dumpResponseDescriptionSuitableForTestResult(response, stringBuilder);
1229 stringBuilder.append('\n');
1230 injectedBundle.outputText(stringBuilder.toString());
1231 }
1232
1233
1234 if (!injectedBundle.testRunner()->shouldDumpResourceResponseMIMETypes())
1235 return;
1236
1237 WKRetainPtr<WKURLRef> url = adoptWK(WKURLResponseCopyURL(response));
1238 WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyLastPathComponent(url.get()));
1239 WKRetainPtr<WKStringRef> mimeTypeString = adoptWK(WKURLResponseCopyMIMEType(response));
1240
1241 StringBuilder stringBuilder;
1242 stringBuilder.append(toWTFString(urlString));
1243 stringBuilder.appendLiteral(" has MIME type ");
1244 stringBuilder.append(toWTFString(mimeTypeString));
1245
1246 String platformMimeType = platformResponseMimeType(response);
1247 if (!platformMimeType.isEmpty() && platformMimeType != toWTFString(mimeTypeString)) {
1248 stringBuilder.appendLiteral(" but platform response has ");
1249 stringBuilder.append(platformMimeType);
1250 }
1251
1252 stringBuilder.append('\n');
1253
1254 injectedBundle.outputText(stringBuilder.toString());
1255}
1256
1257void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
1258{
1259}
1260
1261void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
1262{
1263 auto& injectedBundle = InjectedBundle::singleton();
1264 if (!injectedBundle.isTestRunning())
1265 return;
1266
1267 if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
1268 return;
1269
1270 StringBuilder stringBuilder;
1271 dumpResourceURL(identifier, stringBuilder);
1272 stringBuilder.appendLiteral(" - didFinishLoading\n");
1273 injectedBundle.outputText(stringBuilder.toString());
1274}
1275
1276void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKErrorRef error)
1277{
1278 auto& injectedBundle = InjectedBundle::singleton();
1279 if (!injectedBundle.isTestRunning())
1280 return;
1281
1282 if (!injectedBundle.testRunner()->shouldDumpResourceLoadCallbacks())
1283 return;
1284
1285 StringBuilder stringBuilder;
1286 dumpResourceURL(identifier, stringBuilder);
1287 stringBuilder.appendLiteral(" - didFailLoadingWithError: ");
1288
1289 dumpErrorDescriptionSuitableForTestResult(error, stringBuilder);
1290 stringBuilder.append('\n');
1291 injectedBundle.outputText(stringBuilder.toString());
1292}
1293
1294bool InjectedBundlePage::shouldCacheResponse(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier)
1295{
1296 auto& injectedBundle = InjectedBundle::singleton();
1297 if (!injectedBundle.isTestRunning())
1298 return true;
1299
1300 if (!injectedBundle.testRunner()->shouldDumpWillCacheResponse())
1301 return true;
1302
1303 StringBuilder stringBuilder;
1304 stringBuilder.appendNumber(identifier);
1305 stringBuilder.appendLiteral(" - willCacheResponse: called\n");
1306 injectedBundle.outputText(stringBuilder.toString());
1307
1308 // The default behavior is the cache the response.
1309 return true;
1310}
1311
1312
1313// Policy Client Callbacks
1314
1315WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
1316{
1317 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(page, frame, navigationAction, request, userData);
1318}
1319
1320WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKStringRef frameName, WKTypeRef* userData, const void* clientInfo)
1321{
1322 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNewWindowAction(page, frame, navigationAction, request, frameName, userData);
1323}
1324
1325WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
1326{
1327 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(page, frame, response, request, userData);
1328}
1329
1330void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef* userData, const void* clientInfo)
1331{
1332 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->unableToImplementPolicy(page, frame, error, userData);
1333}
1334
1335WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData)
1336{
1337 auto& injectedBundle = InjectedBundle::singleton();
1338 if (!injectedBundle.isTestRunning())
1339 return WKBundlePagePolicyActionUse;
1340
1341 if (injectedBundle.testRunner()->shouldDumpPolicyCallbacks()) {
1342 StringBuilder stringBuilder;
1343 stringBuilder.appendLiteral(" - decidePolicyForNavigationAction \n");
1344 dumpRequestDescriptionSuitableForTestResult(request, stringBuilder);
1345 stringBuilder.appendLiteral(" is main frame - ");
1346 stringBuilder.append(WKBundleFrameIsMainFrame(frame) ? "yes" : "no");
1347 stringBuilder.appendLiteral(" should open URLs externally - ");
1348 stringBuilder.append(WKBundleNavigationActionGetShouldOpenExternalURLs(navigationAction) ? "yes" : "no");
1349 stringBuilder.append('\n');
1350 injectedBundle.outputText(stringBuilder.toString());
1351 }
1352
1353 if (!injectedBundle.testRunner()->isPolicyDelegateEnabled())
1354 return WKBundlePagePolicyActionPassThrough;
1355
1356 WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
1357 WKRetainPtr<WKStringRef> urlScheme = adoptWK(WKURLCopyScheme(url.get()));
1358
1359 StringBuilder stringBuilder;
1360 stringBuilder.appendLiteral("Policy delegate: attempt to load ");
1361 if (isLocalFileScheme(urlScheme.get())) {
1362 WKRetainPtr<WKStringRef> filename = adoptWK(WKURLCopyLastPathComponent(url.get()));
1363 stringBuilder.append(toWTFString(filename));
1364 } else {
1365 WKRetainPtr<WKStringRef> urlString = adoptWK(WKURLCopyString(url.get()));
1366 stringBuilder.append(toWTFString(urlString));
1367 }
1368 stringBuilder.appendLiteral(" with navigation type \'");
1369 stringBuilder.append(toWTFString(NavigationTypeToString(WKBundleNavigationActionGetNavigationType(navigationAction))));
1370 stringBuilder.appendLiteral("\'");
1371 WKRetainPtr<WKBundleHitTestResultRef> hitTestResultRef = adoptWK(WKBundleNavigationActionCopyHitTestResult(navigationAction));
1372 if (hitTestResultRef) {
1373 WKRetainPtr<WKBundleNodeHandleRef> nodeHandleRef = adoptWK(WKBundleHitTestResultCopyNodeHandle(hitTestResultRef.get()));
1374 stringBuilder.appendLiteral(" originating from ");
1375 stringBuilder.append(dumpPath(m_page, m_world.get(), nodeHandleRef.get()));
1376 }
1377
1378 stringBuilder.append('\n');
1379 injectedBundle.outputText(stringBuilder.toString());
1380
1381 injectedBundle.testRunner()->notifyDone();
1382
1383 return WKBundlePagePolicyActionPassThrough;
1384}
1385
1386WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef, WKTypeRef*)
1387{
1388 return WKBundlePagePolicyActionPassThrough;
1389}
1390
1391WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef, WKURLResponseRef response, WKURLRequestRef, WKTypeRef*)
1392{
1393 auto& injectedBundle = InjectedBundle::singleton();
1394 if (injectedBundle.testRunner() && injectedBundle.testRunner()->isPolicyDelegateEnabled() && WKURLResponseIsAttachment(response)) {
1395 StringBuilder stringBuilder;
1396 WKRetainPtr<WKStringRef> filename = adoptWK(WKURLResponseCopySuggestedFilename(response));
1397 stringBuilder.appendLiteral("Policy delegate: resource is an attachment, suggested file name \'");
1398 stringBuilder.append(toWTFString(filename));
1399 stringBuilder.appendLiteral("\'\n");
1400 InjectedBundle::singleton().outputText(stringBuilder.toString());
1401 }
1402
1403 return WKBundlePagePolicyActionPassThrough;
1404}
1405
1406void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*)
1407{
1408}
1409
1410// UI Client Callbacks
1411
1412void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
1413{
1414 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
1415}
1416
1417void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
1418{
1419 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
1420}
1421
1422void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
1423{
1424 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
1425}
1426
1427void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
1428{
1429 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
1430}
1431
1432void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
1433{
1434 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
1435}
1436
1437void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKBundlePageRef page, WKSecurityOriginRef origin, int64_t totalBytesNeeded, const void* clientInfo)
1438{
1439 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReachApplicationCacheOriginQuota(origin, totalBytesNeeded);
1440}
1441
1442uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKBundlePageRef page, WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes, const void* clientInfo)
1443{
1444 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didExceedDatabaseQuota(origin, databaseName, databaseDisplayName, currentQuotaBytes, currentOriginUsageBytes, currentDatabaseUsageBytes, expectedUsageBytes);
1445}
1446
1447static WTF::String lastFileURLPathComponent(const WTF::String& path)
1448{
1449 size_t pos = path.find("file://");
1450 ASSERT(WTF::notFound != pos);
1451
1452 WTF::String tmpPath = path.substring(pos + 7);
1453 if (tmpPath.length() < 2) // Keep the lone slash to avoid empty output.
1454 return tmpPath;
1455
1456 // Remove the trailing delimiter
1457 if (tmpPath[tmpPath.length() - 1] == '/')
1458 tmpPath.remove(tmpPath.length() - 1);
1459
1460 pos = tmpPath.reverseFind('/');
1461 if (WTF::notFound != pos)
1462 return tmpPath.substring(pos + 1);
1463
1464 return tmpPath;
1465}
1466
1467void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
1468{
1469 auto& injectedBundle = InjectedBundle::singleton();
1470 if (!injectedBundle.isTestRunning())
1471 return;
1472
1473 WTF::String messageString = toWTFString(message);
1474 size_t nullCharPos = messageString.find(UChar(0));
1475 if (nullCharPos != WTF::notFound)
1476 messageString.truncate(nullCharPos);
1477
1478 size_t fileProtocolStart = messageString.find("file://");
1479 if (fileProtocolStart != WTF::notFound)
1480 // FIXME: The code below does not handle additional text after url nor multiple urls. This matches DumpRenderTree implementation.
1481 messageString = messageString.substring(0, fileProtocolStart) + lastFileURLPathComponent(messageString.substring(fileProtocolStart));
1482
1483 StringBuilder stringBuilder;
1484 stringBuilder.appendLiteral("CONSOLE MESSAGE: ");
1485 if (lineNumber) {
1486 stringBuilder.appendLiteral("line ");
1487 stringBuilder.appendNumber(lineNumber);
1488 stringBuilder.appendLiteral(": ");
1489 }
1490 stringBuilder.append(messageString);
1491 stringBuilder.append('\n');
1492
1493 if (injectedBundle.dumpJSConsoleLogInStdErr())
1494 injectedBundle.dumpToStdErr(stringBuilder.toString());
1495 else
1496 injectedBundle.outputText(stringBuilder.toString());
1497}
1498
1499void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
1500{
1501 auto& injectedBundle = InjectedBundle::singleton();
1502 if (!injectedBundle.isTestRunning())
1503 return;
1504
1505 if (!injectedBundle.testRunner()->shouldDumpStatusCallbacks())
1506 return;
1507
1508 StringBuilder stringBuilder;
1509 stringBuilder.appendLiteral("UI DELEGATE STATUS CALLBACK: setStatusText:");
1510 stringBuilder.append(toWTFString(statusbarText));
1511 stringBuilder.append('\n');
1512 injectedBundle.outputText(stringBuilder.toString());
1513}
1514
1515void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
1516{
1517 auto& injectedBundle = InjectedBundle::singleton();
1518 if (!injectedBundle.isTestRunning())
1519 return;
1520
1521 StringBuilder stringBuilder;
1522 stringBuilder.appendLiteral("ALERT: ");
1523 stringBuilder.append(toWTFString(message));
1524 stringBuilder.append('\n');
1525 injectedBundle.outputText(stringBuilder.toString());
1526}
1527
1528void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
1529{
1530 auto& injectedBundle = InjectedBundle::singleton();
1531 if (!injectedBundle.isTestRunning())
1532 return;
1533
1534 StringBuilder stringBuilder;
1535 stringBuilder.appendLiteral("CONFIRM: ");
1536 stringBuilder.append(toWTFString(message));
1537 stringBuilder.append('\n');
1538 injectedBundle.outputText(stringBuilder.toString());
1539}
1540
1541void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
1542{
1543 StringBuilder stringBuilder;
1544 stringBuilder.appendLiteral("PROMPT: ");
1545 stringBuilder.append(toWTFString(message));
1546 stringBuilder.appendLiteral(", default text: ");
1547 stringBuilder.append(toWTFString(defaultValue));
1548 stringBuilder.append('\n');
1549 InjectedBundle::singleton().outputText(stringBuilder.toString());
1550}
1551
1552void InjectedBundlePage::didReachApplicationCacheOriginQuota(WKSecurityOriginRef origin, int64_t totalBytesNeeded)
1553{
1554 auto& injectedBundle = InjectedBundle::singleton();
1555 if (injectedBundle.testRunner()->shouldDumpApplicationCacheDelegateCallbacks()) {
1556 // For example, numbers from 30000 - 39999 will output as 30000.
1557 // Rounding up or down does not really matter for these tests. It's
1558 // sufficient to just get a range of 10000 to determine if we were
1559 // above or below a threshold.
1560 int64_t truncatedSpaceNeeded = (totalBytesNeeded / 10000) * 10000;
1561
1562 StringBuilder stringBuilder;
1563 stringBuilder.appendLiteral("UI DELEGATE APPLICATION CACHE CALLBACK: exceededApplicationCacheOriginQuotaForSecurityOrigin:");
1564 stringBuilder.append(securityOriginToStr(origin));
1565 stringBuilder.appendLiteral(" totalSpaceNeeded:~");
1566 stringBuilder.appendNumber(truncatedSpaceNeeded);
1567 stringBuilder.append('\n');
1568 injectedBundle.outputText(stringBuilder.toString());
1569 }
1570
1571 if (injectedBundle.testRunner()->shouldDisallowIncreaseForApplicationCacheQuota())
1572 return;
1573
1574 // Reset default application cache quota.
1575 WKBundlePageResetApplicationCacheOriginQuota(injectedBundle.page()->page(), adoptWK(WKSecurityOriginCopyToString(origin)).get());
1576}
1577
1578uint64_t InjectedBundlePage::didExceedDatabaseQuota(WKSecurityOriginRef origin, WKStringRef databaseName, WKStringRef databaseDisplayName, uint64_t currentQuotaBytes, uint64_t currentOriginUsageBytes, uint64_t currentDatabaseUsageBytes, uint64_t expectedUsageBytes)
1579{
1580 auto& injectedBundle = InjectedBundle::singleton();
1581 if (injectedBundle.testRunner()->shouldDumpDatabaseCallbacks()) {
1582 StringBuilder stringBuilder;
1583 stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: exceededDatabaseQuotaForSecurityOrigin:");
1584 stringBuilder.append(securityOriginToStr(origin));
1585 stringBuilder.appendLiteral(" database:");
1586 stringBuilder.append(toWTFString(databaseName));
1587 stringBuilder.append('\n');
1588 injectedBundle.outputText(stringBuilder.toString());
1589 }
1590
1591 uint64_t defaultQuota = 5 * 1024 * 1024;
1592 double testDefaultQuota = injectedBundle.testRunner()->databaseDefaultQuota();
1593 if (testDefaultQuota >= 0)
1594 defaultQuota = testDefaultQuota;
1595
1596 unsigned long long newQuota = defaultQuota;
1597
1598 double maxQuota = injectedBundle.testRunner()->databaseMaxQuota();
1599 if (maxQuota >= 0) {
1600 if (defaultQuota < expectedUsageBytes && expectedUsageBytes <= maxQuota) {
1601 newQuota = expectedUsageBytes;
1602
1603 StringBuilder stringBuilder;
1604 stringBuilder.appendLiteral("UI DELEGATE DATABASE CALLBACK: increased quota to ");
1605 stringBuilder.appendNumber(newQuota);
1606 stringBuilder.append('\n');
1607 injectedBundle.outputText(stringBuilder.toString());
1608 }
1609 }
1610 return newQuota;
1611}
1612
1613// Editor Client Callbacks
1614
1615bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1616{
1617 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
1618}
1619
1620bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1621{
1622 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
1623}
1624
1625bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
1626{
1627 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
1628}
1629
1630bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
1631{
1632 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
1633}
1634
1635bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
1636{
1637 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
1638}
1639
1640bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
1641{
1642 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
1643}
1644
1645bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
1646{
1647 return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
1648}
1649
1650void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1651{
1652 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
1653}
1654
1655void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1656{
1657 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
1658}
1659
1660void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1661{
1662 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
1663}
1664
1665void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
1666{
1667 static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
1668}
1669
1670bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
1671{
1672 auto& injectedBundle = InjectedBundle::singleton();
1673 if (!injectedBundle.isTestRunning())
1674 return true;
1675
1676 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1677 StringBuilder stringBuilder;
1678 stringBuilder.appendLiteral("EDITING DELEGATE: shouldBeginEditingInDOMRange:");
1679 stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1680 stringBuilder.append('\n');
1681 injectedBundle.outputText(stringBuilder.toString());
1682 }
1683 return injectedBundle.testRunner()->shouldAllowEditing();
1684}
1685
1686bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
1687{
1688 auto& injectedBundle = InjectedBundle::singleton();
1689 if (!injectedBundle.isTestRunning())
1690 return true;
1691
1692 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1693 StringBuilder stringBuilder;
1694 stringBuilder.appendLiteral("EDITING DELEGATE: shouldEndEditingInDOMRange:");
1695 stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1696 stringBuilder.append('\n');
1697 injectedBundle.outputText(stringBuilder.toString());
1698 }
1699 return injectedBundle.testRunner()->shouldAllowEditing();
1700}
1701
1702bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
1703{
1704 auto& injectedBundle = InjectedBundle::singleton();
1705 if (!injectedBundle.isTestRunning())
1706 return true;
1707
1708 static const char* insertactionstring[] = {
1709 "WebViewInsertActionTyped",
1710 "WebViewInsertActionPasted",
1711 "WebViewInsertActionDropped",
1712 };
1713
1714 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1715 StringBuilder stringBuilder;
1716 stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertNode:");
1717 stringBuilder.append(dumpPath(m_page, m_world.get(), node));
1718 stringBuilder.appendLiteral(" replacingDOMRange:");
1719 stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
1720 stringBuilder.appendLiteral(" givenAction:");
1721 stringBuilder.append(insertactionstring[action]);
1722 stringBuilder.append('\n');
1723 injectedBundle.outputText(stringBuilder.toString());
1724 }
1725 return injectedBundle.testRunner()->shouldAllowEditing();
1726}
1727
1728bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
1729{
1730 auto& injectedBundle = InjectedBundle::singleton();
1731 if (!injectedBundle.isTestRunning())
1732 return true;
1733
1734 static const char *insertactionstring[] = {
1735 "WebViewInsertActionTyped",
1736 "WebViewInsertActionPasted",
1737 "WebViewInsertActionDropped",
1738 };
1739
1740 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1741 StringBuilder stringBuilder;
1742 stringBuilder.appendLiteral("EDITING DELEGATE: shouldInsertText:");
1743 stringBuilder.append(toWTFString(text));
1744 stringBuilder.appendLiteral(" replacingDOMRange:");
1745 stringBuilder.append(rangeToStr(m_page, m_world.get(), rangeToReplace));
1746 stringBuilder.appendLiteral(" givenAction:");
1747 stringBuilder.append(insertactionstring[action]);
1748 stringBuilder.append('\n');
1749 injectedBundle.outputText(stringBuilder.toString());
1750 }
1751 return injectedBundle.testRunner()->shouldAllowEditing();
1752}
1753
1754bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
1755{
1756 auto& injectedBundle = InjectedBundle::singleton();
1757 if (!injectedBundle.isTestRunning())
1758 return true;
1759
1760 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1761 StringBuilder stringBuilder;
1762 stringBuilder.appendLiteral("EDITING DELEGATE: shouldDeleteDOMRange:");
1763 stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1764 stringBuilder.append('\n');
1765 injectedBundle.outputText(stringBuilder.toString());
1766 }
1767 return injectedBundle.testRunner()->shouldAllowEditing();
1768}
1769
1770bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
1771{
1772 auto& injectedBundle = InjectedBundle::singleton();
1773 if (!injectedBundle.isTestRunning())
1774 return true;
1775
1776 static const char *affinitystring[] = {
1777 "NSSelectionAffinityUpstream",
1778 "NSSelectionAffinityDownstream"
1779 };
1780 static const char *boolstring[] = {
1781 "FALSE",
1782 "TRUE"
1783 };
1784
1785 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1786 StringBuilder stringBuilder;
1787 stringBuilder.appendLiteral("EDITING DELEGATE: shouldChangeSelectedDOMRange:");
1788 stringBuilder.append(rangeToStr(m_page, m_world.get(), fromRange));
1789 stringBuilder.appendLiteral(" toDOMRange:");
1790 stringBuilder.append(rangeToStr(m_page, m_world.get(), toRange));
1791 stringBuilder.appendLiteral(" affinity:");
1792 stringBuilder.append(affinitystring[affinity]);
1793 stringBuilder.appendLiteral(" stillSelecting:");
1794 stringBuilder.append(boolstring[stillSelecting]);
1795 stringBuilder.append('\n');
1796 injectedBundle.outputText(stringBuilder.toString());
1797 }
1798 return injectedBundle.testRunner()->shouldAllowEditing();
1799}
1800
1801bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
1802{
1803 auto& injectedBundle = InjectedBundle::singleton();
1804 if (!injectedBundle.isTestRunning())
1805 return true;
1806
1807 if (injectedBundle.testRunner()->shouldDumpEditingCallbacks()) {
1808 StringBuilder stringBuilder;
1809 stringBuilder.appendLiteral("EDITING DELEGATE: shouldApplyStyle:");
1810 stringBuilder.append(styleDecToStr(style));
1811 stringBuilder.appendLiteral(" toElementsInDOMRange:");
1812 stringBuilder.append(rangeToStr(m_page, m_world.get(), range));
1813 stringBuilder.append('\n');
1814 injectedBundle.outputText(stringBuilder.toString());
1815 }
1816 return injectedBundle.testRunner()->shouldAllowEditing();
1817}
1818
1819void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
1820{
1821 auto& injectedBundle = InjectedBundle::singleton();
1822 if (!injectedBundle.isTestRunning())
1823 return;
1824 if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1825 return;
1826
1827 StringBuilder stringBuilder;
1828 stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidBeginEditing:");
1829 stringBuilder.append(toWTFString(notificationName));
1830 stringBuilder.append('\n');
1831 injectedBundle.outputText(stringBuilder.toString());
1832}
1833
1834void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
1835{
1836 auto& injectedBundle = InjectedBundle::singleton();
1837 if (!injectedBundle.isTestRunning())
1838 return;
1839 if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1840 return;
1841
1842 StringBuilder stringBuilder;
1843 stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidEndEditing:");
1844 stringBuilder.append(toWTFString(notificationName));
1845 stringBuilder.append('\n');
1846 injectedBundle.outputText(stringBuilder.toString());
1847}
1848
1849void InjectedBundlePage::didChange(WKStringRef notificationName)
1850{
1851 auto& injectedBundle = InjectedBundle::singleton();
1852 if (!injectedBundle.isTestRunning())
1853 return;
1854 if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1855 return;
1856
1857 StringBuilder stringBuilder;
1858 stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChange:");
1859 stringBuilder.append(toWTFString(notificationName));
1860 stringBuilder.append('\n');
1861 injectedBundle.outputText(stringBuilder.toString());
1862}
1863
1864void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
1865{
1866 auto& injectedBundle = InjectedBundle::singleton();
1867 if (!injectedBundle.isTestRunning())
1868 return;
1869 if (!injectedBundle.testRunner()->shouldDumpEditingCallbacks())
1870 return;
1871
1872 StringBuilder stringBuilder;
1873 stringBuilder.appendLiteral("EDITING DELEGATE: webViewDidChangeSelection:");
1874 stringBuilder.append(toWTFString(notificationName));
1875 stringBuilder.append('\n');
1876 injectedBundle.outputText(stringBuilder.toString());
1877}
1878
1879#if ENABLE(FULLSCREEN_API)
1880bool InjectedBundlePage::supportsFullScreen(WKBundlePageRef pageRef, WKFullScreenKeyboardRequestType requestType)
1881{
1882 auto& injectedBundle = InjectedBundle::singleton();
1883 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1884 injectedBundle.outputText("supportsFullScreen() == true\n");
1885 return true;
1886}
1887
1888void InjectedBundlePage::enterFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1889{
1890 auto& injectedBundle = InjectedBundle::singleton();
1891 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1892 injectedBundle.outputText("enterFullScreenForElement()\n");
1893
1894 if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1895 WKBundlePageWillEnterFullScreen(pageRef);
1896 WKBundlePageDidEnterFullScreen(pageRef);
1897 }
1898}
1899
1900void InjectedBundlePage::exitFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1901{
1902 auto& injectedBundle = InjectedBundle::singleton();
1903 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1904 injectedBundle.outputText("exitFullScreenForElement()\n");
1905
1906 if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1907 WKBundlePageWillExitFullScreen(pageRef);
1908 WKBundlePageDidExitFullScreen(pageRef);
1909 }
1910}
1911
1912void InjectedBundlePage::beganEnterFullScreen(WKBundlePageRef, WKRect, WKRect)
1913{
1914 auto& injectedBundle = InjectedBundle::singleton();
1915 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1916 injectedBundle.outputText("beganEnterFullScreen()\n");
1917}
1918
1919void InjectedBundlePage::beganExitFullScreen(WKBundlePageRef, WKRect, WKRect)
1920{
1921 auto& injectedBundle = InjectedBundle::singleton();
1922 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1923 injectedBundle.outputText("beganExitFullScreen()\n");
1924}
1925
1926void InjectedBundlePage::closeFullScreen(WKBundlePageRef pageRef)
1927{
1928 auto& injectedBundle = InjectedBundle::singleton();
1929 if (injectedBundle.testRunner()->shouldDumpFullScreenCallbacks())
1930 injectedBundle.outputText("closeFullScreen()\n");
1931
1932 if (!injectedBundle.testRunner()->hasCustomFullScreenBehavior()) {
1933 WKBundlePageWillExitFullScreen(pageRef);
1934 WKBundlePageDidExitFullScreen(pageRef);
1935 }
1936}
1937#endif
1938
1939String InjectedBundlePage::dumpHistory()
1940{
1941 return makeString(
1942 "\n============== Back Forward List ==============\n",
1943 toWTFString(adoptWK(WKBundlePageDumpHistoryForTesting(m_page, toWK("/LayoutTests/").get())).get()),
1944 "===============================================\n"
1945 );
1946}
1947
1948#if !PLATFORM(COCOA)
1949void InjectedBundlePage::platformDidStartProvisionalLoadForFrame(WKBundleFrameRef)
1950{
1951}
1952
1953String InjectedBundlePage::platformResponseMimeType(WKURLResponseRef)
1954{
1955 return String();
1956}
1957#endif
1958
1959void InjectedBundlePage::frameDidChangeLocation(WKBundleFrameRef frame)
1960{
1961 auto& injectedBundle = InjectedBundle::singleton();
1962 if (frame != injectedBundle.topLoadingFrame())
1963 return;
1964
1965 injectedBundle.setTopLoadingFrame(nullptr);
1966
1967 if (injectedBundle.testRunner()->shouldWaitUntilDone())
1968 return;
1969
1970 if (injectedBundle.shouldProcessWorkQueue()) {
1971 injectedBundle.processWorkQueue();
1972 return;
1973 }
1974
1975 if (injectedBundle.pageCount())
1976 injectedBundle.page()->dump();
1977 else
1978 injectedBundle.done();
1979}
1980
1981} // namespace WTR
1982