2011-01-18 Maciej Stachowiak <mjs@apple.com>
[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 #include <WebKit2/WKStringCF.h>
41
42 using namespace std;
43
44 namespace WTR {
45
46 template<typename T> static inline WKRetainPtr<T> adoptWK(T item)
47 {
48     return WKRetainPtr<T>(AdoptWK, item);
49 }
50
51 static bool hasPrefix(const string& searchString, const string& prefix)
52 {
53     return searchString.length() >= prefix.length() && searchString.substr(0, prefix.length()) == prefix;
54 }
55
56 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
57 {
58     if (!object)
59         return 0;
60     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
61     return JSObjectGetProperty(context, object, propertyNameString.get(), 0);
62 }
63
64 static double propertyValueDouble(JSContextRef context, JSObjectRef object, const char* propertyName)
65 {
66     JSValueRef value = propertyValue(context, object, propertyName);
67     if (!value)
68         return 0;
69     return JSValueToNumber(context, value, 0);    
70 }
71
72 static int propertyValueInt(JSContextRef context, JSObjectRef object, const char* propertyName)
73 {
74     return static_cast<int>(propertyValueDouble(context, object, propertyName));    
75 }
76
77 static double numericWindowPropertyValue(WKBundleFrameRef frame, const char* propertyName)
78 {
79     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
80     return propertyValueDouble(context, JSContextGetGlobalObject(context), propertyName);
81 }
82
83 static string dumpPath(JSGlobalContextRef context, JSObjectRef nodeValue)
84 {
85     JSValueRef nodeNameValue = propertyValue(context, nodeValue, "nodeName");
86     JSRetainPtr<JSStringRef> jsStringNodeName(Adopt, JSValueToStringCopy(context, nodeNameValue, 0));
87     WKRetainPtr<WKStringRef> nodeName = toWK(jsStringNodeName);
88
89     JSValueRef parentNode = propertyValue(context, nodeValue, "parentNode");
90
91     ostringstream out;
92     out << nodeName;
93
94     if (parentNode && JSValueIsObject(context, parentNode))
95         out << " > " << dumpPath(context, (JSObjectRef)parentNode);
96
97     return out.str();
98 }
99
100 static string dumpPath(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleNodeHandleRef node)
101 {
102     if (!node)
103         return "(null)";
104
105     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
106
107     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
108     JSValueRef nodeValue = WKBundleFrameGetJavaScriptWrapperForNodeForWorld(frame, node, world);
109     ASSERT(JSValueIsObject(context, nodeValue));
110     JSObjectRef nodeObject = (JSObjectRef)nodeValue;
111
112     return dumpPath(context, nodeObject);
113 }
114
115 static string toStr(WKBundlePageRef page, WKBundleScriptWorldRef world, WKBundleRangeHandleRef rangeRef)
116 {
117     if (!rangeRef)
118         return "(null)";
119
120     WKBundleFrameRef frame = WKBundlePageGetMainFrame(page);
121
122     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
123     JSValueRef rangeValue = WKBundleFrameGetJavaScriptWrapperForRangeForWorld(frame, rangeRef, world);
124     ASSERT(JSValueIsObject(context, rangeValue));
125     JSObjectRef rangeObject = (JSObjectRef)rangeValue;
126
127     JSValueRef startNodeValue = propertyValue(context, rangeObject, "startContainer");
128     ASSERT(JSValueIsObject(context, startNodeValue));
129     JSObjectRef startNodeObject = (JSObjectRef)startNodeValue;
130
131     JSValueRef endNodeValue = propertyValue(context, rangeObject, "endContainer");
132     ASSERT(JSValueIsObject(context, endNodeValue));
133     JSObjectRef endNodeObject = (JSObjectRef)endNodeValue;
134
135     int startOffset = propertyValueInt(context, rangeObject, "startOffset");
136     int endOffset = propertyValueInt(context, rangeObject, "endOffset");
137
138     ostringstream out;
139     out << "range from " << startOffset << " of " << dumpPath(context, startNodeObject) << " to " << endOffset << " of " << dumpPath(context, endNodeObject);
140     return out.str();
141 }
142
143 static ostream& operator<<(ostream& out, WKBundleCSSStyleDeclarationRef style)
144 {
145     // DumpRenderTree calls -[DOMCSSStyleDeclaration description], which just dumps class name and object address.
146     // No existing tests actually hit this code path at the time of this writing, because WebCore doesn't call
147     // the editing client if the styling operation source is CommandFromDOM or CommandFromDOMWithUserInterface.
148     out << "<DOMCSSStyleDeclaration ADDRESS>";
149     return out;
150 }
151
152 static ostream& operator<<(ostream& out, WKBundleFrameRef frame)
153 {
154     WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
155     if (WKBundleFrameIsMainFrame(frame)) {
156         if (!WKStringIsEmpty(name.get()))
157             out << "main frame \"" << name << "\"";
158         else
159             out << "main frame";
160     } else {
161         if (!WKStringIsEmpty(name.get()))
162             out << "frame \"" << name << "\"";
163         else
164             out << "frame (anonymous)";
165     }
166
167     return out;
168 }
169
170 InjectedBundlePage::InjectedBundlePage(WKBundlePageRef page)
171     : m_page(page)
172     , m_world(AdoptWK, WKBundleScriptWorldCreateWorld())
173 {
174     WKBundlePageLoaderClient loaderClient = {
175         0,
176         this,
177         didStartProvisionalLoadForFrame,
178         didReceiveServerRedirectForProvisionalLoadForFrame,
179         didFailProvisionalLoadWithErrorForFrame,
180         didCommitLoadForFrame,
181         didFinishDocumentLoadForFrame,
182         didFinishLoadForFrame,
183         didFailLoadWithErrorForFrame,
184         didSameDocumentNavigationForFrame,
185         didReceiveTitleForFrame,
186         0,
187         0,
188         0,
189         didDisplayInsecureContentForFrame,
190         didRunInsecureContentForFrame,
191         didClearWindowForFrame,
192         didCancelClientRedirectForFrame,
193         willPerformClientRedirectForFrame,
194         didHandleOnloadEventsForFrame,
195         shouldLoadResourceForFrame
196     };
197     WKBundlePageSetLoaderClient(m_page, &loaderClient);
198
199     WKBundlePageUIClient uiClient = {
200         0,
201         this,
202         willAddMessageToConsole,
203         willSetStatusbarText,
204         willRunJavaScriptAlert,
205         willRunJavaScriptConfirm,
206         willRunJavaScriptPrompt,
207         0, /*mouseDidMoveOverElement*/
208         0, /*pageDidScroll*/
209     };
210     WKBundlePageSetUIClient(m_page, &uiClient);
211
212     WKBundlePageEditorClient editorClient = {
213         0,
214         this,
215         shouldBeginEditing,
216         shouldEndEditing,
217         shouldInsertNode,
218         shouldInsertText,
219         shouldDeleteRange,
220         shouldChangeSelectedRange,
221         shouldApplyStyle,
222         didBeginEditing,
223         didEndEditing,
224         didChange,
225         didChangeSelection
226     };
227     WKBundlePageSetEditorClient(m_page, &editorClient);
228 }
229
230 InjectedBundlePage::~InjectedBundlePage()
231 {
232 }
233
234 void InjectedBundlePage::stopLoading()
235 {
236     WKBundlePageStopLoading(m_page);
237 }
238
239 void InjectedBundlePage::reset()
240 {
241     WKBundlePageClearMainFrameName(m_page);
242
243     WKBundlePageSetPageZoomFactor(m_page, 1);
244     WKBundlePageSetTextZoomFactor(m_page, 1);
245
246     m_previousTestBackForwardListItem = adoptWK(WKBundleBackForwardListCopyItemAtIndex(WKBundlePageGetBackForwardList(m_page), 0));
247 }
248
249 // Loader Client Callbacks
250
251 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
252 {
253     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didStartProvisionalLoadForFrame(frame);
254 }
255
256 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
257 {
258     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveServerRedirectForProvisionalLoadForFrame(frame);
259 }
260
261 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
262 {
263     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailProvisionalLoadWithErrorForFrame(frame, error);
264 }
265
266 void InjectedBundlePage::didCommitLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
267 {
268     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCommitLoadForFrame(frame);
269 }
270
271 void InjectedBundlePage::didFinishLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
272 {
273     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishLoadForFrame(frame);
274 }
275
276 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
277 {
278     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFinishDocumentLoadForFrame(frame);
279 }
280
281 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKErrorRef error, WKTypeRef*, const void *clientInfo)
282 {
283     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didFailLoadWithErrorForFrame(frame, error);
284 }
285
286 void InjectedBundlePage::didReceiveTitleForFrame(WKBundlePageRef page, WKStringRef title, WKBundleFrameRef frame, WKTypeRef*, const void *clientInfo)
287 {
288     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didReceiveTitleForFrame(title, frame);
289 }
290
291 void InjectedBundlePage::didClearWindowForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKBundleScriptWorldRef world, const void *clientInfo)
292 {
293     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didClearWindowForFrame(frame, world);
294 }
295
296 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
297 {
298     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didCancelClientRedirectForFrame(frame);
299 }
300
301 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKURLRef url, double delay, double date, const void* clientInfo)
302 {
303     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willPerformClientRedirectForFrame(frame, url, delay, date);
304 }
305
306 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKSameDocumentNavigationType type, WKTypeRef*, const void* clientInfo)
307 {
308     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didSameDocumentNavigationForFrame(frame, type);
309 }
310
311 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundlePageRef page, WKBundleFrameRef frame, const void* clientInfo)
312 {
313     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didHandleOnloadEventsForFrame(frame);
314 }
315
316 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
317 {
318     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didDisplayInsecureContentForFrame(frame);
319 }
320
321 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKTypeRef*, const void* clientInfo)
322 {
323     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didRunInsecureContentForFrame(frame);
324 }
325
326 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKStringRef, const void* clientInfo)
327 {
328     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldLoadResourceForFrame(frame);
329 }
330
331
332 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
333 {
334     if (!InjectedBundle::shared().isTestRunning())
335         return;
336
337     if (InjectedBundle::shared().topLoadingFrame())
338         return;
339     InjectedBundle::shared().setTopLoadingFrame(frame);
340 }
341
342 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
343 {
344 }
345
346 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
347 {
348     if (!InjectedBundle::shared().isTestRunning())
349         return;
350
351     if (frame != InjectedBundle::shared().topLoadingFrame())
352         return;
353     InjectedBundle::shared().setTopLoadingFrame(0);
354
355     if (InjectedBundle::shared().layoutTestController()->waitToDump())
356         return;
357
358     InjectedBundle::shared().done();
359 }
360
361 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
362 {
363 }
364
365 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
366
367 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
368 {
369     double x = numericWindowPropertyValue(frame, "pageXOffset");
370     double y = numericWindowPropertyValue(frame, "pageYOffset");
371     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
372         if (shouldIncludeFrameName) {
373             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
374             InjectedBundle::shared().os() << "frame '" << name << "' ";
375         }
376         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
377     }
378 }
379
380 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
381 {
382     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
383     size_t size = WKArrayGetSize(childFrames.get());
384     for (size_t i = 0; i < size; ++i) {
385         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
386         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
387         dumpDescendantFrameScrollPositions(subframe);
388     }
389 }
390
391 void InjectedBundlePage::dumpAllFrameScrollPositions()
392 {
393     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
394     dumpFrameScrollPosition(frame);
395     dumpDescendantFrameScrollPositions(frame);
396 }
397
398 static JSRetainPtr<JSStringRef> toJS(const char* string)
399 {
400     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
401 }
402
403 static bool hasDocumentElement(WKBundleFrameRef frame)
404 {
405     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
406     JSObjectRef globalObject = JSContextGetGlobalObject(context);
407
408     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
409     if (!documentValue)
410         return false;
411
412     ASSERT(JSValueIsObject(context, documentValue));
413     JSObjectRef document = JSValueToObject(context, documentValue, 0);
414
415     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
416     if (!documentElementValue)
417         return false;
418
419     return JSValueToBoolean(context, documentElementValue);
420 }
421
422 static void dumpFrameText(WKBundleFrameRef frame)
423 {
424     // If the frame doesn't have a document element, its inner text will be an empty string, so
425     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
426     // anything in this case, so we shouldn't either.
427     if (!hasDocumentElement(frame))
428         return;
429
430     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
431     InjectedBundle::shared().os() << text << "\n";
432 }
433
434 static void dumpDescendantFramesText(WKBundleFrameRef frame)
435 {
436     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
437     size_t size = WKArrayGetSize(childFrames.get());
438     for (size_t i = 0; i < size; ++i) {
439         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
440         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
441         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
442         dumpFrameText(subframe);
443         dumpDescendantFramesText(subframe);
444     }
445 }
446
447 void InjectedBundlePage::dumpAllFramesText()
448 {
449     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
450     dumpFrameText(frame);
451     dumpDescendantFramesText(frame);
452 }
453
454 void InjectedBundlePage::dump()
455 {
456     ASSERT(InjectedBundle::shared().isTestRunning());
457
458     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
459
460     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
461     case LayoutTestController::RenderTree: {
462         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
463         InjectedBundle::shared().os() << text;
464         break;
465     }
466     case LayoutTestController::MainFrameText:
467         dumpFrameText(WKBundlePageGetMainFrame(m_page));
468         break;
469     case LayoutTestController::AllFramesText:
470         dumpAllFramesText();
471         break;
472     }
473
474     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
475         dumpAllFrameScrollPositions();
476     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
477         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
478
479     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
480         InjectedBundle::shared().dumpBackForwardListsForAllPages();
481
482     InjectedBundle::shared().done();
483 }
484
485 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
486 {
487     if (!InjectedBundle::shared().isTestRunning())
488         return;
489
490     if (frame != InjectedBundle::shared().topLoadingFrame())
491         return;
492     InjectedBundle::shared().setTopLoadingFrame(0);
493
494     if (InjectedBundle::shared().layoutTestController()->waitToDump())
495         return;
496
497     InjectedBundle::shared().page()->dump();
498 }
499
500 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
501 {
502     if (!InjectedBundle::shared().isTestRunning())
503         return;
504
505     if (frame != InjectedBundle::shared().topLoadingFrame())
506         return;
507     InjectedBundle::shared().setTopLoadingFrame(0);
508
509     if (InjectedBundle::shared().layoutTestController()->waitToDump())
510         return;
511
512     InjectedBundle::shared().done();
513 }
514
515 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
516 {
517     if (!InjectedBundle::shared().isTestRunning())
518         return;
519
520     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
521         return;
522
523     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
524 }
525
526 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
527 {
528     if (!InjectedBundle::shared().isTestRunning())
529         return;
530
531     if (WKBundleScriptWorldNormalWorld() != world)
532         return;
533
534     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
535     JSObjectRef window = JSContextGetGlobalObject(context);
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 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundleFrameRef frame)
578 {
579     return true;
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