82437a7a572eff8e18018e7bc1852c64c7f67cff
[WebKit.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 "InjectedBundlePage.h"
27
28 #include "InjectedBundle.h"
29 #include "StringFunctions.h"
30 #include <cmath>
31 #include <JavaScriptCore/JSRetainPtr.h>
32 #include <WebKit2/WKArray.h>
33 #include <WebKit2/WKBundle.h>
34 #include <WebKit2/WKBundleBackForwardList.h>
35 #include <WebKit2/WKBundleBackForwardListItem.h>
36 #include <WebKit2/WKBundleFrame.h>
37 #include <WebKit2/WKBundleFramePrivate.h>
38 #include <WebKit2/WKBundlePagePrivate.h>
39
40 using namespace std;
41
42 namespace WTR {
43
44 template<typename T> static inline WKRetainPtr<T> adoptWK(T item)
45 {
46     return WKRetainPtr<T>(AdoptWK, item);
47 }
48
49 static bool hasPrefix(const string& searchString, const string& prefix)
50 {
51     return searchString.length() >= prefix.length() && searchString.substr(0, prefix.length()) == prefix;
52 }
53
54 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
55 {
56     if (!object)
57         return 0;
58     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
59     return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
60 }
61
62 static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
63 {
64     JSValueRef value = propertyValue(context, object, propertyName);
65     if (!value)
66         return 0;
67     return JSValueToNumber(context, value, 0);    
68 }
69
70 static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
71 {
72     return static_cast<int>(propertyValueDouble(context, object, propertyName));    
73 }
74
75 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
76 {
77     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
78     return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
79 }
80
81 static string dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
82 {
83     JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
84     JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
85     WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
86
87     JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
88
89     ostringstream out;
90     out << nodeName;
91
92     if (parentNode && JSValueIsObject(context, parentNode))
93         out << " > " << dumpPath(context, (JSObjectRef)parentNode);
94
95     return out.str();
96 }
97
98 static string dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
99 {
100     if (!node)
101         return "(null)";
102
103     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
104
105     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
106     JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
107     ASSERT(JSValueIsObject(context, nodeValue));
108     JSObjectRef nodeObject = (JSObjectRef)nodeValue;
109
110     return dumpPath(context, nodeObject);
111 }
112
113 static string toStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
114 {
115     if (!rangeRef)
116         return "(null)";
117
118     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
119
120     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
121     JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
122     ASSERT(JSValueIsObject(context, rangeValue));
123     JSObjectRef rangeObject = (JSObjectRef)rangeValue;
124
125     JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
126     ASSERT(JSValueIsObject(context, startNodeValue));
127     JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
128
129     JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
130     ASSERT(JSValueIsObject(context, endNodeValue));
131     JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
132
133     int startOffset = propertyValueInt(context, rangeObject, "startOffset");
134     int endOffset = propertyValueInt(context, rangeObject, "endOffset");
135
136     ostringstream out;
137     out << "range from " << startOffset << " of " << dumpPath(context, startNodeObject) << " to " << endOffset << " of " << dumpPath(context, endNodeObject);
138     return out.str();
139 }
140
141 static ostream& operator<<(ostream& out, WKBundleCSSStyleDeclarationRef style)
142 {
143     // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
144     // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
145     // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
146     out << "<DOMCSSStyleDeclaration ADDRESS>";
147     return out;
148 }
149
150 static ostream& operator<<(ostream& out, WKBundleFrameRef frame)
151 {
152     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
153     if (WKBundleFrameIsMainFrame(frame)) {
154         if (!WKStringIsEmpty(name.get()))
155             out << "main frame \"" << name << "\"";
156         else
157             out << "main frame";
158     } else {
159         if (!WKStringIsEmpty(name.get()))
160             out << "frame \"" << name << "\"";
161         else
162             out << "frame (anonymous)";
163     }
164
165     return out;
166 }
167
168 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
169     : m_page(page)
170     , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
171 {
172     WKBundlePageLoaderClient loaderClient = {
173         0,
174         this,
175         didStartProvisionalLoadForFrame,
176         didReceiveServerRedirectForProvisionalLoadForFrame,
177         didFailProvisionalLoadWithErrorForFrame,
178         didCommitLoadForFrame,
179         didFinishDocumentLoadForFrame,
180         didFinishLoadForFrame,
181         didFailLoadWithErrorForFrame,
182         didSameDocumentNavigationForFrame,
183         didReceiveTitleForFrame,
184         0,
185         0,
186         0,
187         didDisplayInsecureContentForFrame,
188         didRunInsecureContentForFrame,
189         didClearWindowForFrame,
190         didCancelClientRedirectForFrame,
191         willPerformClientRedirectForFrame,
192         didHandleOnloadEventsForFrame,
193         willSendRequestForFrame
194     };
195     WKBundlePageSetLoaderClient(m_page, &loaderClient);
196
197     WKBundlePageUIClient uiClient = {
198         0,
199         this,
200         willAddMessageToConsole,
201         willSetStatusbarText,
202         willRunJavaScriptAlert,
203         willRunJavaScriptConfirm,
204         willRunJavaScriptPrompt,
205         0, /*mouseDidMoveOverElement*/
206         0, /*pageDidScroll*/
207         0, /*paintCustomOverhangArea*/
208     };
209     WKBundlePageSetUIClient(m_page, &uiClient);
210
211     WKBundlePageEditorClient editorClient = {
212         0,
213         this,
214         shouldBeginEditing,
215         shouldEndEditing,
216         shouldInsertNode,
217         shouldInsertText,
218         shouldDeleteRange,
219         shouldChangeSelectedRange,
220         shouldApplyStyle,
221         didBeginEditing,
222         didEndEditing,
223         didChange,
224         didChangeSelection
225     };
226     WKBundlePageSetEditorClient(m_page, &editorClient);
227 }
228
229 InjectedBundlePage::~InjectedBundlePage()
230 {
231 }
232
233 void InjectedBundlePage::stopLoading()
234 {
235     WKBundlePageStopLoading(m_page);
236 }
237
238 void InjectedBundlePage::reset()
239 {
240     WKBundlePageClearMainFrameName(m_page);
241
242     WKBundlePageSetPageZoomFactor(m_page, 1);
243     WKBundlePageSetTextZoomFactor(m_page, 1);
244
245     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
246 }
247
248 // Loader Client Callbacks
249
250 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
251 {
252     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
253 }
254
255 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
256 {
257     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
258 }
259
260 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
261 {
262     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
263 }
264
265 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
266 {
267     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
268 }
269
270 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
271 {
272     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
273 }
274
275 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
276 {
277     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
278 }
279
280 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
281 {
282     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
283 }
284
285 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
286 {
287     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
288 }
289
290 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
291 {
292     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
293 }
294
295 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
296 {
297     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
298 }
299
300 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
301 {
302     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
303 }
304
305 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
306 {
307     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
308 }
309
310 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
311 {
312     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
313 }
314
315 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
316 {
317     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
318 }
319
320 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
321 {
322     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
323 }
324
325 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef page, WKBundleFrameRef frame, uint64_t identifier, WKURLRequestRef request, WKURLResponseRef redirectResponse, const void* clientInfo)
326 {
327     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSendRequestForFrame(page, frame, identifier, request, redirectResponse);
328 }
329
330 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
331 {
332     if (!InjectedBundle::shared().isTestRunning())
333         return;
334
335     if (InjectedBundle::shared().topLoadingFrame())
336         return;
337     InjectedBundle::shared().setTopLoadingFrame(frame);
338 }
339
340 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
341 {
342 }
343
344 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
345 {
346     if (!InjectedBundle::shared().isTestRunning())
347         return;
348
349     if (frame != InjectedBundle::shared().topLoadingFrame())
350         return;
351     InjectedBundle::shared().setTopLoadingFrame(0);
352
353     if (InjectedBundle::shared().layoutTestController()->waitToDump())
354         return;
355
356     InjectedBundle::shared().done();
357 }
358
359 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
360 {
361 }
362
363 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
364
365 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
366 {
367     double x = numericWindowPropertyValue(frame, "pageXOffset");
368     double y = numericWindowPropertyValue(frame, "pageYOffset");
369     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
370         if (shouldIncludeFrameName) {
371             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
372             InjectedBundle::shared().os() << "frame '" << name << "' ";
373         }
374         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
375     }
376 }
377
378 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
379 {
380     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
381     size_t size = WKArrayGetSize(childFrames.get());
382     for (size_t i = 0; i < size; ++i) {
383         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
384         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
385         dumpDescendantFrameScrollPositions(subframe);
386     }
387 }
388
389 void InjectedBundlePage::dumpAllFrameScrollPositions()
390 {
391     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
392     dumpFrameScrollPosition(frame);
393     dumpDescendantFrameScrollPositions(frame);
394 }
395
396 static JSRetainPtr<JSStringRef> toJS(const char* string)
397 {
398     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
399 }
400
401 static bool hasDocumentElement(WKBundleFrameRef frame)
402 {
403     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
404     JSObjectRef globalObject = JSContextGetGlobalObject(context);
405
406     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
407     if (!documentValue)
408         return false;
409
410     ASSERT(JSValueIsObject(context, documentValue));
411     JSObjectRef document = JSValueToObject(context, documentValue, 0);
412
413     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
414     if (!documentElementValue)
415         return false;
416
417     return JSValueToBoolean(context, documentElementValue);
418 }
419
420 static void dumpFrameText(WKBundleFrameRef frame)
421 {
422     // If the frame doesn't have a document element, its inner text will be an empty string, so
423     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
424     // anything in this case, so we shouldn't either.
425     if (!hasDocumentElement(frame))
426         return;
427
428     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
429     InjectedBundle::shared().os() << text << "\n";
430 }
431
432 static void dumpDescendantFramesText(WKBundleFrameRef frame)
433 {
434     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
435     size_t size = WKArrayGetSize(childFrames.get());
436     for (size_t i = 0; i < size; ++i) {
437         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
438         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
439         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
440         dumpFrameText(subframe);
441         dumpDescendantFramesText(subframe);
442     }
443 }
444
445 void InjectedBundlePage::dumpAllFramesText()
446 {
447     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
448     dumpFrameText(frame);
449     dumpDescendantFramesText(frame);
450 }
451
452 void InjectedBundlePage::dump()
453 {
454     ASSERT(InjectedBundle::shared().isTestRunning());
455
456     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
457
458     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
459     case LayoutTestController::RenderTree: {
460         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
461         InjectedBundle::shared().os() << text;
462         break;
463     }
464     case LayoutTestController::MainFrameText:
465         dumpFrameText(WKBundlePageGetMainFrame(m_page));
466         break;
467     case LayoutTestController::AllFramesText:
468         dumpAllFramesText();
469         break;
470     }
471
472     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
473         dumpAllFrameScrollPositions();
474     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
475         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
476
477     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
478         InjectedBundle::shared().dumpBackForwardListsForAllPages();
479
480     InjectedBundle::shared().done();
481 }
482
483 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
484 {
485     if (!InjectedBundle::shared().isTestRunning())
486         return;
487
488     if (frame != InjectedBundle::shared().topLoadingFrame())
489         return;
490     InjectedBundle::shared().setTopLoadingFrame(0);
491
492     if (InjectedBundle::shared().layoutTestController()->waitToDump())
493         return;
494
495     InjectedBundle::shared().page()->dump();
496 }
497
498 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
499 {
500     if (!InjectedBundle::shared().isTestRunning())
501         return;
502
503     if (frame != InjectedBundle::shared().topLoadingFrame())
504         return;
505     InjectedBundle::shared().setTopLoadingFrame(0);
506
507     if (InjectedBundle::shared().layoutTestController()->waitToDump())
508         return;
509
510     InjectedBundle::shared().done();
511 }
512
513 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
514 {
515     if (!InjectedBundle::shared().isTestRunning())
516         return;
517
518     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
519         return;
520
521     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
522 }
523
524 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
525 {
526     if (!InjectedBundle::shared().isTestRunning())
527         return;
528
529     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
530     JSObjectRef window = JSContextGetGlobalObject(context);
531
532     if (WKBundleScriptWorldNormalWorld() != world) {
533         JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, LayoutTestController::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
534         return;
535     }
536
537     JSValueRef exception = 0;
538     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
539     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
540     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
541 }
542
543 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
544 {
545 }
546
547 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
548 {
549 }
550
551 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
552 {
553 }
554
555 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
556 {
557     if (!InjectedBundle::shared().isTestRunning())
558         return;
559
560     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
561     if (pendingFrameUnloadEvents)
562         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
563 }
564
565 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
566 {
567 }
568
569 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
570 {
571 }
572
573 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
574 {
575 }
576
577 WKURLRequestRef InjectedBundlePage::willSendRequestForFrame(WKBundlePageRef, WKBundleFrameRef, uint64_t, WKURLRequestRef request, WKURLResponseRef)
578 {
579     return request;
580 }
581
582 // UI Client Callbacks
583
584 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
585 {
586     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
587 }
588
589 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
590 {
591     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
592 }
593
594 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
595 {
596     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
597 }
598
599 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
600 {
601     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
602 }
603
604 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
605 {
606     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
607 }
608
609 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
610 {
611     if (!InjectedBundle::shared().isTestRunning())
612         return;
613
614     // FIXME: Strip file: urls.
615     InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << message << "\n";
616 }
617
618 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
619 {
620     if (!InjectedBundle::shared().isTestRunning())
621         return;
622
623     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
624         return;
625
626     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
627 }
628
629 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
630 {
631     if (!InjectedBundle::shared().isTestRunning())
632         return;
633
634     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
635 }
636
637 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
638 {
639     if (!InjectedBundle::shared().isTestRunning())
640         return;
641
642     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
643 }
644
645 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
646 {
647     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
648 }
649
650 // Editor Client Callbacks
651
652 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
653 {
654     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
655 }
656
657 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
658 {
659     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
660 }
661
662 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
663 {
664     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
665 }
666
667 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
668 {
669     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
670 }
671
672 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
673 {
674     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
675 }
676
677 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
678 {
679     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
680 }
681
682 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
683 {
684     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
685 }
686
687 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
688 {
689     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
690 }
691
692 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
693 {
694     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
695 }
696
697 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
698 {
699     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
700 }
701
702 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
703 {
704     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
705 }
706
707 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
708 {
709     if (!InjectedBundle::shared().isTestRunning())
710         return true;
711
712     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
713         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
714     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
715 }
716
717 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
718 {
719     if (!InjectedBundle::shared().isTestRunning())
720         return true;
721
722     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
723         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
724     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
725 }
726
727 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
728 {
729     if (!InjectedBundle::shared().isTestRunning())
730         return true;
731
732     static const char* insertactionstring[] = {
733         "WebViewInsertActionTyped",
734         "WebViewInsertActionPasted",
735         "WebViewInsertActionDropped",
736     };
737
738     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
739         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";
740     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
741 }
742
743 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
744 {
745     if (!InjectedBundle::shared().isTestRunning())
746         return true;
747
748     static const char *insertactionstring[] = {
749         "WebViewInsertActionTyped",
750         "WebViewInsertActionPasted",
751         "WebViewInsertActionDropped",
752     };
753
754     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
755         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
756     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
757 }
758
759 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
760 {
761     if (!InjectedBundle::shared().isTestRunning())
762         return true;
763
764     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
765         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
766     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
767 }
768
769 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
770 {
771     if (!InjectedBundle::shared().isTestRunning())
772         return true;
773
774     static const char *affinitystring[] = {
775         "NSSelectionAffinityUpstream",
776         "NSSelectionAffinityDownstream"
777     };
778     static const char *boolstring[] = {
779         "FALSE",
780         "TRUE"
781     };
782
783     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
784         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";
785     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
786 }
787
788 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
789 {
790     if (!InjectedBundle::shared().isTestRunning())
791         return true;
792
793     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
794         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
795     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
796 }
797
798 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
799 {
800     if (!InjectedBundle::shared().isTestRunning())
801         return;
802
803     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
804         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
805 }
806
807 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
808 {
809     if (!InjectedBundle::shared().isTestRunning())
810         return;
811
812     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
813         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
814 }
815
816 void InjectedBundlePage::didChange(WKStringRef notificationName)
817 {
818     if (!InjectedBundle::shared().isTestRunning())
819         return;
820
821     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
822         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
823 }
824
825 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
826 {
827     if (!InjectedBundle::shared().isTestRunning())
828         return;
829
830     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
831         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
832 }
833
834 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
835 {
836     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
837 }
838
839 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
840 {
841     unsigned column = 0;
842     if (isCurrentItem) {
843         InjectedBundle::shared().os() << "curr->";
844         column = 6;
845     }
846     for (unsigned i = column; i < indent; i++)
847         InjectedBundle::shared().os() << ' ';
848
849     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
850     if (hasPrefix(url, "file:")) {
851         string directoryName = "/LayoutTests/";
852         size_t start = url.find(directoryName);
853         if (start == string::npos)
854             start = 0;
855         else
856             start += directoryName.size();
857         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
858     } else
859         InjectedBundle::shared().os() << url;
860
861     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
862     if (target.length())
863         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
864
865     // FIXME: Need WKBackForwardListItemIsTargetItem.
866     if (WKBundleBackForwardListItemIsTargetItem(item))
867         InjectedBundle::shared().os() << "  **nav target**";
868
869     InjectedBundle::shared().os() << '\n';
870
871     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
872         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
873         size_t size = WKArrayGetSize(kids.get());
874         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
875         for (size_t i = 0; i < size; ++i)
876             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
877         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
878         for (size_t i = 0; i < size; ++i)
879             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
880     }
881 }
882
883 void InjectedBundlePage::dumpBackForwardList()
884 {
885     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
886
887     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
888
889     // Print out all items in the list after m_previousTestBackForwardListItem.
890     // Gather items from the end of the list, then print them out from oldest to newest.
891     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
892     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
893         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
894         // Something is wrong if the item from the last test is in the forward part of the list.
895         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
896         itemsToPrint.append(item);
897     }
898
899     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
900
901     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
902
903     int currentItemIndex = itemsToPrint.size() - 1;
904
905     int backListCount = WKBundleBackForwardListGetBackListCount(list);
906     for (int i = -1; i >= -backListCount; --i) {
907         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
908         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
909             break;
910         itemsToPrint.append(item);
911     }
912
913     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
914         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
915
916     InjectedBundle::shared().os() << "===============================================\n";
917 }
918
919 } // namespace WTR