<rdar://problem/10809525> WebKit2’s WebFrameLoaderClient::shouldUseCredentialStorage...
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / InjectedBundlePage.cpp
1 /*
2  * Copyright (C) 2010 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 <WebKit2/WKArray.h>
35 #include <WebKit2/WKBundle.h>
36 #include <WebKit2/WKBundleBackForwardList.h>
37 #include <WebKit2/WKBundleBackForwardListItem.h>
38 #include <WebKit2/WKBundleFrame.h>
39 #include <WebKit2/WKBundleFramePrivate.h>
40 #include <WebKit2/WKBundleHitTestResult.h>
41 #include <WebKit2/WKBundleNavigationAction.h>
42 #include <WebKit2/WKBundleNodeHandlePrivate.h>
43 #include <WebKit2/WKBundlePagePrivate.h>
44 #include <WebKit2/WKURLRequest.h>
45
46 #if PLATFORM(QT)
47 #include "DumpRenderTreeSupportQt.h"
48 #endif
49
50 using namespace std;
51
52 namespace WTR {
53
54 static bool hasPrefix(const string& searchString, const string& prefix)
55 {
56     return searchString.length() >= prefix.length() && searchString.substr(0, prefix.length()) == prefix;
57 }
58
59 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
60 {
61     if (!object)
62         return 0;
63     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
64     return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
65 }
66
67 static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
68 {
69     JSValueRef value = propertyValue(context, object, propertyName);
70     if (!value)
71         return 0;
72     return JSValueToNumber(context, value, 0);    
73 }
74
75 static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
76 {
77     return static_cast<int>(propertyValueDouble(context, object, propertyName));    
78 }
79
80 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
81 {
82     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
83     return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
84 }
85
86 static string dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
87 {
88     JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
89     JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
90     WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
91
92     JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
93
94     ostringstream out;
95     out << nodeName;
96
97     if (parentNode && JSValueIsObject(context, parentNode))
98         out << " > " << dumpPath(context, (JSObjectRef)parentNode);
99
100     return out.str();
101 }
102
103 static string dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
104 {
105     if (!node)
106         return "(null)";
107
108     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
109
110     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
111     JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
112     ASSERT(JSValueIsObject(context, nodeValue));
113     JSObjectRef nodeObject = (JSObjectRef)nodeValue;
114
115     return dumpPath(context, nodeObject);
116 }
117
118 static string toStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
119 {
120     if (!rangeRef)
121         return "(null)";
122
123     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
124
125     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
126     JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
127     ASSERT(JSValueIsObject(context, rangeValue));
128     JSObjectRef rangeObject = (JSObjectRef)rangeValue;
129
130     JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
131     ASSERT(JSValueIsObject(context, startNodeValue));
132     JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
133
134     JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
135     ASSERT(JSValueIsObject(context, endNodeValue));
136     JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
137
138     int startOffset = propertyValueInt(context, rangeObject, "startOffset");
139     int endOffset = propertyValueInt(context, rangeObject, "endOffset");
140
141     ostringstream out;
142     out << "range from " << startOffset << " of " << dumpPath(context, startNodeObject) << " to " << endOffset << " of " << dumpPath(context, endNodeObject);
143     return out.str();
144 }
145
146 static WKRetainPtr<WKStringRef> navigationTypeToString(WKFrameNavigationType type)
147 {
148     switch (type) {
149     case kWKFrameNavigationTypeLinkClicked:
150         return adoptWK(WKStringCreateWithUTF8CString("link clicked"));
151     case kWKFrameNavigationTypeFormSubmitted:
152         return adoptWK(WKStringCreateWithUTF8CString("form submitted"));
153     case kWKFrameNavigationTypeBackForward:
154         return adoptWK(WKStringCreateWithUTF8CString("back/forward"));
155     case kWKFrameNavigationTypeReload:
156         return adoptWK(WKStringCreateWithUTF8CString("reload"));
157     case kWKFrameNavigationTypeFormResubmitted:
158         return adoptWK(WKStringCreateWithUTF8CString("form resubmitted"));
159     case kWKFrameNavigationTypeOther:
160         return adoptWK(WKStringCreateWithUTF8CString("other"));
161     }
162     return adoptWK(WKStringCreateWithUTF8CString("illegal value"));
163 }
164
165 static ostream& operator<<(ostream& out, WKBundleCSSStyleDeclarationRef style)
166 {
167     // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
168     // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
169     // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
170     out << "<DOMCSSStyleDeclaration ADDRESS>";
171     return out;
172 }
173
174 static ostream& operator<<(ostream& out, WKBundleFrameRef frame)
175 {
176     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
177     if (WKBundleFrameIsMainFrame(frame)) {
178         if (!WKStringIsEmpty(name.get()))
179             out << "main frame \"" << name << "\"";
180         else
181             out << "main frame";
182     } else {
183         if (!WKStringIsEmpty(name.get()))
184             out << "frame \"" << name << "\"";
185         else
186             out << "frame (anonymous)";
187     }
188
189     return out;
190 }
191
192 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
193     : m_page(page)
194     , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
195 {
196     WKBundlePageLoaderClient loaderClient = {
197         kWKBundlePageLoaderClientCurrentVersion,
198         this,
199         didStartProvisionalLoadForFrame,
200         didReceiveServerRedirectForProvisionalLoadForFrame,
201         didFailProvisionalLoadWithErrorForFrame,
202         didCommitLoadForFrame,
203         didFinishDocumentLoadForFrame,
204         didFinishLoadForFrame,
205         didFailLoadWithErrorForFrame,
206         didSameDocumentNavigationForFrame,
207         didReceiveTitleForFrame,
208         0, // didFirstLayoutForFrame
209         0, // didFirstVisuallyNonEmptyLayoutForFrame
210         0, // didRemoveFrameFromHierarchy
211         didDisplayInsecureContentForFrame,
212         didRunInsecureContentForFrame,
213         didClearWindowForFrame,
214         didCancelClientRedirectForFrame,
215         willPerformClientRedirectForFrame,
216         didHandleOnloadEventsForFrame,
217         0, // didLayoutForFrame
218         didDetectXSSForFrame,
219     };
220     WKBundlePageSetPageLoaderClient(m_page, &loaderClient);
221
222     WKBundlePageResourceLoadClient resourceLoadClient = {
223         kWKBundlePageResourceLoadClientCurrentVersion,
224         this,
225         didInitiateLoadForResource,
226         willSendRequestForFrame,
227         didReceiveResponseForResource,
228         didReceiveContentLengthForResource,
229         didFinishLoadForResource,
230         didFailLoadForResource,
231         0, // shouldCacheResponse
232         0 // shouldUseCredentialStorage
233     };
234     WKBundlePageSetResourceLoadClient(m_page, &resourceLoadClient);
235
236     WKBundlePagePolicyClient policyClient = {
237         kWKBundlePagePolicyClientCurrentVersion,
238         this,
239         decidePolicyForNavigationAction,
240         decidePolicyForNewWindowAction,
241         decidePolicyForResponse,
242         unableToImplementPolicy
243     };
244     WKBundlePageSetPolicyClient(m_page, &policyClient);
245
246     WKBundlePageUIClient uiClient = {
247         kWKBundlePageUIClientCurrentVersion,
248         this,
249         willAddMessageToConsole,
250         willSetStatusbarText,
251         willRunJavaScriptAlert,
252         willRunJavaScriptConfirm,
253         willRunJavaScriptPrompt,
254         0, /*mouseDidMoveOverElement*/
255         0, /*pageDidScroll*/
256         0, /*paintCustomOverhangArea*/
257         0, /*shouldGenerateFileForUpload*/
258         0, /*generateFileForUpload*/
259         0, /*shouldRubberBandInDirection*/
260         0, /*statusBarIsVisible*/
261         0, /*menuBarIsVisible*/
262         0, /*toolbarsAreVisible*/
263     };
264     WKBundlePageSetUIClient(m_page, &uiClient);
265
266     WKBundlePageEditorClient editorClient = {
267         kWKBundlePageEditorClientCurrentVersion,
268         this,
269         shouldBeginEditing,
270         shouldEndEditing,
271         shouldInsertNode,
272         shouldInsertText,
273         shouldDeleteRange,
274         shouldChangeSelectedRange,
275         shouldApplyStyle,
276         didBeginEditing,
277         didEndEditing,
278         didChange,
279         didChangeSelection
280     };
281     WKBundlePageSetEditorClient(m_page, &editorClient);
282
283 #if ENABLE(FULLSCREEN_API)
284     WKBundlePageFullScreenClient fullScreenClient = {
285         kWKBundlePageFullScreenClientCurrentVersion,
286         this,
287         supportsFullScreen,
288         enterFullScreenForElement,
289         exitFullScreenForElement,
290     };
291     WKBundlePageSetFullScreenClient(m_page, &fullScreenClient);
292 #endif
293 }
294
295 InjectedBundlePage::~InjectedBundlePage()
296 {
297 }
298
299 void InjectedBundlePage::stopLoading()
300 {
301     WKBundlePageStopLoading(m_page);
302 }
303
304 void InjectedBundlePage::reset()
305 {
306     WKBundlePageClearMainFrameName(m_page);
307
308     WKBundlePageSetPageZoomFactor(m_page, 1);
309     WKBundlePageSetTextZoomFactor(m_page, 1);
310
311     WKPoint origin = { 0, 0 };
312     WKBundlePageSetScaleAtOrigin(m_page, 1, origin);
313
314     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
315
316     WKBundleFrameClearOpener(WKBundlePageGetMainFrame(m_page));
317     
318     WKBundlePageSetTracksRepaints(m_page, false);
319 }
320
321 // Loader Client Callbacks
322
323 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
324 {
325     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
326 }
327
328 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
329 {
330     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
331 }
332
333 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
334 {
335     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
336 }
337
338 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
339 {
340     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
341 }
342
343 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
344 {
345     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
346 }
347
348 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
349 {
350     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
351 }
352
353 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
354 {
355     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
356 }
357
358 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
359 {
360     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
361 }
362
363 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
364 {
365     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
366 }
367
368 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
369 {
370     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
371 }
372
373 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
374 {
375     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
376 }
377
378 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
379 {
380     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
381 }
382
383 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
384 {
385     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
386 }
387
388 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
389 {
390     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
391 }
392
393 void InjectedBundlePage::didDetectXSSForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
394 {
395     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDetectXSSForFrame(frame);
396 }
397
398 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
399 {
400     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
401 }
402
403 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, bool pageLoadIsProvisional, const void* clientInfo)
404 {
405     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didInitiateLoadForResource(page, frame, identifier, request, pageLoadIsProvisional);
406 }
407
408 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
409 {
410     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
411 }
412
413 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLResponseRef response, const void* clientInfo)
414 {
415     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveResponseForResource(page, frame, identifier, response);
416 }
417
418 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, uint64_t length, const void* clientInfo)
419 {
420     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveContentLengthForResource(page, frame, identifier, length);
421 }
422
423 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, const void* clientInfo)
424 {
425     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier);
426 }
427
428 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKErrorRef error, const void* clientInfo)
429 {
430     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForResource(page, frame, identifier, error);
431 }
432
433 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
434 {
435     if (!InjectedBundle::shared().isTestRunning())
436         return;
437
438     if (InjectedBundle::shared().topLoadingFrame())
439         return;
440     InjectedBundle::shared().setTopLoadingFrame(frame);
441 }
442
443 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
444 {
445 }
446
447 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
448 {
449     if (!InjectedBundle::shared().isTestRunning())
450         return;
451
452     if (frame != InjectedBundle::shared().topLoadingFrame())
453         return;
454     InjectedBundle::shared().setTopLoadingFrame(0);
455
456     if (InjectedBundle::shared().layoutTestController()->waitToDump())
457         return;
458
459     InjectedBundle::shared().done();
460 }
461
462 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
463 {
464 }
465
466 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
467
468 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
469 {
470     double x = numericWindowPropertyValue(frame, "pageXOffset");
471     double y = numericWindowPropertyValue(frame, "pageYOffset");
472     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
473         if (shouldIncludeFrameName) {
474             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
475             InjectedBundle::shared().os() << "frame '" << name << "' ";
476         }
477         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
478     }
479 }
480
481 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
482 {
483     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
484     size_t size = WKArrayGetSize(childFrames.get());
485     for (size_t i = 0; i < size; ++i) {
486         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
487         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
488         dumpDescendantFrameScrollPositions(subframe);
489     }
490 }
491
492 void InjectedBundlePage::dumpAllFrameScrollPositions()
493 {
494     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
495     dumpFrameScrollPosition(frame);
496     dumpDescendantFrameScrollPositions(frame);
497 }
498
499 static JSRetainPtr<JSStringRef> toJS(const char* string)
500 {
501     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
502 }
503
504 static bool hasDocumentElement(WKBundleFrameRef frame)
505 {
506     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
507     JSObjectRef globalObject = JSContextGetGlobalObject(context);
508
509     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
510     if (!documentValue)
511         return false;
512
513     ASSERT(JSValueIsObject(context, documentValue));
514     JSObjectRef document = JSValueToObject(context, documentValue, 0);
515
516     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
517     if (!documentElementValue)
518         return false;
519
520     return JSValueToBoolean(context, documentElementValue);
521 }
522
523 static void dumpFrameText(WKBundleFrameRef frame)
524 {
525     // If the frame doesn't have a document element, its inner text will be an empty string, so
526     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
527     // anything in this case, so we shouldn't either.
528     if (!hasDocumentElement(frame))
529         return;
530
531     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
532     InjectedBundle::shared().os() << text << "\n";
533 }
534
535 static void dumpDescendantFramesText(WKBundleFrameRef frame)
536 {
537     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
538     size_t size = WKArrayGetSize(childFrames.get());
539     for (size_t i = 0; i < size; ++i) {
540         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
541         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
542         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
543         dumpFrameText(subframe);
544         dumpDescendantFramesText(subframe);
545     }
546 }
547
548 void InjectedBundlePage::dumpAllFramesText()
549 {
550     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
551     dumpFrameText(frame);
552     dumpDescendantFramesText(frame);
553 }
554
555 void InjectedBundlePage::dump()
556 {
557     ASSERT(InjectedBundle::shared().isTestRunning());
558
559     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
560
561     // Force a paint before dumping. This matches DumpRenderTree on Windows. (DumpRenderTree on Mac
562     // does this at a slightly different time.) See <http://webkit.org/b/55469> for details.
563     WKBundlePageForceRepaint(m_page);
564
565     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
566     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleFrameCopyURL(frame)).get())));
567     if (strstr(url.c_str(), "dumpAsText/"))
568         InjectedBundle::shared().layoutTestController()->dumpAsText(false);
569
570     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
571     case LayoutTestController::RenderTree: {
572         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
573         InjectedBundle::shared().os() << text;
574         break;
575     }
576     case LayoutTestController::MainFrameText:
577         dumpFrameText(WKBundlePageGetMainFrame(m_page));
578         break;
579     case LayoutTestController::AllFramesText:
580         dumpAllFramesText();
581         break;
582     }
583
584     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
585         dumpAllFrameScrollPositions();
586     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
587         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
588
589     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
590         InjectedBundle::shared().dumpBackForwardListsForAllPages();
591
592     if (InjectedBundle::shared().shouldDumpPixels() && InjectedBundle::shared().layoutTestController()->shouldDumpPixels()) {
593         InjectedBundle::shared().setPixelResult(adoptWK(WKBundlePageCreateSnapshotInViewCoordinates(m_page, WKBundleFrameGetVisibleContentBounds(WKBundlePageGetMainFrame(m_page)), kWKImageOptionsShareable)).get());
594         if (WKBundlePageIsTrackingRepaints(m_page))
595             InjectedBundle::shared().setRepaintRects(adoptWK(WKBundlePageCopyTrackedRepaintRects(m_page)).get());
596     }
597
598     InjectedBundle::shared().done();
599 }
600
601 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
602 {
603     if (!InjectedBundle::shared().isTestRunning())
604         return;
605
606     if (frame != InjectedBundle::shared().topLoadingFrame())
607         return;
608     InjectedBundle::shared().setTopLoadingFrame(0);
609
610     if (InjectedBundle::shared().layoutTestController()->waitToDump())
611         return;
612
613     InjectedBundle::shared().page()->dump();
614 }
615
616 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
617 {
618     if (!InjectedBundle::shared().isTestRunning())
619         return;
620
621     if (frame != InjectedBundle::shared().topLoadingFrame())
622         return;
623     InjectedBundle::shared().setTopLoadingFrame(0);
624
625     if (InjectedBundle::shared().layoutTestController()->waitToDump())
626         return;
627
628     InjectedBundle::shared().done();
629 }
630
631 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
632 {
633     if (!InjectedBundle::shared().isTestRunning())
634         return;
635
636     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
637         return;
638
639     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
640 }
641
642 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
643 {
644     if (!InjectedBundle::shared().isTestRunning())
645         return;
646
647     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
648     JSObjectRef window = JSContextGetGlobalObject(context);
649
650     if (WKBundleScriptWorldNormalWorld() != world) {
651         JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, LayoutTestController::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
652         return;
653     }
654
655     JSValueRef exception = 0;
656     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
657     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
658     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
659     InjectedBundle::shared().textInputController()->makeWindowObject(context, window, &exception);
660     InjectedBundle::shared().accessibilityController()->makeWindowObject(context, window, &exception);
661
662 #if PLATFORM(QT)
663     DumpRenderTreeSupportQt::injectInternalsObject(context);
664 #else
665     WebCoreTestSupport::injectInternalsObject(context);
666 #endif
667 }
668
669 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
670 {
671 }
672
673 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
674 {
675 }
676
677 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
678 {
679 }
680
681 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
682 {
683     if (!InjectedBundle::shared().isTestRunning())
684         return;
685
686     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
687     if (pendingFrameUnloadEvents)
688         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
689 }
690
691 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
692 {
693 }
694
695 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
696 {
697 }
698
699 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
700 {
701 }
702
703 void InjectedBundlePage::didDetectXSSForFrame(WKBundleFrameRef frame)
704 {
705 }
706
707 void InjectedBundlePage::didInitiateLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t identifier, WKURLRequestRef, bool)
708 {
709 }
710
711 // Resource Load Client Callbacks
712
713 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLRequestRef request, WKURLResponseRef)
714 {
715     if (InjectedBundle::shared().isTestRunning() && InjectedBundle::shared().layoutTestController()->willSendRequestReturnsNull())
716         return 0;
717
718     WKRetainPtr<WKURLRef> url = adoptWK(WKURLRequestCopyURL(request));
719     WKRetainPtr<WKStringRef> host = adoptWK(WKURLCopyHostName(url.get()));
720     WKRetainPtr<WKStringRef> scheme = adoptWK(WKURLCopyScheme(url.get()));
721     if (host && !WKStringIsEmpty(host.get())
722         && (WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "http") || WKStringIsEqualToUTF8CStringIgnoringCase(scheme.get(), "https"))
723         && !WKStringIsEqualToUTF8CString(host.get(), "127.0.0.1")
724         && !WKStringIsEqualToUTF8CString(host.get(), "255.255.255.255") // Used in some tests that expect to get back an error.
725         && !WKStringIsEqualToUTF8CStringIgnoringCase(host.get(), "localhost")) {
726         InjectedBundle::shared().os() << "Blocked access to external URL " << url << "\n";
727         return 0;
728     }
729
730     WKRetain(request);
731     return request;
732 }
733
734 void InjectedBundlePage::didReceiveResponseForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLResponseRef)
735 {
736 }
737
738 void InjectedBundlePage::didReceiveContentLengthForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, uint64_t)
739 {
740 }
741
742 void InjectedBundlePage::didFinishLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t)
743 {
744 }
745
746 void InjectedBundlePage::didFailLoadForResource(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKErrorRef)
747 {
748 }
749
750
751 // Policy Client Callbacks
752
753 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
754 {
755     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNavigationAction(page, frame, navigationAction, request, userData);
756 }
757
758 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKStringRef frameName, WKTypeRef* userData, const void* clientInfo)
759 {
760     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForNewWindowAction(page, frame, navigationAction, request, frameName, userData);
761 }
762
763 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef page, WKBundleFrameRef frame, WKURLResponseRef response, WKURLRequestRef request, WKTypeRef* userData, const void* clientInfo)
764 {
765     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->decidePolicyForResponse(page, frame, response, request, userData);
766 }
767
768 void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef* userData, const void* clientInfo)
769 {
770     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->unableToImplementPolicy(page, frame, error, userData);
771 }
772
773 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNavigationAction(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleNavigationActionRef navigationAction, WKURLRequestRef request, WKTypeRef* userData)
774 {
775     if (!InjectedBundle::shared().isTestRunning())
776         return WKBundlePagePolicyActionUse;
777
778     if (!InjectedBundle::shared().layoutTestController()->isPolicyDelegateEnabled())
779         return WKBundlePagePolicyActionUse;
780
781     if (InjectedBundle::shared().layoutTestController()->waitToDump()) {
782         InjectedBundle::shared().os() << "Policy delegate: attempt to load " << adoptWK(WKURLRequestCopyURL(request)) << " with navigation type \'" << navigationTypeToString(WKBundleNavigationActionGetNavigationType(navigationAction)) << "\'";
783         WKBundleHitTestResultRef hitTestResultRef = WKBundleNavigationActionCopyHitTestResult(navigationAction);
784         if (hitTestResultRef)
785             InjectedBundle::shared().os() << " originating from " << dumpPath(m_page, m_world.get(), WKBundleHitTestResultCopyNodeHandle(hitTestResultRef));
786
787         InjectedBundle::shared().os() << "\n";
788         InjectedBundle::shared().layoutTestController()->notifyDone();
789     }
790
791     if (InjectedBundle::shared().layoutTestController()->isPolicyDelegatePermissive())
792         return WKBundlePagePolicyActionUse;
793     return WKBundlePagePolicyActionPassThrough;
794 }
795
796 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForNewWindowAction(WKBundlePageRef, WKBundleFrameRef, WKBundleNavigationActionRef, WKURLRequestRef, WKStringRef, WKTypeRef*)
797 {
798     return WKBundlePagePolicyActionUse;
799 }
800
801 WKBundlePagePolicyAction InjectedBundlePage::decidePolicyForResponse(WKBundlePageRef, WKBundleFrameRef, WKURLResponseRef, WKURLRequestRef, WKTypeRef*)
802 {
803     return WKBundlePagePolicyActionUse;
804 }
805
806 void InjectedBundlePage::unableToImplementPolicy(WKBundlePageRef, WKBundleFrameRef, WKErrorRef, WKTypeRef*)
807 {
808 }
809
810 // UI Client Callbacks
811
812 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
813 {
814     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
815 }
816
817 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
818 {
819     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
820 }
821
822 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
823 {
824     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
825 }
826
827 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
828 {
829     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
830 }
831
832 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
833 {
834     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
835 }
836
837 static string lastFileURLPathComponent(const string& path)
838 {
839     size_t pos = path.find("file://");
840     ASSERT(string::npos != pos);
841
842     string tmpPath = path.substr(pos + 7);
843     if (tmpPath.length() < 2) // Keep the lone slash to avoid empty output.
844         return tmpPath;
845
846     // Remove the trailing delimiter
847     if (tmpPath[tmpPath.length() - 1] == '/')
848         tmpPath.erase(tmpPath.length() - 1);
849
850     pos = tmpPath.rfind('/');
851     if (string::npos != pos)
852         return tmpPath.substr(pos + 1);
853
854     return tmpPath;
855 }
856
857 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
858 {
859     if (!InjectedBundle::shared().isTestRunning())
860         return;
861
862     string messageString = toSTD(message);
863     size_t fileProtocolStart = messageString.find("file://");
864     if (fileProtocolStart != string::npos)
865         // FIXME: The code below does not handle additional text after url nor multiple urls. This matches DumpRenderTree implementation.
866         messageString = messageString.substr(0, fileProtocolStart) + lastFileURLPathComponent(messageString.substr(fileProtocolStart));
867
868     InjectedBundle::shared().os() << "CONSOLE MESSAGE: ";
869     if (lineNumber)
870         InjectedBundle::shared().os() << "line " << lineNumber << ": ";
871     InjectedBundle::shared().os() << messageString << "\n";
872
873 }
874
875 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
876 {
877     if (!InjectedBundle::shared().isTestRunning())
878         return;
879
880     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
881         return;
882
883     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
884 }
885
886 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
887 {
888     if (!InjectedBundle::shared().isTestRunning())
889         return;
890
891     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
892 }
893
894 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
895 {
896     if (!InjectedBundle::shared().isTestRunning())
897         return;
898
899     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
900 }
901
902 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
903 {
904     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
905 }
906
907 // Editor Client Callbacks
908
909 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
910 {
911     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
912 }
913
914 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
915 {
916     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
917 }
918
919 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
920 {
921     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
922 }
923
924 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
925 {
926     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
927 }
928
929 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
930 {
931     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
932 }
933
934 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
935 {
936     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
937 }
938
939 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
940 {
941     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
942 }
943
944 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
945 {
946     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
947 }
948
949 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
950 {
951     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
952 }
953
954 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
955 {
956     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
957 }
958
959 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
960 {
961     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
962 }
963
964 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
965 {
966     if (!InjectedBundle::shared().isTestRunning())
967         return true;
968
969     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
970         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
971     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
972 }
973
974 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
975 {
976     if (!InjectedBundle::shared().isTestRunning())
977         return true;
978
979     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
980         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
981     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
982 }
983
984 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
985 {
986     if (!InjectedBundle::shared().isTestRunning())
987         return true;
988
989     static const char* insertactionstring[] = {
990         "WebViewInsertActionTyped",
991         "WebViewInsertActionPasted",
992         "WebViewInsertActionDropped",
993     };
994
995     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
996         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertNode:" << dumpPath(m_page, m_world.get(), node) << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
997     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
998 }
999
1000 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
1001 {
1002     if (!InjectedBundle::shared().isTestRunning())
1003         return true;
1004
1005     static const char *insertactionstring[] = {
1006         "WebViewInsertActionTyped",
1007         "WebViewInsertActionPasted",
1008         "WebViewInsertActionDropped",
1009     };
1010
1011     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1012         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
1013     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
1014 }
1015
1016 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
1017 {
1018     if (!InjectedBundle::shared().isTestRunning())
1019         return true;
1020
1021     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1022         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
1023     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
1024 }
1025
1026 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
1027 {
1028     if (!InjectedBundle::shared().isTestRunning())
1029         return true;
1030
1031     static const char *affinitystring[] = {
1032         "NSSelectionAffinityUpstream",
1033         "NSSelectionAffinityDownstream"
1034     };
1035     static const char *boolstring[] = {
1036         "FALSE",
1037         "TRUE"
1038     };
1039
1040     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1041         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldChangeSelectedDOMRange:" << toStr(m_page, m_world.get(), fromRange) << " toDOMRange:" << toStr(m_page, m_world.get(), toRange) << " affinity:" << affinitystring[affinity] << " stillSelecting:" << boolstring[stillSelecting] << "\n";
1042     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
1043 }
1044
1045 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
1046 {
1047     if (!InjectedBundle::shared().isTestRunning())
1048         return true;
1049
1050     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1051         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
1052     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
1053 }
1054
1055 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
1056 {
1057     if (!InjectedBundle::shared().isTestRunning())
1058         return;
1059
1060     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1061         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
1062 }
1063
1064 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
1065 {
1066     if (!InjectedBundle::shared().isTestRunning())
1067         return;
1068
1069     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1070         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
1071 }
1072
1073 void InjectedBundlePage::didChange(WKStringRef notificationName)
1074 {
1075     if (!InjectedBundle::shared().isTestRunning())
1076         return;
1077
1078     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1079         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
1080 }
1081
1082 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
1083 {
1084     if (!InjectedBundle::shared().isTestRunning())
1085         return;
1086
1087     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
1088         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
1089 }
1090
1091 #if ENABLE(FULLSCREEN_API)
1092 bool InjectedBundlePage::supportsFullScreen(WKBundlePageRef pageRef, WKFullScreenKeyboardRequestType requestType)
1093 {
1094     if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks())
1095         InjectedBundle::shared().os() << "supportsFullScreen() == true\n";
1096     return true;
1097 }
1098
1099 void InjectedBundlePage::enterFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1100 {
1101     if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks())
1102         InjectedBundle::shared().os() << "enterFullScreenForElement()\n";
1103     WKBundlePageWillEnterFullScreen(pageRef);
1104     WKBundlePageDidEnterFullScreen(pageRef);
1105 }
1106
1107 void InjectedBundlePage::exitFullScreenForElement(WKBundlePageRef pageRef, WKBundleNodeHandleRef elementRef)
1108 {
1109     if (InjectedBundle::shared().layoutTestController()->shouldDumpFullScreenCallbacks())
1110         InjectedBundle::shared().os() << "exitFullScreenForElement()\n";
1111     WKBundlePageWillExitFullScreen(pageRef);
1112     WKBundlePageDidExitFullScreen(pageRef);
1113 }
1114 #endif
1115
1116 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
1117 {
1118     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
1119 }
1120
1121 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
1122 {
1123     unsigned column = 0;
1124     if (isCurrentItem) {
1125         InjectedBundle::shared().os() << "curr->";
1126         column = 6;
1127     }
1128     for (unsigned i = column; i < indent; i++)
1129         InjectedBundle::shared().os() << ' ';
1130
1131     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
1132     if (hasPrefix(url, "file:")) {
1133         string directoryName = "/LayoutTests/";
1134         size_t start = url.find(directoryName);
1135         if (start == string::npos)
1136             start = 0;
1137         else
1138             start += directoryName.size();
1139         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
1140     } else
1141         InjectedBundle::shared().os() << url;
1142
1143     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
1144     if (target.length())
1145         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
1146
1147     // FIXME: Need WKBackForwardListItemIsTargetItem.
1148     if (WKBundleBackForwardListItemIsTargetItem(item))
1149         InjectedBundle::shared().os() << "  **nav target**";
1150
1151     InjectedBundle::shared().os() << '\n';
1152
1153     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
1154         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
1155         size_t size = WKArrayGetSize(kids.get());
1156         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
1157         for (size_t i = 0; i < size; ++i)
1158             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
1159         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
1160         for (size_t i = 0; i < size; ++i)
1161             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
1162     }
1163 }
1164
1165 void InjectedBundlePage::dumpBackForwardList()
1166 {
1167     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
1168
1169     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
1170
1171     // Print out all items in the list after m_previousTestBackForwardListItem.
1172     // Gather items from the end of the list, then print them out from oldest to newest.
1173     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
1174     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
1175         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
1176         // Something is wrong if the item from the last test is in the forward part of the list.
1177         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
1178         itemsToPrint.append(item);
1179     }
1180
1181     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
1182
1183     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
1184
1185     int currentItemIndex = itemsToPrint.size() - 1;
1186
1187     int backListCount = WKBundleBackForwardListGetBackListCount(list);
1188     for (int i = -1; i >= -backListCount; --i) {
1189         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
1190         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
1191             break;
1192         itemsToPrint.append(item);
1193     }
1194
1195     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
1196         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
1197
1198     InjectedBundle::shared().os() << "===============================================\n";
1199 }
1200
1201 } // namespace WTR