2010-07-11 Maciej Stachowiak <mjs@apple.com>
[WebKit.git] / WebKitTools / WebKitTestRunner / InjectedBundle / LayoutTestController.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 "LayoutTestController.h"
27 #include "InjectedBundle.h"
28 #include "InjectedBundlePage.h"
29
30 #include <JavaScriptCore/JSRetainPtr.h>
31 #include <WebKit2/WKBundleFrame.h>
32 #include <WebKit2/WKRetainPtr.h>
33 #include <WebKit2/WKStringCF.h>
34 #include <WebKit2/WebKit2.h>
35
36 namespace WTR {
37
38 PassRefPtr<LayoutTestController> LayoutTestController::create(const std::string& testPathOrURL)
39 {
40     return adoptRef(new LayoutTestController(testPathOrURL));
41 }
42
43 LayoutTestController::LayoutTestController(const std::string& testPathOrURL)
44     : m_dumpAsText(false)
45     , m_waitToDump(false)
46     , m_testPathOrURL(testPathOrURL)
47 {
48 }
49
50 LayoutTestController::~LayoutTestController()
51 {
52 }
53
54 static const CFTimeInterval waitToDumpWatchdogInterval = 30.0;
55
56
57 void LayoutTestController::invalidateWaitToDumpWatchdog()
58 {
59     if (m_waitToDumpWatchdog) {
60         CFRunLoopTimerInvalidate(m_waitToDumpWatchdog.get());
61         m_waitToDumpWatchdog = 0;
62     }
63 }
64
65 static void waitUntilDoneWatchdogFired(CFRunLoopTimerRef timer, void* info)
66 {
67     InjectedBundle::shared().layoutTestController()->waitToDumpWatchdogTimerFired();
68 }
69
70 void LayoutTestController::setWaitToDump()
71 {
72     m_waitToDump = true;
73     if (!m_waitToDumpWatchdog) {
74         m_waitToDumpWatchdog.adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + waitToDumpWatchdogInterval, 
75                                                       0, 0, 0, waitUntilDoneWatchdogFired, NULL));
76         CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_waitToDumpWatchdog.get(), kCFRunLoopCommonModes);
77     }
78 }
79
80 void LayoutTestController::waitToDumpWatchdogTimerFired()
81 {
82     invalidateWaitToDumpWatchdog();
83     const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
84     InjectedBundle::shared().os() << message << "\n";
85     InjectedBundle::shared().done();
86 }
87
88 void LayoutTestController::notifyDone()
89 {
90     if (m_waitToDump && !InjectedBundle::shared().page()->isLoading())
91         InjectedBundle::shared().page()->dump();
92     m_waitToDump = false;
93 }
94
95 unsigned LayoutTestController::numberOfActiveAnimations() const
96 {
97     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
98     return WKBundleFrameGetNumberOfActiveAnimations(mainFrame);
99 }
100
101 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
102 {
103     RetainPtr<CFStringRef> idCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, elementId));
104     WKRetainPtr<WKStringRef> idWK(AdoptWK, WKStringCreateWithCFString(idCF.get()));
105     RetainPtr<CFStringRef> nameCF(AdoptCF, JSStringCopyCFString(kCFAllocatorDefault, animationName));
106     WKRetainPtr<WKStringRef> nameWK(AdoptWK, WKStringCreateWithCFString(nameCF.get()));
107
108     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
109     return WKBundleFramePauseAnimationOnElementWithId(mainFrame, nameWK.get(), idWK.get(), time);
110 }
111
112 static JSValueRef dumpAsTextCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
113 {
114     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
115     controller->setDumpAsText(true);
116     return JSValueMakeUndefined(context);
117 }
118
119 static JSValueRef waitUntilDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
120 {
121     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
122     controller->setWaitToDump();
123     return JSValueMakeUndefined(context);
124 }
125
126 static JSValueRef notifyDoneCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
127 {
128     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
129     controller->notifyDone();
130     return JSValueMakeUndefined(context);
131 }
132
133 static JSValueRef numberOfActiveAnimationsCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
134 {
135     if (argumentCount)
136         return JSValueMakeUndefined(context);
137
138     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
139     return JSValueMakeNumber(context, controller->numberOfActiveAnimations());
140 }
141
142 static JSValueRef pauseAnimationAtTimeOnElementWithIdCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
143 {
144     if (argumentCount != 3)
145         return JSValueMakeUndefined(context);
146
147     JSRetainPtr<JSStringRef> animationName(Adopt, JSValueToStringCopy(context, arguments[0], exception));
148     ASSERT(!*exception);
149     double time = JSValueToNumber(context, arguments[1], exception);
150     ASSERT(!*exception);
151     JSRetainPtr<JSStringRef> elementId(Adopt, JSValueToStringCopy(context, arguments[2], exception));
152     ASSERT(!*exception);
153
154     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
155     return JSValueMakeBoolean(context, controller->pauseAnimationAtTimeOnElementWithId(animationName.get(), time, elementId.get()));
156 }
157
158 // Object Finalization
159
160 static void layoutTestControllerObjectFinalize(JSObjectRef object)
161 {
162     LayoutTestController* controller = static_cast<LayoutTestController*>(JSObjectGetPrivate(object));
163     controller->deref();
164 }
165
166 // Object Creation
167
168 void LayoutTestController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
169 {
170     JSRetainPtr<JSStringRef> layoutTestContollerStr(Adopt, JSStringCreateWithUTF8CString("layoutTestController"));
171     ref();
172
173     JSClassRef classRef = getJSClass();
174     JSValueRef layoutTestContollerObject = JSObjectMake(context, classRef, this);
175     JSClassRelease(classRef);
176
177     JSObjectSetProperty(context, windowObject, layoutTestContollerStr.get(), layoutTestContollerObject, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
178 }
179
180 JSClassRef LayoutTestController::getJSClass()
181 {
182     static JSStaticFunction* staticFunctions = LayoutTestController::staticFunctions();
183     static JSClassDefinition classDefinition = {
184         0, kJSClassAttributeNone, "LayoutTestController", 0, 0, staticFunctions,
185         0, layoutTestControllerObjectFinalize, 0, 0, 0, 0, 0, 0, 0, 0, 0
186     };
187
188     return JSClassCreate(&classDefinition);
189 }
190
191 JSStaticFunction* LayoutTestController::staticFunctions()
192 {
193     static JSStaticFunction staticFunctions[] = {
194         { "dumpAsText", dumpAsTextCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
195         { "notifyDone", notifyDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
196         { "numberOfActiveAnimations", numberOfActiveAnimationsCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
197         { "pauseAnimationAtTimeOnElementWithId", pauseAnimationAtTimeOnElementWithIdCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
198         { "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
199         { 0, 0, 0 }
200     };
201
202     return staticFunctions;
203 }
204
205 } // namespace WTR