709988b7c4df4c8a0443141f3bdd7c749fdb4bbc
[WebKit-https.git] / Tools / WebKitTestRunner / InjectedBundle / LayoutTestController.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 "LayoutTestController.h"
28
29 #include "InjectedBundle.h"
30 #include "InjectedBundlePage.h"
31 #include "JSLayoutTestController.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/WKRetainPtr.h>
45 #include <WebKit2/WebKit2.h>
46 #include <wtf/HashMap.h>
47 #include <wtf/text/StringBuilder.h>
48
49 namespace WTR {
50
51 // This is lower than DumpRenderTree's timeout, to make it easier to work through the failures
52 // Eventually it should be changed to match.
53 const double LayoutTestController::waitToDumpWatchdogTimerInterval = 6;
54
55 static JSValueRef propertyValue(JSContextRef context, JSObjectRef object, const char* propertyName)
56 {
57     if (!object)
58         return 0;
59     JSRetainPtr<JSStringRef> propertyNameString(Adopt, JSStringCreateWithUTF8CString(propertyName));
60     JSValueRef exception;
61     return JSObjectGetProperty(context, object, propertyNameString.get(), &exception);
62 }
63
64 static JSObjectRef propertyObject(JSContextRef context, JSObjectRef object, const char* propertyName)
65 {
66     JSValueRef value = propertyValue(context, object, propertyName);
67     if (!value || !JSValueIsObject(context, value))
68         return 0;
69     return const_cast<JSObjectRef>(value);
70 }
71
72 static JSObjectRef getElementById(WKBundleFrameRef frame, JSStringRef elementId)
73 {
74     JSContextRef context = WKBundleFrameGetJavaScriptContext(frame);
75     JSObjectRef document = propertyObject(context, JSContextGetGlobalObject(context), "document");
76     if (!document)
77         return 0;
78     JSValueRef getElementById = propertyObject(context, document, "getElementById");
79     if (!getElementById || !JSValueIsObject(context, getElementById))
80         return 0;
81     JSValueRef elementIdValue = JSValueMakeString(context, elementId);
82     JSValueRef exception;
83     JSValueRef element = JSObjectCallAsFunction(context, const_cast<JSObjectRef>(getElementById), document, 1, &elementIdValue, &exception);
84     if (!element || !JSValueIsObject(context, element))
85         return 0;
86     return const_cast<JSObjectRef>(element);
87 }
88
89 PassRefPtr<LayoutTestController> LayoutTestController::create()
90 {
91     return adoptRef(new LayoutTestController);
92 }
93
94 LayoutTestController::LayoutTestController()
95     : m_whatToDump(RenderTree)
96     , m_shouldDumpAllFrameScrollPositions(false)
97     , m_shouldDumpBackForwardListsForAllWindows(false)
98     , m_shouldAllowEditing(true)
99     , m_shouldCloseExtraWindows(false)
100     , m_dumpEditingCallbacks(false)
101     , m_dumpStatusCallbacks(false)
102     , m_dumpTitleChanges(false)
103     , m_dumpPixels(true)
104     , m_dumpFullScreenCallbacks(false)
105     , m_dumpFrameLoadCallbacks(false)
106     , m_waitToDump(false)
107     , m_testRepaint(false)
108     , m_testRepaintSweepHorizontally(false)
109     , m_willSendRequestReturnsNull(false)
110     , m_policyDelegateEnabled(false)
111     , m_policyDelegatePermissive(false)
112     , m_globalFlag(false)
113     , m_customFullScreenBehavior(false)
114 {
115     platformInitialize();
116 }
117
118 LayoutTestController::~LayoutTestController()
119 {
120 }
121
122 JSClassRef LayoutTestController::wrapperClass()
123 {
124     return JSLayoutTestController::layoutTestControllerClass();
125 }
126
127 void LayoutTestController::display()
128 {
129     WKBundlePageRef page = InjectedBundle::shared().page()->page();
130     WKBundlePageForceRepaint(page);
131     WKBundlePageSetTracksRepaints(page, true);
132     WKBundlePageResetTrackedRepaints(page);
133 }
134
135 void LayoutTestController::dumpAsText(bool dumpPixels)
136 {
137     if (m_whatToDump < MainFrameText)
138         m_whatToDump = MainFrameText;
139     m_dumpPixels = dumpPixels;
140 }
141
142 // FIXME: Needs a full implementation see https://bugs.webkit.org/show_bug.cgi?id=42546
143 void LayoutTestController::setCustomPolicyDelegate(bool enabled, bool permissive)
144 {
145     m_policyDelegateEnabled = enabled;
146     m_policyDelegatePermissive = permissive;
147 }
148
149 void LayoutTestController::waitForPolicyDelegate()
150 {
151     setCustomPolicyDelegate(true);
152     waitUntilDone();
153 }
154
155 void LayoutTestController::waitUntilDone()
156 {
157     m_waitToDump = true;
158     if (InjectedBundle::shared().useWaitToDumpWatchdogTimer())
159         initializeWaitToDumpWatchdogTimerIfNeeded();
160 }
161
162 void LayoutTestController::waitToDumpWatchdogTimerFired()
163 {
164     invalidateWaitToDumpWatchdogTimer();
165     const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
166     InjectedBundle::shared().stringBuilder()->append(message);
167     InjectedBundle::shared().stringBuilder()->append("\n");
168     InjectedBundle::shared().done();
169 }
170
171 void LayoutTestController::notifyDone()
172 {
173     if (!InjectedBundle::shared().isTestRunning())
174         return;
175
176     if (m_waitToDump && !InjectedBundle::shared().topLoadingFrame())
177         InjectedBundle::shared().page()->dump();
178
179     m_waitToDump = false;
180 }
181
182 unsigned LayoutTestController::numberOfActiveAnimations() const
183 {
184     // FIXME: Is it OK this works only for the main frame?
185     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
186     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
187     return WKBundleFrameGetNumberOfActiveAnimations(mainFrame);
188 }
189
190 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(JSStringRef animationName, double time, JSStringRef elementId)
191 {
192     // FIXME: Is it OK this works only for the main frame?
193     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
194     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
195     return WKBundleFramePauseAnimationOnElementWithId(mainFrame, toWK(animationName).get(), toWK(elementId).get(), time);
196 }
197
198 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(JSStringRef propertyName, double time, JSStringRef elementId)
199 {
200     // FIXME: Is it OK this works only for the main frame?
201     // FIXME: If this is needed only for the main frame, then why is the function on WKBundleFrame instead of WKBundlePage?
202     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
203     return WKBundleFramePauseTransitionOnElementWithId(mainFrame, toWK(propertyName).get(), toWK(elementId).get(), time);
204 }
205
206 void LayoutTestController::suspendAnimations()
207 {
208     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
209     WKBundleFrameSuspendAnimations(mainFrame);
210 }
211
212 void LayoutTestController::resumeAnimations()
213 {
214     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
215     WKBundleFrameResumeAnimations(mainFrame);
216 }
217
218 JSRetainPtr<JSStringRef> LayoutTestController::layerTreeAsText() const
219 {
220     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
221     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyLayerTreeAsText(mainFrame));
222     return toJS(text);
223 }
224
225 void LayoutTestController::addUserScript(JSStringRef source, bool runAtStart, bool allFrames)
226 {
227     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
228     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
229
230     WKBundleAddUserScript(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
231         (runAtStart ? kWKInjectAtDocumentStart : kWKInjectAtDocumentEnd),
232         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
233 }
234
235 void LayoutTestController::addUserStyleSheet(JSStringRef source, bool allFrames)
236 {
237     WKRetainPtr<WKStringRef> sourceWK = toWK(source);
238     WKRetainPtr<WKBundleScriptWorldRef> scriptWorld(AdoptWK, WKBundleScriptWorldCreateWorld());
239
240     WKBundleAddUserStyleSheet(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), scriptWorld.get(), sourceWK.get(), 0, 0, 0,
241         (allFrames ? kWKInjectInAllFrames : kWKInjectInTopFrameOnly));
242 }
243
244 void LayoutTestController::keepWebHistory()
245 {
246     WKBundleSetShouldTrackVisitedLinks(InjectedBundle::shared().bundle(), true);
247 }
248
249 JSValueRef LayoutTestController::computedStyleIncludingVisitedInfo(JSValueRef element)
250 {
251     // FIXME: Is it OK this works only for the main frame?
252     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
253     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
254     if (!JSValueIsObject(context, element))
255         return JSValueMakeUndefined(context);
256     JSValueRef value = WKBundleFrameGetComputedStyleIncludingVisitedInfo(mainFrame, const_cast<JSObjectRef>(element));
257     if (!value)
258         return JSValueMakeUndefined(context);
259     return value;
260 }
261
262 JSRetainPtr<JSStringRef> LayoutTestController::counterValueForElementById(JSStringRef elementId)
263 {
264     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
265     JSObjectRef element = getElementById(mainFrame, elementId);
266     if (!element)
267         return 0;
268     WKRetainPtr<WKStringRef> value(AdoptWK, WKBundleFrameCopyCounterValue(mainFrame, const_cast<JSObjectRef>(element)));
269     return toJS(value);
270 }
271
272 JSRetainPtr<JSStringRef> LayoutTestController::markerTextForListItem(JSValueRef element)
273 {
274     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
275     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
276     if (!element || !JSValueIsObject(context, element))
277         return 0;
278     WKRetainPtr<WKStringRef> text(AdoptWK, WKBundleFrameCopyMarkerText(mainFrame, const_cast<JSObjectRef>(element)));
279     if (WKStringIsEmpty(text.get()))
280         return 0;
281     return toJS(text);
282 }
283
284 void LayoutTestController::execCommand(JSStringRef name, JSStringRef argument)
285 {
286     WKBundlePageExecuteEditingCommand(InjectedBundle::shared().page()->page(), toWK(name).get(), toWK(argument).get());
287 }
288
289 bool LayoutTestController::findString(JSStringRef target, JSValueRef optionsArrayAsValue)
290 {
291     WKFindOptions options = 0;
292
293     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
294     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
295     JSRetainPtr<JSStringRef> lengthPropertyName(Adopt, JSStringCreateWithUTF8CString("length"));
296     JSObjectRef optionsArray = JSValueToObject(context, optionsArrayAsValue, 0);
297     JSValueRef lengthValue = JSObjectGetProperty(context, optionsArray, lengthPropertyName.get(), 0);
298     if (!JSValueIsNumber(context, lengthValue))
299         return false;
300
301     size_t length = static_cast<size_t>(JSValueToNumber(context, lengthValue, 0));
302     for (size_t i = 0; i < length; ++i) {
303         JSValueRef value = JSObjectGetPropertyAtIndex(context, optionsArray, i, 0);
304         if (!JSValueIsString(context, value))
305             continue;
306
307         JSRetainPtr<JSStringRef> optionName(Adopt, JSValueToStringCopy(context, value, 0));
308
309         if (JSStringIsEqualToUTF8CString(optionName.get(), "CaseInsensitive"))
310             options |= kWKFindOptionsCaseInsensitive;
311         else if (JSStringIsEqualToUTF8CString(optionName.get(), "AtWordStarts"))
312             options |= kWKFindOptionsAtWordStarts;
313         else if (JSStringIsEqualToUTF8CString(optionName.get(), "TreatMedialCapitalAsWordStart"))
314             options |= kWKFindOptionsTreatMedialCapitalAsWordStart;
315         else if (JSStringIsEqualToUTF8CString(optionName.get(), "Backwards"))
316             options |= kWKFindOptionsBackwards;
317         else if (JSStringIsEqualToUTF8CString(optionName.get(), "WrapAround"))
318             options |= kWKFindOptionsWrapAround;
319         else if (JSStringIsEqualToUTF8CString(optionName.get(), "StartInSelection")) {
320             // FIXME: No kWKFindOptionsStartInSelection.
321         }
322     }
323
324     return WKBundlePageFindString(InjectedBundle::shared().page()->page(), toWK(target).get(), options);
325 }
326
327 void LayoutTestController::clearAllDatabases()
328 {
329     WKBundleClearAllDatabases(InjectedBundle::shared().bundle());
330 }
331
332 void LayoutTestController::setDatabaseQuota(uint64_t quota)
333 {
334     return WKBundleSetDatabaseQuota(InjectedBundle::shared().bundle(), quota);
335 }
336
337 void LayoutTestController::clearAllApplicationCaches()
338 {
339     WKBundleClearApplicationCache(InjectedBundle::shared().bundle());
340 }
341
342 void LayoutTestController::setAppCacheMaximumSize(uint64_t size)
343 {
344     WKBundleSetAppCacheMaximumSize(InjectedBundle::shared().bundle(), size);
345 }
346
347 bool LayoutTestController::isCommandEnabled(JSStringRef name)
348 {
349     return WKBundlePageIsEditingCommandEnabled(InjectedBundle::shared().page()->page(), toWK(name).get());
350 }
351
352 void LayoutTestController::setCanOpenWindows(bool)
353 {
354     // It's not clear if or why any tests require opening windows be forbidden.
355     // For now, just ignore this setting, and if we find later it's needed we can add it.
356 }
357
358 void LayoutTestController::setXSSAuditorEnabled(bool enabled)
359 {
360     WKRetainPtr<WKStringRef> key(AdoptWK, WKStringCreateWithUTF8CString("WebKitXSSAuditorEnabled"));
361     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), key.get(), enabled);
362 }
363
364 void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
365 {
366     WKBundleSetAllowUniversalAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
367 }
368
369 void LayoutTestController::setAllowFileAccessFromFileURLs(bool enabled)
370 {
371     WKBundleSetAllowFileAccessFromFileURLs(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
372 }
373
374 void LayoutTestController::setFrameFlatteningEnabled(bool enabled)
375 {
376     WKBundleSetFrameFlatteningEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
377 }
378
379 void LayoutTestController::setGeolocationPermission(bool enabled)
380 {
381     WKBundleSetGeolocationPermission(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
382 }
383
384 void LayoutTestController::setJavaScriptCanAccessClipboard(bool enabled)
385 {
386      WKBundleSetJavaScriptCanAccessClipboard(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
387 }
388
389 void LayoutTestController::setPrivateBrowsingEnabled(bool enabled)
390 {
391      WKBundleSetPrivateBrowsingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
392 }
393
394 void LayoutTestController::setPopupBlockingEnabled(bool enabled)
395 {
396      WKBundleSetPopupBlockingEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
397 }
398
399 void LayoutTestController::setAuthorAndUserStylesEnabled(bool enabled)
400 {
401      WKBundleSetAuthorAndUserStylesEnabled(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), enabled);
402 }
403
404 void LayoutTestController::addOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
405 {
406     WKBundleAddOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
407 }
408
409 void LayoutTestController::removeOriginAccessWhitelistEntry(JSStringRef sourceOrigin, JSStringRef destinationProtocol, JSStringRef destinationHost, bool allowDestinationSubdomains)
410 {
411     WKBundleRemoveOriginAccessWhitelistEntry(InjectedBundle::shared().bundle(), toWK(sourceOrigin).get(), toWK(destinationProtocol).get(), toWK(destinationHost).get(), allowDestinationSubdomains);
412 }
413
414 int LayoutTestController::numberOfPages(double pageWidthInPixels, double pageHeightInPixels)
415 {
416     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
417     return WKBundleNumberOfPages(InjectedBundle::shared().bundle(), mainFrame, pageWidthInPixels, pageHeightInPixels);
418 }
419
420 int LayoutTestController::pageNumberForElementById(JSStringRef id, double pageWidthInPixels, double pageHeightInPixels)
421 {
422     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
423     return WKBundlePageNumberForElementById(InjectedBundle::shared().bundle(), mainFrame, toWK(id).get(), pageWidthInPixels, pageHeightInPixels);
424 }
425
426 JSRetainPtr<JSStringRef> LayoutTestController::pageSizeAndMarginsInPixels(int pageIndex, int width, int height, int marginTop, int marginRight, int marginBottom, int marginLeft)
427 {
428     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
429     return toJS(WKBundlePageSizeAndMarginsInPixels(InjectedBundle::shared().bundle(), mainFrame, pageIndex, width, height, marginTop, marginRight, marginBottom, marginLeft));
430 }
431
432 bool LayoutTestController::isPageBoxVisible(int pageIndex)
433 {
434     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
435     return WKBundleIsPageBoxVisible(InjectedBundle::shared().bundle(), mainFrame, pageIndex);
436 }
437
438 void LayoutTestController::setValueForUser(JSContextRef context, JSValueRef element, JSStringRef value)
439 {
440     if (!element || !JSValueIsObject(context, element))
441         return;
442
443     WKRetainPtr<WKBundleNodeHandleRef> nodeHandle(AdoptWK, WKBundleNodeHandleCreate(context, const_cast<JSObjectRef>(element)));
444     WKBundleNodeHandleSetHTMLInputElementValueForUser(nodeHandle.get(), toWK(value).get());
445 }
446
447 unsigned LayoutTestController::windowCount()
448 {
449     return InjectedBundle::shared().pageCount();
450 }
451
452 void LayoutTestController::clearBackForwardList()
453 {
454     WKBundleBackForwardListClear(WKBundlePageGetBackForwardList(InjectedBundle::shared().page()->page()));
455 }
456
457 // Object Creation
458
459 void LayoutTestController::makeWindowObject(JSContextRef context, JSObjectRef windowObject, JSValueRef* exception)
460 {
461     setProperty(context, windowObject, "layoutTestController", this, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, exception);
462 }
463
464 void LayoutTestController::showWebInspector()
465 {
466 #if ENABLE(INSPECTOR)
467     WKBundleInspectorShow(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
468 #endif // ENABLE(INSPECTOR)
469 }
470
471 void LayoutTestController::closeWebInspector()
472 {
473 #if ENABLE(INSPECTOR)
474     WKBundleInspectorClose(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()));
475 #endif // ENABLE(INSPECTOR)
476 }
477
478 void LayoutTestController::evaluateInWebInspector(long callID, JSStringRef script)
479 {
480 #if ENABLE(INSPECTOR)
481     WKRetainPtr<WKStringRef> scriptWK = toWK(script);
482     WKBundleInspectorEvaluateScriptForTest(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), callID, scriptWK.get());
483 #endif // ENABLE(INSPECTOR)
484 }
485
486 void LayoutTestController::setJavaScriptProfilingEnabled(bool enabled)
487 {
488 #if ENABLE(INSPECTOR)
489     WKBundleInspectorSetJavaScriptProfilingEnabled(WKBundlePageGetInspector(InjectedBundle::shared().page()->page()), enabled);
490 #endif // ENABLE(INSPECTOR)
491 }
492
493 typedef WTF::HashMap<unsigned, WKRetainPtr<WKBundleScriptWorldRef> > WorldMap;
494 static WorldMap& worldMap()
495 {
496     static WorldMap& map = *new WorldMap;
497     return map;
498 }
499
500 unsigned LayoutTestController::worldIDForWorld(WKBundleScriptWorldRef world)
501 {
502     WorldMap::const_iterator end = worldMap().end();
503     for (WorldMap::const_iterator it = worldMap().begin(); it != end; ++it) {
504         if (it->second == world)
505             return it->first;
506     }
507
508     return 0;
509 }
510
511 void LayoutTestController::evaluateScriptInIsolatedWorld(JSContextRef context, unsigned worldID, JSStringRef script)
512 {
513     // A worldID of 0 always corresponds to a new world. Any other worldID corresponds to a world
514     // that is created once and cached forever.
515     WKRetainPtr<WKBundleScriptWorldRef> world;
516     if (!worldID)
517         world.adopt(WKBundleScriptWorldCreateWorld());
518     else {
519         WKRetainPtr<WKBundleScriptWorldRef>& worldSlot = worldMap().add(worldID, 0).iterator->second;
520         if (!worldSlot)
521             worldSlot.adopt(WKBundleScriptWorldCreateWorld());
522         world = worldSlot;
523     }
524
525     WKBundleFrameRef frame = WKBundleFrameForJavaScriptContext(context);
526     if (!frame)
527         frame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
528
529     JSGlobalContextRef jsContext = WKBundleFrameGetJavaScriptContextForWorld(frame, world.get());
530     JSEvaluateScript(jsContext, script, 0, 0, 0, 0); 
531 }
532
533 void LayoutTestController::setPOSIXLocale(JSStringRef locale)
534 {
535     char localeBuf[32];
536     JSStringGetUTF8CString(locale, localeBuf, sizeof(localeBuf));
537     setlocale(LC_ALL, localeBuf);
538 }
539
540 void LayoutTestController::setTextDirection(JSStringRef direction)
541 {
542     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
543     return WKBundleFrameSetTextDirection(mainFrame, toWK(direction).get());
544 }
545     
546 void LayoutTestController::setShouldStayOnPageAfterHandlingBeforeUnload(bool shouldStayOnPage)
547 {
548     InjectedBundle::shared().postNewBeforeUnloadReturnValue(!shouldStayOnPage);
549 }
550
551 void LayoutTestController::setPageVisibility(JSStringRef state)
552 {
553     WKStringRef visibilityStateKey = toWK(state).get();
554     WebCore::PageVisibilityState visibilityState = WebCore::PageVisibilityStateVisible;
555
556     if (WKStringIsEqualToUTF8CString(visibilityStateKey, "hidden"))
557         visibilityState = WebCore::PageVisibilityStateHidden;
558     else if (WKStringIsEqualToUTF8CString(visibilityStateKey, "prerender"))
559         visibilityState = WebCore::PageVisibilityStatePrerender;
560
561     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), visibilityState, /* isInitialState */ false);
562 }
563
564 void LayoutTestController::resetPageVisibility()
565 {
566     WKBundleSetPageVisibilityState(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), WebCore::PageVisibilityStateVisible, /* isInitialState */ true);
567 }
568
569 void LayoutTestController::dumpConfigurationForViewport(int deviceDPI, int deviceWidth, int deviceHeight, int availableWidth, int availableHeight)
570 {
571     InjectedBundle::shared().stringBuilder()->append(toWTFString(adoptWK(WKBundlePageViewportConfigurationAsText(InjectedBundle::shared().page()->page(), deviceDPI, deviceWidth, deviceHeight, availableWidth, availableHeight))));
572 }
573
574 typedef WTF::HashMap<unsigned, JSValueRef> CallbackMap;
575 static CallbackMap& callbackMap()
576 {
577     static CallbackMap& map = *new CallbackMap;
578     return map;
579 }
580
581 enum {
582     AddChromeInputFieldCallbackID = 1,
583     RemoveChromeInputFieldCallbackID,
584     FocusWebViewCallbackID,
585     SetBackingScaleFactorCallbackID
586 };
587
588 static void cacheLayoutTestControllerCallback(unsigned index, JSValueRef callback)
589 {
590     if (!callback)
591         return;
592
593     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
594     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
595     JSValueProtect(context, callback);
596     callbackMap().add(index, callback);
597 }
598
599 static void callLayoutTestControllerCallback(unsigned index)
600 {
601     if (!callbackMap().contains(index))
602         return;
603     WKBundleFrameRef mainFrame = WKBundlePageGetMainFrame(InjectedBundle::shared().page()->page());
604     JSContextRef context = WKBundleFrameGetJavaScriptContext(mainFrame);
605     JSObjectRef callback = JSValueToObject(context, callbackMap().take(index), 0);
606     JSObjectCallAsFunction(context, callback, JSContextGetGlobalObject(context), 0, 0, 0);
607     JSValueUnprotect(context, callback);
608 }
609
610 void LayoutTestController::addChromeInputField(JSValueRef callback)
611 {
612     cacheLayoutTestControllerCallback(AddChromeInputFieldCallbackID, callback);
613     InjectedBundle::shared().postAddChromeInputField();
614 }
615
616 void LayoutTestController::removeChromeInputField(JSValueRef callback)
617 {
618     cacheLayoutTestControllerCallback(RemoveChromeInputFieldCallbackID, callback);
619     InjectedBundle::shared().postRemoveChromeInputField();
620 }
621
622 void LayoutTestController::focusWebView(JSValueRef callback)
623 {
624     cacheLayoutTestControllerCallback(FocusWebViewCallbackID, callback);
625     InjectedBundle::shared().postFocusWebView();
626 }
627
628 void LayoutTestController::setBackingScaleFactor(double backingScaleFactor, JSValueRef callback)
629 {
630     cacheLayoutTestControllerCallback(SetBackingScaleFactorCallbackID, callback);
631     InjectedBundle::shared().postSetBackingScaleFactor(backingScaleFactor);
632 }
633
634 void LayoutTestController::setWindowIsKey(bool isKey)
635 {
636     InjectedBundle::shared().postSetWindowIsKey(isKey);
637 }
638
639 void LayoutTestController::callAddChromeInputFieldCallback()
640 {
641     callLayoutTestControllerCallback(AddChromeInputFieldCallbackID);
642 }
643
644 void LayoutTestController::callRemoveChromeInputFieldCallback()
645 {
646     callLayoutTestControllerCallback(RemoveChromeInputFieldCallbackID);
647 }
648
649 void LayoutTestController::callFocusWebViewCallback()
650 {
651     callLayoutTestControllerCallback(FocusWebViewCallbackID);
652 }
653
654 void LayoutTestController::callSetBackingScaleFactorCallback()
655 {
656     callLayoutTestControllerCallback(SetBackingScaleFactorCallbackID);
657 }
658
659 void LayoutTestController::overridePreference(JSStringRef preference, bool value)
660 {
661     WKBundleOverrideBoolPreferenceForTestRunner(InjectedBundle::shared().bundle(), InjectedBundle::shared().pageGroup(), toWK(preference).get(), value);
662 }
663
664 } // namespace WTR