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