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