de06c516339db0e482674909274f1fa64744b874
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / TestRunner.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012 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 "config.h"
27 #include "TestRunner.h"
28
29 #include "InjectedBundle.h"
30 #include "InjectedBundlePage.h"
31 #include "JSTestRunner.h"
32 #include "PlatformWebView.h"
33 #include "StringFunctions.h"
34 #include "TestController.h"
35 #include <WebCore/PageVisibilityState.h>
36 #include <WebKit2/WKBundleBackForwardList.h>
37 #include <WebKit2/WKBundleFrame.h>
38 #include <WebKit2/WKBundleFramePrivate.h>
39 #include <WebKit2/WKBundleInspector.h>
40 #include <WebKit2/WKBundleNodeHandlePrivate.h>
41 #include <WebKit2/WKBundlePagePrivate.h>
42 #include <WebKit2/WKBundlePrivate.h>
43 #include <WebKit2/WKBundleScriptWorld.h>
44 #include <WebKit2/WKData.h>
45 #include <WebKit2/WKRetainPtr.h>
46 #include <WebKit2/WKSerializedScriptValue.h>
47 #include <WebKit2/WebKit2_C.h>
48 #include <wtf/CurrentTime.h>
49 #include <wtf/HashMap.h>
50 #include <wtf/OwnArrayPtr.h>
51 #include <wtf/PassOwnArrayPtr.h>
52 #include <wtf/text/CString.h>
53 #include <wtf/text/StringBuilder.h>
54
55 #if ENABLE(WEB_INTENTS)
56 #include <WebKit2/WKBundleIntent.h>
57 #include <WebKit2/WKBundleIntentRequest.h>
58 #endif
59
60 namespace WTR {
61
62 const double TestRunner::waitToDumpWatchdogTimerInterval = 30;
63
64 PassRefPtr<TestRunner> TestRunner::create()
65 {
66     return adoptRef(new TestRunner);
67 }
68
69 TestRunner::TestRunner()
70     : m_whatToDump(RenderTree)
71     , m_shouldDumpAllFrameScrollPositions(false)
72     , m_shouldDumpBackForwardListsForAllWindows(false)
73     , m_shouldAllowEditing(true)
74     , m_shouldCloseExtraWindows(false)
75     , m_dumpEditingCallbacks(false)
76     , m_dumpStatusCallbacks(false)
77     , m_dumpTitleChanges(false)
78     , m_dumpPixels(true)
79     , m_dumpSelectionRect(false)
80     , m_dumpFullScreenCallbacks(false)
81     , m_dumpFrameLoadCallbacks(false)
82     , m_dumpProgressFinishedCallback(false)
83     , m_dumpResourceLoadCallbacks(false)
84     , m_dumpResourceResponseMIMETypes(false)
85     , m_dumpWillCacheResponse(false)
86     , m_dumpApplicationCacheDelegateCallbacks(false)
87     , m_dumpDatabaseCallbacks(false)
88     , m_disallowIncreaseForApplicationCacheQuota(false)
89     , m_waitToDump(false)
90     , m_testRepaint(false)
91     , m_testRepaintSweepHorizontally(false)
92     , m_willSendRequestReturnsNull(false)
93     , m_willSendRequestReturnsNullOnRedirect(false)
94     , m_shouldStopProvisionalFrameLoads(false)
95     , m_policyDelegateEnabled(false)
96     , m_policyDelegatePermissive(false)
97     , m_globalFlag(false)
98     , m_customFullScreenBehavior(false)
99     , m_userStyleSheetEnabled(false)
100     , m_userStyleSheetLocation(adoptWK(WKStringCreateWithUTF8CString("")))
101 {
102     platformInitialize();
103 }
104
105 TestRunner::~TestRunner()
106 {
107 }
108
109 JSClassRef TestRunner::wrapperClass()
110 {
111     return JSTestRunner::testRunnerClass();
112 }
113
114 void TestRunner::display()
115 {
116     WKBundlePageRef page = InjectedBundle::shared().page()->page();
117     WKBundlePageForceRepaint(page);
118     WKBundlePageSetTracksRepaints(page, true);
119     WKBundlePageResetTrackedRepaints(page);
120 }
121
122 void TestRunner::dumpAsText(bool dumpPixels)
123 {
124     if (m_whatToDump < MainFrameText)
125         m_whatToDump = MainFrameText;
126     m_dumpPixels = dumpPixels;
127 }
128
129 void TestRunner::setCustomPolicyDelegate(bool enabled, bool permissive)
130 {
131     m_policyDelegateEnabled = enabled;
132     m_policyDelegatePermissive = permissive;
133
134     InjectedBundle::shared().setCustomPolicyDelegate(enabled, permissive);
135 }
136
137 void TestRunner::waitForPolicyDelegate()
138 {
139     setCustomPolicyDelegate(true);
140     waitUntilDone();
141 }
142
143 void TestRunner::waitUntilDone()
144 {
145     m_waitToDump = true;
146     if (InjectedBundle::shared().useWaitToDumpWatchdogTimer())
147         initializeWaitToDumpWatchdogTimerIfNeeded();
148 }
149
150 void TestRunner::waitToDumpWatchdogTimerFired()
151 {
152     invalidateWaitToDumpWatchdogTimer();
153     InjectedBundle::shared().outputText("FAIL: Timed out waiting for notifyDone to be called\n\n");
154     InjectedBundle::shared().done();
155 }
156
157 void TestRunner::notifyDone()
158 {
159     if (!InjectedBundle::shared().isTestRunning())
160         return;
161
162     if (m_waitToDump && !InjectedBundle::shared().topLoadingFrame())
163         InjectedBundle::shared().page()->dump();
164
165     m_waitToDump = false;
166 }
167
168 unsigned TestRunner::numberOfActiveAnimations() const
169 {
170     // FIXME: Is it OK this works only for the main frame?
171     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
172     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
173     return WKBundleFrameGetNumberOfActiveAnimations(mainFrame);
174 }
175
176 bool TestRunner::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
177 {
178     // FIXME: Is it OK this works only for the main frame?
179     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
180     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
181     return WKBundleFramePauseAnimationOnElementWithId(mainFrame, toWK(animationName).get(), toWK(elementId).get(), time);
182 }
183
184 bool TestRunner::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
185 {
186     // FIXME: Is it OK this works only for the main frame?
187     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
188     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
189     return WKBundleFramePauseTransitionOnElementWithId(mainFrame, toWK(propertyName).get(), toWK(elementId).get(), time);
190 }
191
192 void TestRunner::suspendAnimations()
193 {
194     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
195     WKBundleFrameSuspendAnimations(mainFrame);
196 }
197
198 void TestRunner::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
199 {
200     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
201     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
202
203     WKBundleAddUserScript(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
204         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
205         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
206 }
207
208 void TestRunner::addUserStyleSheet(JSStringRef source, bool allFrames)
209 {
210     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
211     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
212
213     WKBundleAddUserStyleSheet(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
214         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
215 }
216
217 void TestRunner::keepWebHistory()
218 {
219     WKBundleSetShouldTrackVisitedLinks(InjectedBundle::shared().bundle(), true);
220 }
221
222 JSValueRef TestRunner::computedStyleIncludingVisitedInfo(JSValueRef element)
223 {
224     // FIXME: Is it OK this works only for the main frame?
225     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
226     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
227     if (!JSValueIsObject(context, element))
228         return JSValueMakeUndefined(context);
229     JSValueRef value = WKBundleFrameGetComputedStyleIncludingVisitedInfo(mainFrame, const_cast<JSObjectRef>(element));
230     if (!value)
231         return JSValueMakeUndefined(context);
232     return value;
233 }
234
235 JSRetainPtr<JSStringRef> TestRunner::markerTextForListItem(JSValueRef element)
236 {
237     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
238     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
239     if (!element || !JSValueIsObject(context, element))
240         return 0;
241     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyMarkerText(mainFrame, const_cast<JSObjectRef>(element)));
242     if (WKStringIsEmpty(text.get()))
243         return 0;
244     return toJS(text);
245 }
246
247 void TestRunner::execCommand(JSStringRef name, JSStringRef argument)
248 {
249     WKBundlePageExecuteEditingCommand(InjectedBundle::shared().page()->page(), toWK(name).get(), toWK(argument).get());
250 }
251
252 bool TestRunner::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
253 {
254     WKFindOptions options = 0;
255
256     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
257     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
258     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
259     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
260     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
261     if (!JSValueIsNumber(context, lengthValue))
262         return false;
263
264     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
265     for (size_t i = 0; i < length; ++i) {
266         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
267         if (!JSValueIsString(context, value))
268             continue;
269
270         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
271
272         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
273             options |= kWKFindOptionsCaseInsensitive;
274         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
275             options |= kWKFindOptionsAtWordStarts;
276         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
277             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
278         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
279             options |= kWKFindOptionsBackwards;
280         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
281             options |= kWKFindOptionsWrapAround;
282         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
283             // FIXME: No kWKFindOptionsStartInSelection.
284         }
285     }
286
287     return WKBundlePageFindString(InjectedBundle::shared().page()->page(), toWK(target).get(), options);
288 }
289
290 void TestRunner::clearAllDatabases()
291 {
292     WKBundleClearAllDatabases(InjectedBundle::shared().bundle());
293 }
294
295 void TestRunner::setDatabaseQuota(uint64_t quota)
296 {
297     return WKBundleSetDatabaseQuota(InjectedBundle::shared().bundle(), quota);
298 }
299
300 void TestRunner::clearAllApplicationCaches()
301 {
302     WKBundleClearApplicationCache(InjectedBundle::shared().bundle());
303 }
304
305 void TestRunner::clearApplicationCacheForOrigin(JSStringRef origin)
306 {
307     WKBundleClearApplicationCacheForOrigin(InjectedBundle::shared().bundle(), toWK(origin).get());
308 }
309
310 void TestRunner::setAppCacheMaximumSize(uint64_t size)
311 {
312     WKBundleSetAppCacheMaximumSize(InjectedBundle::shared().bundle(), size);
313 }
314
315 long long TestRunner::applicationCacheDiskUsageForOrigin(JSStringRef origin)
316 {
317     return WKBundleGetAppCacheUsageForOrigin(InjectedBundle::shared().bundle(), toWK(origin).get());
318 }
319
320 void TestRunner::setApplicationCacheOriginQuota(unsigned long long bytes)
321 {
322     WKRetainPtr<WKStringRef> origin(AdoptWK, WKStringCreateWithUTF8CString("http://127.0.0.1:8000"));
323     WKBundleSetApplicationCacheOriginQuota(InjectedBundle::shared().bundle(), origin.get(), bytes);
324 }
325
326 void TestRunner::disallowIncreaseForApplicationCacheQuota()
327 {
328     m_disallowIncreaseForApplicationCacheQuota = true;
329 }
330
331 static inline JSValueRef stringArrayToJS(JSContextRef context, WKArrayRef strings)
332 {
333     const size_t count = WKArrayGetSize(strings);
334
335     OwnArrayPtr<JSValueRef> jsStringsArray = adoptArrayPtr(new JSValueRef[count]);
336     for (size_t i = 0; i < count; ++i) {
337         WKStringRef stringRef = static_cast<WKStringRef>(WKArrayGetItemAtIndex(strings, i));
338         JSRetainPtr<JSStringRef> stringJS = toJS(stringRef);
339         jsStringsArray[i] = JSValueMakeString(context, stringJS.get());
340     }
341
342     return JSObjectMakeArray(context, count, jsStringsArray.get(), 0);
343 }
344
345 JSValueRef TestRunner::originsWithApplicationCache()
346 {
347     WKRetainPtr<WKArrayRef> origins(AdoptWK, WKBundleCopyOriginsWithApplicationCache(InjectedBundle::shared().bundle()));
348
349     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
350     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
351
352     return stringArrayToJS(context, origins.get());
353 }
354
355 bool TestRunner::isCommandEnabled(JSStringRef name)
356 {
357     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::shared().page()->page(), toWK(name).get());
358 }
359
360 void TestRunner::setCanOpenWindows(bool)
361 {
362     // It's not clear if or why any tests require opening windows be forbidden.
363     // For now, just ignore this setting, and if we find later it's needed we can add it.
364 }
365
366 void TestRunner::setXSSAuditorEnabled(bool enabled)
367 {
368     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
369     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), key.get(), enabled);
370 }
371
372 void TestRunner::setAllowUniversalAccessFromFileURLs(bool enabled)
373 {
374     WKBundleSetAllowUniversalAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
375 }
376
377 void TestRunner::setAllowFileAccessFromFileURLs(bool enabled)
378 {
379     WKBundleSetAllowFileAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
380 }
381
382 void TestRunner::setFrameFlatteningEnabled(bool enabled)
383 {
384     WKBundleSetFrameFlatteningEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
385 }
386
387 void TestRunner::setPluginsEnabled(bool enabled)
388 {
389     WKBundleSetPluginsEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
390 }
391
392 void TestRunner::setJavaScriptCanAccessClipboard(bool enabled)
393 {
394      WKBundleSetJavaScriptCanAccessClipboard(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
395 }
396
397 void TestRunner::setPrivateBrowsingEnabled(bool enabled)
398 {
399      WKBundleSetPrivateBrowsingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
400 }
401
402 void TestRunner::setPopupBlockingEnabled(bool enabled)
403 {
404      WKBundleSetPopupBlockingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
405 }
406
407 void TestRunner::setAuthorAndUserStylesEnabled(bool enabled)
408 {
409      WKBundleSetAuthorAndUserStylesEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
410 }
411
412 void TestRunner::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
413 {
414     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
415 }
416
417 void TestRunner::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
418 {
419     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
420 }
421
422 bool TestRunner::isPageBoxVisible(int pageIndex)
423 {
424     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
425     return WKBundleIsPageBoxVisible(InjectedBundle::shared().bundle(), mainFrame, pageIndex);
426 }
427
428 void TestRunner::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
429 {
430     if (!element || !JSValueIsObject(context, element))
431         return;
432
433     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
434     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
435 }
436
437 void TestRunner::setAudioData(JSContextRef context, JSValueRef data)
438 {
439     WKRetainPtr<WKDataRef> audioData(AdoptWK, WKBundleCreateWKDataFromUInt8Array(InjectedBundle::shared().bundle(), context, data));
440     InjectedBundle::shared().setAudioResult(audioData.get());
441     m_whatToDump = Audio;
442 }
443
444 unsigned TestRunner::windowCount()
445 {
446     return InjectedBundle::shared().pageCount();
447 }
448
449 void TestRunner::clearBackForwardList()
450 {
451     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::shared().page()->page()));
452 }
453
454 // Object Creation
455
456 void TestRunner::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
457 {
458     setProperty(context, windowObject, "testRunner", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
459 }
460
461 void TestRunner::showWebInspector()
462 {
463 #if ENABLE(INSPECTOR)
464     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
465 #endif // ENABLE(INSPECTOR)
466 }
467
468 void TestRunner::closeWebInspector()
469 {
470 #if ENABLE(INSPECTOR)
471     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
472 #endif // ENABLE(INSPECTOR)
473 }
474
475 void TestRunner::evaluateInWebInspector(long callID, JSStringRef script)
476 {
477 #if ENABLE(INSPECTOR)
478     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
479     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), callID, scriptWK.get());
480 #endif // ENABLE(INSPECTOR)
481 }
482
483 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
484 static WorldMap& worldMap()
485 {
486     static WorldMap& map = *new WorldMap;
487     return map;
488 }
489
490 unsigned TestRunner::worldIDForWorld(WKBundleScriptWorldRef world)
491 {
492     WorldMap::const_iterator end = worldMap().end();
493     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
494         if (it->value == world)
495             return it->key;
496     }
497
498     return 0;
499 }
500
501 void TestRunner::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
502 {
503     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
504     // that is created once and cached forever.
505     WKRetainPtr<WKBundleScriptWorldRef> world;
506     if (!worldID)
507         world.adopt(WKBundleScriptWorldCreateWorld());
508     else {
509         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, 0).iterator->value;
510         if (!worldSlot)
511             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
512         world = worldSlot;
513     }
514
515     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
516     if (!frame)
517         frame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
518
519     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
520     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
521 }
522
523 void TestRunner::setPOSIXLocale(JSStringRef locale)
524 {
525     char localeBuf[32];
526     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
527     setlocale(LC_ALL, localeBuf);
528 }
529
530 void TestRunner::setTextDirection(JSStringRef direction)
531 {
532     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
533     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
534 }
535     
536 void TestRunner::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
537 {
538     InjectedBundle::shared().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
539 }
540
541 void TestRunner::setDefersLoading(bool shouldDeferLoading)
542 {
543     WKBundlePageSetDefersLoading(InjectedBundle::shared().page()->page(), shouldDeferLoading);
544 }
545
546 void TestRunner::setPageVisibility(JSStringRef state)
547 {
548     WebCore::PageVisibilityState visibilityState = WebCore::PageVisibilityStateVisible;
549
550     if (JSStringIsEqualToUTF8CString(state, "hidden"))
551         visibilityState = WebCore::PageVisibilityStateHidden;
552     else if (JSStringIsEqualToUTF8CString(state, "prerender"))
553         visibilityState = WebCore::PageVisibilityStatePrerender;
554     else if (JSStringIsEqualToUTF8CString(state, "preview"))
555         visibilityState = WebCore::PageVisibilityStatePreview;
556
557     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), visibilityState, /* isInitialState */ false);
558 }
559
560 void TestRunner::resetPageVisibility()
561 {
562     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), WebCore::PageVisibilityStateVisible, /* isInitialState */ true);
563 }
564
565 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
566 static CallbackMap& callbackMap()
567 {
568     static CallbackMap& map = *new CallbackMap;
569     return map;
570 }
571
572 enum {
573     AddChromeInputFieldCallbackID = 1,
574     RemoveChromeInputFieldCallbackID,
575     FocusWebViewCallbackID,
576     SetBackingScaleFactorCallbackID
577 };
578
579 static void cacheTestRunnerCallback(unsigned index, JSValueRef callback)
580 {
581     if (!callback)
582         return;
583
584     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
585     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
586     JSValueProtect(context, callback);
587     callbackMap().add(index, callback);
588 }
589
590 static void callTestRunnerCallback(unsigned index)
591 {
592     if (!callbackMap().contains(index))
593         return;
594     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
595     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
596     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
597     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
598     JSValueUnprotect(context, callback);
599 }
600
601 unsigned TestRunner::workerThreadCount()
602 {
603     return WKBundleGetWorkerThreadCount(InjectedBundle::shared().bundle());
604 }
605
606 void TestRunner::addChromeInputField(JSValueRef callback)
607 {
608     cacheTestRunnerCallback(AddChromeInputFieldCallbackID, callback);
609     InjectedBundle::shared().postAddChromeInputField();
610 }
611
612 void TestRunner::removeChromeInputField(JSValueRef callback)
613 {
614     cacheTestRunnerCallback(RemoveChromeInputFieldCallbackID, callback);
615     InjectedBundle::shared().postRemoveChromeInputField();
616 }
617
618 void TestRunner::focusWebView(JSValueRef callback)
619 {
620     cacheTestRunnerCallback(FocusWebViewCallbackID, callback);
621     InjectedBundle::shared().postFocusWebView();
622 }
623
624 void TestRunner::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
625 {
626     cacheTestRunnerCallback(SetBackingScaleFactorCallbackID, callback);
627     InjectedBundle::shared().postSetBackingScaleFactor(backingScaleFactor);
628 }
629
630 void TestRunner::setWindowIsKey(bool isKey)
631 {
632     InjectedBundle::shared().postSetWindowIsKey(isKey);
633 }
634
635 void TestRunner::callAddChromeInputFieldCallback()
636 {
637     callTestRunnerCallback(AddChromeInputFieldCallbackID);
638 }
639
640 void TestRunner::callRemoveChromeInputFieldCallback()
641 {
642     callTestRunnerCallback(RemoveChromeInputFieldCallbackID);
643 }
644
645 void TestRunner::callFocusWebViewCallback()
646 {
647     callTestRunnerCallback(FocusWebViewCallbackID);
648 }
649
650 void TestRunner::callSetBackingScaleFactorCallback()
651 {
652     callTestRunnerCallback(SetBackingScaleFactorCallbackID);
653 }
654
655 static inline bool toBool(JSStringRef value)
656 {
657     return JSStringIsEqualToUTF8CString(value, "true") || JSStringIsEqualToUTF8CString(value, "1");
658 }
659
660 void TestRunner::overridePreference(JSStringRef preference, JSStringRef value)
661 {
662     // FIXME: handle non-boolean preferences.
663     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), toWK(preference).get(), toBool(value));
664 }
665
666 void TestRunner::sendWebIntentResponse(JSStringRef reply)
667 {
668 #if ENABLE(WEB_INTENTS)
669     WKRetainPtr<WKBundleIntentRequestRef> currentRequest = InjectedBundle::shared().page()->currentIntentRequest();
670     if (!currentRequest)
671         return;
672
673     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
674     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
675
676     if (reply) {
677         WKRetainPtr<WKSerializedScriptValueRef> serializedData(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, reply), 0));
678         WKBundleIntentRequestPostResult(currentRequest.get(), serializedData.get());
679     } else {
680         JSRetainPtr<JSStringRef> errorReply(JSStringCreateWithUTF8CString("ERROR"));
681         WKRetainPtr<WKSerializedScriptValueRef> serializedData(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, errorReply.get()), 0));
682         WKBundleIntentRequestPostFailure(currentRequest.get(), serializedData.get());
683     }
684 #endif
685 }
686
687 void TestRunner::deliverWebIntent(JSStringRef action, JSStringRef type, JSStringRef data)
688 {
689 #if ENABLE(WEB_INTENTS)
690     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
691     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
692
693     WKRetainPtr<WKStringRef> actionWK = toWK(action);
694     WKRetainPtr<WKStringRef> typeWK = toWK(type);
695     WKRetainPtr<WKSerializedScriptValueRef> dataWK(AdoptWK, WKSerializedScriptValueCreate(context, JSValueMakeString(context, data), 0));
696
697     WKRetainPtr<WKMutableDictionaryRef> intentInitDict(AdoptWK, WKMutableDictionaryCreate());
698     WKRetainPtr<WKStringRef> actionKey(AdoptWK, WKStringCreateWithUTF8CString("action"));
699     WKDictionaryAddItem(intentInitDict.get(), actionKey.get(), actionWK.get());
700
701     WKRetainPtr<WKStringRef> typeKey(AdoptWK, WKStringCreateWithUTF8CString("type"));
702     WKDictionaryAddItem(intentInitDict.get(), typeKey.get(), typeWK.get());
703
704     WKRetainPtr<WKStringRef> dataKey(AdoptWK, WKStringCreateWithUTF8CString("data"));
705     WKDictionaryAddItem(intentInitDict.get(), dataKey.get(), dataWK.get());
706
707     WKRetainPtr<WKBundleIntentRef> wkIntent(AdoptWK, WKBundleIntentCreate(intentInitDict.get()));
708     WKBundlePageDeliverIntentToFrame(InjectedBundle::shared().page()->page(), mainFrame, wkIntent.get());
709 #endif
710 }
711
712 void TestRunner::setAlwaysAcceptCookies(bool accept)
713 {
714     WKBundleSetAlwaysAcceptCookies(InjectedBundle::shared().bundle(), accept);
715 }
716
717 double TestRunner::preciseTime()
718 {
719     return currentTime();
720 }
721
722 void TestRunner::setUserStyleSheetEnabled(bool enabled)
723 {
724     m_userStyleSheetEnabled = enabled;
725
726     WKRetainPtr<WKStringRef> emptyUrl = adoptWK(WKStringCreateWithUTF8CString(""));
727     WKStringRef location = enabled ? m_userStyleSheetLocation.get() : emptyUrl.get();
728     WKBundleSetUserStyleSheetLocation(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), location);
729 }
730
731 void TestRunner::setUserStyleSheetLocation(JSStringRef location)
732 {
733     m_userStyleSheetLocation = adoptWK(WKStringCreateWithJSString(location));
734
735     if (m_userStyleSheetEnabled)
736         setUserStyleSheetEnabled(true);
737 }
738
739 void TestRunner::setMinimumTimerInterval(double seconds)
740 {
741     WKBundleSetMinimumTimerInterval(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), seconds);
742 }
743
744 void TestRunner::setSpatialNavigationEnabled(bool enabled)
745 {
746     WKBundleSetSpatialNavigationEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
747 }
748
749 void TestRunner::setTabKeyCyclesThroughElements(bool enabled)
750 {
751     WKBundleSetTabKeyCyclesThroughElements(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), enabled);
752 }
753
754 void TestRunner::setSerializeHTTPLoads()
755 {
756     WKBundleSetSerialLoadingEnabled(InjectedBundle::shared().bundle(), true);
757 }
758
759 void TestRunner::dispatchPendingLoadRequests()
760 {
761     WKBundleDispatchPendingLoadRequests(InjectedBundle::shared().bundle());
762 }
763
764 void TestRunner::setCacheModel(int model)
765 {
766     WKBundleSetCacheModel(InjectedBundle::shared().bundle(), model);
767 }
768
769 void TestRunner::grantWebNotificationPermission(JSStringRef origin)
770 {
771     WKRetainPtr<WKStringRef> originWK = toWK(origin);
772     WKBundleSetWebNotificationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), originWK.get(), true);
773 }
774
775 void TestRunner::denyWebNotificationPermission(JSStringRef origin)
776 {
777     WKRetainPtr<WKStringRef> originWK = toWK(origin);
778     WKBundleSetWebNotificationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page(), originWK.get(), false);
779 }
780
781 void TestRunner::removeAllWebNotificationPermissions()
782 {
783     WKBundleRemoveAllWebNotificationPermissions(InjectedBundle::shared().bundle(), InjectedBundle::shared().page()->page());
784 }
785
786 void TestRunner::simulateWebNotificationClick(JSValueRef notification)
787 {
788     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
789     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
790     uint64_t notificationID = WKBundleGetWebNotificationID(InjectedBundle::shared().bundle(), context, notification);
791     InjectedBundle::shared().postSimulateWebNotificationClick(notificationID);
792 }
793
794 void TestRunner::setGeolocationPermission(bool enabled)
795 {
796     // FIXME: this should be done by frame.
797     InjectedBundle::shared().setGeolocationPermission(enabled);
798 }
799
800 void TestRunner::setMockGeolocationPosition(double latitude, double longitude, double accuracy, JSValueRef jsAltitude, JSValueRef jsAltitudeAccuracy, JSValueRef jsHeading, JSValueRef jsSpeed)
801 {
802     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
803     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
804
805     bool providesAltitude = false;
806     double altitude = 0.;
807     if (!JSValueIsUndefined(context, jsAltitude)) {
808         providesAltitude = true;
809         altitude = JSValueToNumber(context, jsAltitude, 0);
810     }
811
812     bool providesAltitudeAccuracy = false;
813     double altitudeAccuracy = 0.;
814     if (!JSValueIsUndefined(context, jsAltitudeAccuracy)) {
815         providesAltitudeAccuracy = true;
816         altitudeAccuracy = JSValueToNumber(context, jsAltitudeAccuracy, 0);
817     }
818
819     bool providesHeading = false;
820     double heading = 0.;
821     if (!JSValueIsUndefined(context, jsHeading)) {
822         providesHeading = true;
823         heading = JSValueToNumber(context, jsHeading, 0);
824     }
825
826     bool providesSpeed = false;
827     double speed = 0.;
828     if (!JSValueIsUndefined(context, jsSpeed)) {
829         providesSpeed = true;
830         speed = JSValueToNumber(context, jsSpeed, 0);
831     }
832
833     InjectedBundle::shared().setMockGeolocationPosition(latitude, longitude, accuracy, providesAltitude, altitude, providesAltitudeAccuracy, altitudeAccuracy, providesHeading, heading, providesSpeed, speed);
834 }
835
836 void TestRunner::setMockGeolocationPositionUnavailableError(JSStringRef message)
837 {
838     WKRetainPtr<WKStringRef> messageWK = toWK(message);
839     InjectedBundle::shared().setMockGeolocationPositionUnavailableError(messageWK.get());
840 }
841
842 bool TestRunner::callShouldCloseOnWebView()
843 {
844     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
845     return WKBundleFrameCallShouldCloseOnWebView(mainFrame);
846 }
847
848 void TestRunner::queueBackNavigation(unsigned howFarBackward)
849 {
850     InjectedBundle::shared().queueBackNavigation(howFarBackward);
851 }
852
853 void TestRunner::queueForwardNavigation(unsigned howFarForward)
854 {
855     InjectedBundle::shared().queueForwardNavigation(howFarForward);
856 }
857
858 void TestRunner::queueLoad(JSStringRef url, JSStringRef target)
859 {
860     WKRetainPtr<WKURLRef> baseURLWK(AdoptWK, WKBundleFrameCopyURL(WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page())));
861     WKRetainPtr<WKURLRef> urlWK(AdoptWK, WKURLCreateWithBaseURL(baseURLWK.get(), toWTFString(toWK(url)).utf8().data()));
862     WKRetainPtr<WKStringRef> urlStringWK(AdoptWK, WKURLCopyString(urlWK.get()));
863
864     InjectedBundle::shared().queueLoad(urlStringWK.get(), toWK(target).get());
865 }
866
867 void TestRunner::queueLoadHTMLString(JSStringRef content, JSStringRef baseURL, JSStringRef unreachableURL)
868 {
869     WKRetainPtr<WKStringRef> contentWK = toWK(content);
870     WKRetainPtr<WKStringRef> baseURLWK = baseURL ? toWK(baseURL) : WKRetainPtr<WKStringRef>();
871     WKRetainPtr<WKStringRef> unreachableURLWK = unreachableURL ? toWK(unreachableURL) : WKRetainPtr<WKStringRef>();
872
873     InjectedBundle::shared().queueLoadHTMLString(contentWK.get(), baseURLWK.get(), unreachableURLWK.get());
874 }
875
876 void TestRunner::queueReload()
877 {
878     InjectedBundle::shared().queueReload();
879 }
880
881 void TestRunner::queueLoadingScript(JSStringRef script)
882 {
883     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
884     InjectedBundle::shared().queueLoadingScript(scriptWK.get());
885 }
886
887 void TestRunner::queueNonLoadingScript(JSStringRef script)
888 {
889     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
890     InjectedBundle::shared().queueNonLoadingScript(scriptWK.get());
891 }
892
893 void TestRunner::setViewModeMediaFeature(JSStringRef mode)
894 {
895     WKRetainPtr<WKStringRef> modeWK = toWK(mode);
896     WKBundlePageSetViewMode(InjectedBundle::shared().page()->page(), modeWK.get());
897 }
898
899 } // namespace WTR