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