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