0875ca4a894e7d8a50fb8d7463e82f9420828547
[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         shouldLoadResourceForFrame
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 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundlePageRef page, WKBundleFrameRef frame, WKStringRef, const void* clientInfo)
326 {
327     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldLoadResourceForFrame(frame);
328 }
329
330
331 void InjectedBundlePage::didStartProvisionalLoadForFrame(WKBundleFrameRef frame)
332 {
333     if (!InjectedBundle::shared().isTestRunning())
334         return;
335
336     if (InjectedBundle::shared().topLoadingFrame())
337         return;
338     InjectedBundle::shared().setTopLoadingFrame(frame);
339 }
340
341 void InjectedBundlePage::didReceiveServerRedirectForProvisionalLoadForFrame(WKBundleFrameRef frame)
342 {
343 }
344
345 void InjectedBundlePage::didFailProvisionalLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef error)
346 {
347     if (!InjectedBundle::shared().isTestRunning())
348         return;
349
350     if (frame != InjectedBundle::shared().topLoadingFrame())
351         return;
352     InjectedBundle::shared().setTopLoadingFrame(0);
353
354     if (InjectedBundle::shared().layoutTestController()->waitToDump())
355         return;
356
357     InjectedBundle::shared().done();
358 }
359
360 void InjectedBundlePage::didCommitLoadForFrame(WKBundleFrameRef frame)
361 {
362 }
363
364 enum FrameNamePolicy { ShouldNotIncludeFrameName, ShouldIncludeFrameName };
365
366 static void dumpFrameScrollPosition(WKBundleFrameRef frame, FrameNamePolicy shouldIncludeFrameName = ShouldNotIncludeFrameName)
367 {
368     double x = numericWindowPropertyValue(frame, "pageXOffset");
369     double y = numericWindowPropertyValue(frame, "pageYOffset");
370     if (fabs(x) > 0.00000001 || fabs(y) > 0.00000001) {
371         if (shouldIncludeFrameName) {
372             WKRetainPtr<WKStringRef> name(AdoptWK, WKBundleFrameCopyName(frame));
373             InjectedBundle::shared().os() << "frame '" << name << "' ";
374         }
375         InjectedBundle::shared().os() << "scrolled to " << x << "," << y << "\n";
376     }
377 }
378
379 static void dumpDescendantFrameScrollPositions(WKBundleFrameRef frame)
380 {
381     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
382     size_t size = WKArrayGetSize(childFrames.get());
383     for (size_t i = 0; i < size; ++i) {
384         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
385         dumpFrameScrollPosition(subframe, ShouldIncludeFrameName);
386         dumpDescendantFrameScrollPositions(subframe);
387     }
388 }
389
390 void InjectedBundlePage::dumpAllFrameScrollPositions()
391 {
392     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
393     dumpFrameScrollPosition(frame);
394     dumpDescendantFrameScrollPositions(frame);
395 }
396
397 static JSRetainPtr<JSStringRef> toJS(const char* string)
398 {
399     return JSRetainPtr<JSStringRef>(Adopt, JSStringCreateWithUTF8CString(string));
400 }
401
402 static bool hasDocumentElement(WKBundleFrameRef frame)
403 {
404     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContext(frame);
405     JSObjectRef globalObject = JSContextGetGlobalObject(context);
406
407     JSValueRef documentValue = JSObjectGetProperty(context, globalObject, toJS("document").get(), 0);
408     if (!documentValue)
409         return false;
410
411     ASSERT(JSValueIsObject(context, documentValue));
412     JSObjectRef document = JSValueToObject(context, documentValue, 0);
413
414     JSValueRef documentElementValue = JSObjectGetProperty(context, document, toJS("documentElement").get(), 0);
415     if (!documentElementValue)
416         return false;
417
418     return JSValueToBoolean(context, documentElementValue);
419 }
420
421 static void dumpFrameText(WKBundleFrameRef frame)
422 {
423     // If the frame doesn't have a document element, its inner text will be an empty string, so
424     // we'll end up just appending a single newline below. But DumpRenderTree doesn't append
425     // anything in this case, so we shouldn't either.
426     if (!hasDocumentElement(frame))
427         return;
428
429     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyInnerText(frame));
430     InjectedBundle::shared().os() << text << "\n";
431 }
432
433 static void dumpDescendantFramesText(WKBundleFrameRef frame)
434 {
435     WKRetainPtr<WKArrayRef> childFrames(AdoptWK, WKBundleFrameCopyChildFrames(frame));
436     size_t size = WKArrayGetSize(childFrames.get());
437     for (size_t i = 0; i < size; ++i) {
438         WKBundleFrameRef subframe = static_cast<WKBundleFrameRef>(WKArrayGetItemAtIndex(childFrames.get(), i));
439         WKRetainPtr<WKStringRef> subframeName(AdoptWK, WKBundleFrameCopyName(subframe));
440         InjectedBundle::shared().os() << "\n--------\nFrame: '" << subframeName << "'\n--------\n";
441         dumpFrameText(subframe);
442         dumpDescendantFramesText(subframe);
443     }
444 }
445
446 void InjectedBundlePage::dumpAllFramesText()
447 {
448     WKBundleFrameRef frame = WKBundlePageGetMainFrame(m_page);
449     dumpFrameText(frame);
450     dumpDescendantFramesText(frame);
451 }
452
453 void InjectedBundlePage::dump()
454 {
455     ASSERT(InjectedBundle::shared().isTestRunning());
456
457     InjectedBundle::shared().layoutTestController()->invalidateWaitToDumpWatchdogTimer();
458
459     switch (InjectedBundle::shared().layoutTestController()->whatToDump()) {
460     case LayoutTestController::RenderTree: {
461         WKRetainPtr<WKStringRef> text(AdoptWK, WKBundlePageCopyRenderTreeExternalRepresentation(m_page));
462         InjectedBundle::shared().os() << text;
463         break;
464     }
465     case LayoutTestController::MainFrameText:
466         dumpFrameText(WKBundlePageGetMainFrame(m_page));
467         break;
468     case LayoutTestController::AllFramesText:
469         dumpAllFramesText();
470         break;
471     }
472
473     if (InjectedBundle::shared().layoutTestController()->shouldDumpAllFrameScrollPositions())
474         dumpAllFrameScrollPositions();
475     else if (InjectedBundle::shared().layoutTestController()->shouldDumpMainFrameScrollPosition())
476         dumpFrameScrollPosition(WKBundlePageGetMainFrame(m_page));
477
478     if (InjectedBundle::shared().layoutTestController()->shouldDumpBackForwardListsForAllWindows())
479         InjectedBundle::shared().dumpBackForwardListsForAllPages();
480
481     InjectedBundle::shared().done();
482 }
483
484 void InjectedBundlePage::didFinishLoadForFrame(WKBundleFrameRef frame)
485 {
486     if (!InjectedBundle::shared().isTestRunning())
487         return;
488
489     if (frame != InjectedBundle::shared().topLoadingFrame())
490         return;
491     InjectedBundle::shared().setTopLoadingFrame(0);
492
493     if (InjectedBundle::shared().layoutTestController()->waitToDump())
494         return;
495
496     InjectedBundle::shared().page()->dump();
497 }
498
499 void InjectedBundlePage::didFailLoadWithErrorForFrame(WKBundleFrameRef frame, WKErrorRef)
500 {
501     if (!InjectedBundle::shared().isTestRunning())
502         return;
503
504     if (frame != InjectedBundle::shared().topLoadingFrame())
505         return;
506     InjectedBundle::shared().setTopLoadingFrame(0);
507
508     if (InjectedBundle::shared().layoutTestController()->waitToDump())
509         return;
510
511     InjectedBundle::shared().done();
512 }
513
514 void InjectedBundlePage::didReceiveTitleForFrame(WKStringRef title, WKBundleFrameRef frame)
515 {
516     if (!InjectedBundle::shared().isTestRunning())
517         return;
518
519     if (!InjectedBundle::shared().layoutTestController()->shouldDumpTitleChanges())
520         return;
521
522     InjectedBundle::shared().os() << "TITLE CHANGED: " << title << "\n";
523 }
524
525 void InjectedBundlePage::didClearWindowForFrame(WKBundleFrameRef frame, WKBundleScriptWorldRef world)
526 {
527     if (!InjectedBundle::shared().isTestRunning())
528         return;
529
530     JSGlobalContextRef context = WKBundleFrameGetJavaScriptContextForWorld(frame, world);
531     JSObjectRef window = JSContextGetGlobalObject(context);
532
533     if (WKBundleScriptWorldNormalWorld() != world) {
534         JSObjectSetProperty(context, window, toJS("__worldID").get(), JSValueMakeNumber(context, LayoutTestController::worldIDForWorld(world)), kJSPropertyAttributeReadOnly, 0);
535         return;
536     }
537
538     JSValueRef exception = 0;
539     InjectedBundle::shared().layoutTestController()->makeWindowObject(context, window, &exception);
540     InjectedBundle::shared().gcController()->makeWindowObject(context, window, &exception);
541     InjectedBundle::shared().eventSendingController()->makeWindowObject(context, window, &exception);
542 }
543
544 void InjectedBundlePage::didCancelClientRedirectForFrame(WKBundleFrameRef frame)
545 {
546 }
547
548 void InjectedBundlePage::willPerformClientRedirectForFrame(WKBundleFrameRef frame, WKURLRef url, double delay, double date)
549 {
550 }
551
552 void InjectedBundlePage::didSameDocumentNavigationForFrame(WKBundleFrameRef frame, WKSameDocumentNavigationType type)
553 {
554 }
555
556 void InjectedBundlePage::didFinishDocumentLoadForFrame(WKBundleFrameRef frame)
557 {
558     if (!InjectedBundle::shared().isTestRunning())
559         return;
560
561     unsigned pendingFrameUnloadEvents = WKBundleFrameGetPendingUnloadCount(frame);
562     if (pendingFrameUnloadEvents)
563         InjectedBundle::shared().os() << frame << " - has " << pendingFrameUnloadEvents << " onunload handler(s)\n";
564 }
565
566 void InjectedBundlePage::didHandleOnloadEventsForFrame(WKBundleFrameRef frame)
567 {
568 }
569
570 void InjectedBundlePage::didDisplayInsecureContentForFrame(WKBundleFrameRef frame)
571 {
572 }
573
574 void InjectedBundlePage::didRunInsecureContentForFrame(WKBundleFrameRef frame)
575 {
576 }
577
578 bool InjectedBundlePage::shouldLoadResourceForFrame(WKBundleFrameRef frame)
579 {
580     return true;
581 }
582
583 // UI Client Callbacks
584
585 void InjectedBundlePage::willAddMessageToConsole(WKBundlePageRef page, WKStringRef message, uint32_t lineNumber, const void *clientInfo)
586 {
587     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willAddMessageToConsole(message, lineNumber);
588 }
589
590 void InjectedBundlePage::willSetStatusbarText(WKBundlePageRef page, WKStringRef statusbarText, const void *clientInfo)
591 {
592     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willSetStatusbarText(statusbarText);
593 }
594
595 void InjectedBundlePage::willRunJavaScriptAlert(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
596 {
597     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptAlert(message, frame);
598 }
599
600 void InjectedBundlePage::willRunJavaScriptConfirm(WKBundlePageRef page, WKStringRef message, WKBundleFrameRef frame, const void *clientInfo)
601 {
602     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptConfirm(message, frame);
603 }
604
605 void InjectedBundlePage::willRunJavaScriptPrompt(WKBundlePageRef page, WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef frame, const void *clientInfo)
606 {
607     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->willRunJavaScriptPrompt(message, defaultValue, frame);
608 }
609
610 void InjectedBundlePage::willAddMessageToConsole(WKStringRef message, uint32_t lineNumber)
611 {
612     if (!InjectedBundle::shared().isTestRunning())
613         return;
614
615     // FIXME: Strip file: urls.
616     InjectedBundle::shared().os() << "CONSOLE MESSAGE: line " << lineNumber << ": " << message << "\n";
617 }
618
619 void InjectedBundlePage::willSetStatusbarText(WKStringRef statusbarText)
620 {
621     if (!InjectedBundle::shared().isTestRunning())
622         return;
623
624     if (!InjectedBundle::shared().layoutTestController()->shouldDumpStatusCallbacks())
625         return;
626
627     InjectedBundle::shared().os() << "UI DELEGATE STATUS CALLBACK: setStatusText:" << statusbarText << "\n";
628 }
629
630 void InjectedBundlePage::willRunJavaScriptAlert(WKStringRef message, WKBundleFrameRef)
631 {
632     if (!InjectedBundle::shared().isTestRunning())
633         return;
634
635     InjectedBundle::shared().os() << "ALERT: " << message << "\n";
636 }
637
638 void InjectedBundlePage::willRunJavaScriptConfirm(WKStringRef message, WKBundleFrameRef)
639 {
640     if (!InjectedBundle::shared().isTestRunning())
641         return;
642
643     InjectedBundle::shared().os() << "CONFIRM: " << message << "\n";
644 }
645
646 void InjectedBundlePage::willRunJavaScriptPrompt(WKStringRef message, WKStringRef defaultValue, WKBundleFrameRef)
647 {
648     InjectedBundle::shared().os() << "PROMPT: " << message << ", default text: " << defaultValue <<  "\n";
649 }
650
651 // Editor Client Callbacks
652
653 bool InjectedBundlePage::shouldBeginEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
654 {
655     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldBeginEditing(range);
656 }
657
658 bool InjectedBundlePage::shouldEndEditing(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
659 {
660     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldEndEditing(range);
661 }
662
663 bool InjectedBundlePage::shouldInsertNode(WKBundlePageRef page, WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
664 {
665     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertNode(node, rangeToReplace, action);
666 }
667
668 bool InjectedBundlePage::shouldInsertText(WKBundlePageRef page, WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action, const void* clientInfo)
669 {
670     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldInsertText(text, rangeToReplace, action);
671 }
672
673 bool InjectedBundlePage::shouldDeleteRange(WKBundlePageRef page, WKBundleRangeHandleRef range, const void* clientInfo)
674 {
675     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldDeleteRange(range);
676 }
677
678 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundlePageRef page, WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting, const void* clientInfo)
679 {
680     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldChangeSelectedRange(fromRange, toRange, affinity, stillSelecting);
681 }
682
683 bool InjectedBundlePage::shouldApplyStyle(WKBundlePageRef page, WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range, const void* clientInfo)
684 {
685     return static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->shouldApplyStyle(style, range);
686 }
687
688 void InjectedBundlePage::didBeginEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
689 {
690     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didBeginEditing(notificationName);
691 }
692
693 void InjectedBundlePage::didEndEditing(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
694 {
695     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didEndEditing(notificationName);
696 }
697
698 void InjectedBundlePage::didChange(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
699 {
700     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChange(notificationName);
701 }
702
703 void InjectedBundlePage::didChangeSelection(WKBundlePageRef page, WKStringRef notificationName, const void* clientInfo)
704 {
705     static_cast<InjectedBundlePage*>(const_cast<void*>(clientInfo))->didChangeSelection(notificationName);
706 }
707
708 bool InjectedBundlePage::shouldBeginEditing(WKBundleRangeHandleRef range)
709 {
710     if (!InjectedBundle::shared().isTestRunning())
711         return true;
712
713     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
714         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldBeginEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
715     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
716 }
717
718 bool InjectedBundlePage::shouldEndEditing(WKBundleRangeHandleRef range)
719 {
720     if (!InjectedBundle::shared().isTestRunning())
721         return true;
722
723     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
724         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldEndEditingInDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
725     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
726 }
727
728 bool InjectedBundlePage::shouldInsertNode(WKBundleNodeHandleRef node, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
729 {
730     if (!InjectedBundle::shared().isTestRunning())
731         return true;
732
733     static const char* insertactionstring[] = {
734         "WebViewInsertActionTyped",
735         "WebViewInsertActionPasted",
736         "WebViewInsertActionDropped",
737     };
738
739     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
740         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";
741     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
742 }
743
744 bool InjectedBundlePage::shouldInsertText(WKStringRef text, WKBundleRangeHandleRef rangeToReplace, WKInsertActionType action)
745 {
746     if (!InjectedBundle::shared().isTestRunning())
747         return true;
748
749     static const char *insertactionstring[] = {
750         "WebViewInsertActionTyped",
751         "WebViewInsertActionPasted",
752         "WebViewInsertActionDropped",
753     };
754
755     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
756         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldInsertText:" << text << " replacingDOMRange:" << toStr(m_page, m_world.get(), rangeToReplace) << " givenAction:" << insertactionstring[action] << "\n";
757     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
758 }
759
760 bool InjectedBundlePage::shouldDeleteRange(WKBundleRangeHandleRef range)
761 {
762     if (!InjectedBundle::shared().isTestRunning())
763         return true;
764
765     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
766         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldDeleteDOMRange:" << toStr(m_page, m_world.get(), range) << "\n";
767     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
768 }
769
770 bool InjectedBundlePage::shouldChangeSelectedRange(WKBundleRangeHandleRef fromRange, WKBundleRangeHandleRef toRange, WKAffinityType affinity, bool stillSelecting)
771 {
772     if (!InjectedBundle::shared().isTestRunning())
773         return true;
774
775     static const char *affinitystring[] = {
776         "NSSelectionAffinityUpstream",
777         "NSSelectionAffinityDownstream"
778     };
779     static const char *boolstring[] = {
780         "FALSE",
781         "TRUE"
782     };
783
784     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
785         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";
786     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
787 }
788
789 bool InjectedBundlePage::shouldApplyStyle(WKBundleCSSStyleDeclarationRef style, WKBundleRangeHandleRef range)
790 {
791     if (!InjectedBundle::shared().isTestRunning())
792         return true;
793
794     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
795         InjectedBundle::shared().os() << "EDITING DELEGATE: shouldApplyStyle:" << style << " toElementsInDOMRange:" << toStr(m_page, m_world.get(), range)  << "\n";
796     return InjectedBundle::shared().layoutTestController()->shouldAllowEditing();
797 }
798
799 void InjectedBundlePage::didBeginEditing(WKStringRef notificationName)
800 {
801     if (!InjectedBundle::shared().isTestRunning())
802         return;
803
804     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
805         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidBeginEditing:" << notificationName << "\n";
806 }
807
808 void InjectedBundlePage::didEndEditing(WKStringRef notificationName)
809 {
810     if (!InjectedBundle::shared().isTestRunning())
811         return;
812
813     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
814         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidEndEditing:" << notificationName << "\n";
815 }
816
817 void InjectedBundlePage::didChange(WKStringRef notificationName)
818 {
819     if (!InjectedBundle::shared().isTestRunning())
820         return;
821
822     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
823         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChange:" << notificationName << "\n";
824 }
825
826 void InjectedBundlePage::didChangeSelection(WKStringRef notificationName)
827 {
828     if (!InjectedBundle::shared().isTestRunning())
829         return;
830
831     if (InjectedBundle::shared().layoutTestController()->shouldDumpEditingCallbacks())
832         InjectedBundle::shared().os() << "EDITING DELEGATE: webViewDidChangeSelection:" << notificationName << "\n";
833 }
834
835 static bool compareByTargetName(WKBundleBackForwardListItemRef item1, WKBundleBackForwardListItemRef item2)
836 {
837     return toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item1))) < toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item2)));
838 }
839
840 static void dumpBackForwardListItem(WKBundleBackForwardListItemRef item, unsigned indent, bool isCurrentItem)
841 {
842     unsigned column = 0;
843     if (isCurrentItem) {
844         InjectedBundle::shared().os() << "curr->";
845         column = 6;
846     }
847     for (unsigned i = column; i < indent; i++)
848         InjectedBundle::shared().os() << ' ';
849
850     string url = toSTD(adoptWK(WKURLCopyString(adoptWK(WKBundleBackForwardListItemCopyURL(item)).get())));
851     if (hasPrefix(url, "file:")) {
852         string directoryName = "/LayoutTests/";
853         size_t start = url.find(directoryName);
854         if (start == string::npos)
855             start = 0;
856         else
857             start += directoryName.size();
858         InjectedBundle::shared().os() << "(file test):" << url.substr(start);
859     } else
860         InjectedBundle::shared().os() << url;
861
862     string target = toSTD(adoptWK(WKBundleBackForwardListItemCopyTarget(item)));
863     if (target.length())
864         InjectedBundle::shared().os() << " (in frame \"" << target << "\")";
865
866     // FIXME: Need WKBackForwardListItemIsTargetItem.
867     if (WKBundleBackForwardListItemIsTargetItem(item))
868         InjectedBundle::shared().os() << "  **nav target**";
869
870     InjectedBundle::shared().os() << '\n';
871
872     if (WKRetainPtr<WKArrayRef> kids = adoptWK(WKBundleBackForwardListItemCopyChildren(item))) {
873         // Sort to eliminate arbitrary result ordering which defeats reproducible testing.
874         size_t size = WKArrayGetSize(kids.get());
875         Vector<WKBundleBackForwardListItemRef> sortedKids(size);
876         for (size_t i = 0; i < size; ++i)
877             sortedKids[i] = static_cast<WKBundleBackForwardListItemRef>(WKArrayGetItemAtIndex(kids.get(), i));
878         stable_sort(sortedKids.begin(), sortedKids.end(), compareByTargetName);
879         for (size_t i = 0; i < size; ++i)
880             dumpBackForwardListItem(sortedKids[i], indent + 4, false);
881     }
882 }
883
884 void InjectedBundlePage::dumpBackForwardList()
885 {
886     InjectedBundle::shared().os() << "\n============== Back Forward List ==============\n";
887
888     WKBundleBackForwardListRef list = WKBundlePageGetBackForwardList(m_page);
889
890     // Print out all items in the list after m_previousTestBackForwardListItem.
891     // Gather items from the end of the list, then print them out from oldest to newest.
892     Vector<WKRetainPtr<WKBundleBackForwardListItemRef> > itemsToPrint;
893     for (unsigned i = WKBundleBackForwardListGetForwardListCount(list); i; --i) {
894         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
895         // Something is wrong if the item from the last test is in the forward part of the list.
896         ASSERT(!WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()));
897         itemsToPrint.append(item);
898     }
899
900     ASSERT(!WKBundleBackForwardListItemIsSame(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)).get(), m_previousTestBackForwardListItem.get()));
901
902     itemsToPrint.append(adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, 0)));
903
904     int currentItemIndex = itemsToPrint.size() - 1;
905
906     int backListCount = WKBundleBackForwardListGetBackListCount(list);
907     for (int i = -1; i >= -backListCount; --i) {
908         WKRetainPtr<WKBundleBackForwardListItemRef> item = adoptWK(WKBundleBackForwardListCopyItemAtIndex(list, i));
909         if (WKBundleBackForwardListItemIsSame(item.get(), m_previousTestBackForwardListItem.get()))
910             break;
911         itemsToPrint.append(item);
912     }
913
914     for (int i = itemsToPrint.size() - 1; i >= 0; i--)
915         dumpBackForwardListItem(itemsToPrint[i].get(), 8, i == currentItemIndex);
916
917     InjectedBundle::shared().os() << "===============================================\n";
918 }
919
920 } // namespace WTR